Search code examples
swiftcocoa-touchfoundation

NSInvalidUnarchiveOperationException error when decoding object from archive in Swift


Please note that I'm really new to Swift and iOS programming so some of you may find this a bit silly.

Anyway, so I am encoding an Int object and associating it with a String key like so:

func encodeWithCoder(aCoder: NSCoder) {
    // Note that `rating` is an Int
    aCoder.encodeObject(rating, forKey: PropertyKey.ratingKey)

}

Now when I try to decode it like so:

required convenience init?(coder aDecoder: NSCoder) {
    let rating = aDecoder.decodeIntegerForKey(PropertyKey.ratingKey)

    // Initialising a model class
    self.init(rating: rating)
}

The constant rating is expected to an Int because decodeIntegerForKey is expected to return an Int by default

Build goes well, but it crashes when I run it and logs an error as replicated below.

Terminating app due to uncaught exception 
'NSInvalidUnarchiveOperationException', 
reason: '*** -[NSKeyedUnarchiver decodeInt64ForKey:]: 
value for key (rating) is not an integer number'

But it seems to work well when I change decodeIntegerForKey to decodeObjectForKey and downcast the return value to an Int.

Like so:

required convenience init?(coder aDecoder: NSCoder) {
    // Replaced `decodeInteger` with `decodeObject` and downcasting the return value to Int 
    let rating = aDecoder.decodeObjectForKey(PropertyKey.ratingKey) as! Int
    self.init(rating: rating)
}

It is getting hard for me to understand why the exception, because I encoded it as an Int and decodeInteger returns an Int by default.

Also, I feel like the NSInvalidUnarchiveOperationException is telling me that I've used the wrong operation to decode the encoded object out.

This isn't making any sense to me, help


Solution

  • This question has been resolved. Thanks to @PhillipMills for clarifying.

    The implementation was done wrong while encoding the Int object. I was encoding it in AnyObject instead of Int and was trying to decode it as an Int. And that's why I had to downcast it and decoding as an Int wasn't working.

    Encoding should have been done like so:

    func encodeWithCoder(aCoder: NSCoder) {
        // Note that `rating` is an Int
        aCoder.encodeInteger(rating, forKey: PropertyKey.ratingKey)
    
    }