Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast to typeof(self)

Tags:

swift

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
    }
}
like image 736
Rob Zombie Avatar asked Aug 12 '14 18:08

Rob Zombie


1 Answers

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") {
    // ...
}
like image 77
Martin R Avatar answered Oct 18 '22 18:10

Martin R