Is it possible to make a category (extension) that would eventually return an object casted to instancetype? I have a category to loads SKS files but since this category is for SKNode then all other subclasses like SKScene or SKEmitterNode, etc.. will adopt it too.
So I would simply like to avoid always casting from SKNode to instancetype. Is it possible to change return type to instancetype and make sure that compiler is happy with return value?
I think I can use -> Self as return type but then I have no idea how to cast scene to instancetype so this thing would compile..
For example:
SKEmitterNode.unarchiveFromFile("Blah") would return an instance of SKEmitterNode
extension SKNode {
    class func unarchiveFromFile(file: String) -> SKNode {
        let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks")
        var sceneData = NSData.dataWithContentsOfFile(path, options: .DataReadingMappedIfSafe, error: nil)
        let unarchiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
        unarchiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
        let scene = unarchiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as SKNode
        unarchiver.finishDecoding()
        return scene
    }
}
This might work for you. I could not test it because I have not much experience with SpriteKit. But it does compile and the compiler inferred type for
let e = SKEmitterNode.unarchiveFromFile("Blah")
is SKEmitterNode. The idea is to define a generic helper function
func unarchiveFromFileHelper<T where T : SKNode>(file: String) -> T 
so that
class func unarchiveFromFile(file: String) -> Self {
    // define helper function ...
    return unarchiveFromFileHelper(file)
}
calls the helper function with T == Self.
extension SKNode {
    class func unarchiveFromFile(file: String) -> Self {
        func unarchiveFromFileHelper<T where T : SKNode>(file: String) -> T {
            let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks")
            var sceneData = NSData.dataWithContentsOfFile(path, options: .DataReadingMappedIfSafe, error: nil)
            let unarchiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
            unarchiver.setClass(T.classForKeyedUnarchiver(), forClassName: "SKScene")
            let scene = unarchiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as T
            unarchiver.finishDecoding()
            return scene
        }
        return unarchiveFromFileHelper(file)
    }
}
Update: If you are targeting iOS 8/OS X 10.10 or later then there is no need for a custom unarchive method anymore. As noted in Cannot use unarchiveFromFile to set GameScene in SpriteKit, you can use
convenience init?(fileNamed filename: String)
from the SKNode superclass, e.g.
if let e = SKEmitterNode(fileNamed: "Blah") {
    // ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With