archive array of optional structs with NSCoding in Swift?

Chuck Smith picture Chuck Smith · Aug 14, 2014 · Viewed 11.4k times · Source

I've done a lot of NSCoding archiving in Obj-C, but I'm not sure how it handles structs in Swift, nor arrays with optional values. Here is my code:

public struct SquareCoords {
    var x: Int, y: Int
}

and here is the class which I need to store:

public class Player: NSCoding {
    var playerNum: Int
    var name = ""
    private var moveHistory: [SquareCoords?] = []

    init (playerNum: Int, name: String) {
        self.playerNum = playerNum
        self.name = name
    }

    public required init(coder aDecoder: NSCoder!) {
        playerNum = aDecoder.decodeIntegerForKey("playerNumKey")
        name = aDecoder.decodeObjectForKey("nameKey") as String
        moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords?]
    }

    public func encodeWithCoder(aCoder: NSCoder!) {
        aCoder.encodeInteger(playerNum, forKey: "playerNumKey")
        aCoder.encodeObject(name, forKey: "nameKey")
        aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")
    }
...

On the last line of the coder init, I get the following error message in XCode:

'AnyObject' is not convertible to [SquareCoords?]'

and on the last line of encodeWithEncoder:

Extra argument 'forKey' in call

Can anyone get me moving in the right direction?

Answer

Imanou Petit picture Imanou Petit · Aug 19, 2014

In The Swift Programming Language, Apple states:

Swift provides two special type aliases for working with non-specific types:
- AnyObject can represent an instance of any class type.
- Any can represent an instance of any type at all, including function types.

Knowing that, type SquareCoords (Swift Structure) and type [SquareCoords] (Swift array of Swift Structure) can't conform to protocol AnyObject.

On the other hand, decodeObjectForKey: requires a parameter that conforms to protocol AnyObject, and encodeObject:forKey: returns AnyObject. Thus, the two following lines can't compile:

moveHistory = aDecoder.decodeObjectForKey("moveHistoryKey") as [SquareCoords?]
aCoder.encodeObject(moveHistory, forKey: "moveHistoryKey")

Therefore, unless you find a way to make SquareCoords conform to protocol AnyObject (I don't know if it's possible), you will have to transform SquareCoords from Swift Structure to Class.

PS: At this point, you may ask: "OK, but how is it possible that type String - that is in fact a Swift Struct - can conform to protocol AnyObject?" Well, that's because String is bridged seamlessly to Foundation’s NSString class (Array, Dictionary are bridged to NSArray and NSDictionary the same way). Read this blog post if you want to have a better look at it.