Reputation: 41
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.
Upvotes: 4
Views: 1237
Reputation: 1016
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 }
}
Upvotes: 2
Reputation: 14824
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
}
Upvotes: 2