Using Switch-Case with implicit type conversion to subtypes

Suppose that InputState is a class having all of its subclasses:

class InputState{
	class Deactivated: InputState {}
	class Empty: InputState {}
	class EmptyIgnoringPreviousState: Empty {}
	class Committing: InputState {}
	class NotEmpty: InputState {}
	class Inputting: NotEmpty {}
	class Marking: NotEmpty {}
	class ChoosingCandidate: NotEmpty {}
	class AssociatedPhrases: InputState {}
	class SymbolTable: ChoosingCandidate {}
}

Now I am trying to refactor the following block:

    if let newState = newState as? InputState.Deactivated {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.Empty {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.EmptyIgnoringPreviousState {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.Committing {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.Inputting {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.Marking {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.ChoosingCandidate {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.AssociatedPhrases {
      handle(state: newState, previous: prevState, client: currentClient)
    } else if let newState = newState as? InputState.SymbolTable {
      handle(state: newState, previous: prevState, client: currentClient)
    }

I tried to use switch like this:

    switch newState {
      case .Deactivated, .Empty, .EmptyIgnoringPreviousState, .Committing, .Inputting, .Marking, .ChoosingCandidate, .AssociatedPhrases, .SymbolTable: do {
        
      }
    }

But Xcode complaints:

I don't know what should I do in this case.

What's the definition and implementation of handle? It's not clear to me why you need to downcast before calling it.

The codes I am going to refactor was originally written by Zonble for a project interoping Objective-c and Swift:

Looks like this seems not making enough of sense to you (in a Swift-only project like mine the following one). Are you expecting that I should use protocols in this context? Please tell me if it is necessary. I don't hesitate to rewrite the entire InputState system.

vChewing-macOS/InputState.swift at main · ShikiSuen/vChewing-macOS (github.com)
vChewing-macOS/ctlInputMethod.swift at main · ShikiSuen/vChewing-macOS (github.com)

There's a lot of surgery that can be done, but if you just want to convert to using a switch statement, then you write:

switch newState {
case let newState as InputState.Deactivated:
  handle(state: /* etc. */)
case let newState as InputState.Empty:
  handle(state: /* etc. */)
}

Unless you do a much larger refactor, you cannot write the call to handle just one time, because each of these functions is a different function that happens to share the same name.

In order to write something one time but get a different result depending on the dynamic type of an argument, you're looking for dynamic dispatch, which in this case would require each class to have its own override.

1 Like

Thanks. Looks like there's no way to further-simplify this block without mass-surgery.