Optional casting to a variable whose Type is a class type in Swift

I've looked for similar questions on Google and SO and couldn't find anything directly related. There seem to be two similar (maybe?) questions in C#, but I don't know the language, so I didn't really understand the questions properly (How to cast object to type described by Type class?, and Cast a variable to a type represented by another Type variable?).

I was experimenting with writing a generic scene-changing function in my GameViewController in SpriteKit. I made a SceneChangeType enum to use as a parameter. The error came in trying to optional cast a variable to what I expected to be a generic Type.

Just to clarify, I'm sure there are lots of reasons why this isn't a good idea. I can think of other ways to handle scene-changing, like just writing separate methods for each scene change. I'm just curious from a technical standpoint why I'm getting an error that I can't understand.

The relevant code is as follows:

In GameViewController.swift

func genericSceneChangeWithType(sceneChangeType: SceneChangeType) {
    let expectedType = sceneChangeType.oldSceneType
    guard let oldScene = self.currentScene as? expectedType else { return }
    ...
}

enum SceneChangeType {
    case TitleSceneToGameScene
    var oldSceneType: SKScene.Type {
        switch self {
        case .TitleSceneToGameScene:
            return TitleScene.self
        }
    }
    ...
}

For clarification, TitleScene is a custom subclass of SKScene, and self.currentScene has type SKScene?.

On the line,

guard let oldScene = self.currentScene as? expectedType else { return }

I get the error,

'expectedType' is not a type

Am I just massively misunderstanding things here? I thought you could use similar syntax to return a generic type from a function (e.g. Swift: how to return class type from function).
Is the issue because it's a property?
Is this not even possible, or is there some other way to check the type if the expected type is not known until runtime?

To emphasise again, I'm not asking about better ways to change scenes, and I'm not sure I have any examples where this is absolutely necessary. But understanding why this doesn't work might help me, or others, to understand the workings of the language better.

Thank you.


ANSWERS:


You have the right idea, you're just confusing runtime vs. compile-time information. When you cast with as, you need to provide a type at compile time. This could be an explicit type like String, or it could be a generic parameter (as generics are a compile-time feature). You cannot, however, pass a variable containing a type like expectedType.

What you can do instead is check:

if self.currentScene.dynamicType == expectedType

However, as this doesn't cast currentScene, it won't allow you to call any methods that are specific to expectedType. It also won't work if currentScene is a subclass of expectedType, and you probably want it to. The problem is that you're passing around types at runtime, when you need to know types at compile-time in order to call methods or cast.

What you need, therefore, are Swift language features that seem helpful but work at compile-time, and I can think of two:

Generics, something like:

func genericSceneChange<OldType: SKScene, NewType: SKScene>() {
    ...
}

Overloading, something like:

func changeTo(scene: OneType) {
    //do something
}

func changeTo(scene: OtherType) {
    //do something else
}

Thats because when you set expectedType to SceneChangeType, it's actually an instance of MetaType. A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types. Hence why you are unable to cast to it and receive that error message.

By passing in a Generic type, you should be able to get the functionality you are looking for.

func genericSceneChangeWithType<T>(sceneChangeType: T) {
  guard let oldScene = self.currentScene as? T else { return }  
}


 MORE:


 ? Pass parameter to function via reflection
 ? How do I design this well?
 ? How to tell if an instance is of a certain Type or any derived types
 ? How to cast an object of unknown type to its type in run time?
 ? Cast a variable to a Type and call Methods
 ? Dynamicly Converting Type Object to Type (Some Class)
 ? Unable to cast/match types when using generics in interfaces, why?
 ? Cast a CollectionBase subclass to a generic List without specifying the type explicitly
 ? C# Casting between objects of different types
 ? C# casting object to class with type argument