I have 2 objects, 1 which is a subclass of the other. When I try to unarchive the subobject I get fatal error: unexpectedly found nil while unwrapping an Optional value even when I manually test archiving and unarchiving the object.

Specifically I crash at:

let subobjectsArray = NSKeyedUnarchiver.unarchiveObject(with: subobjects) as? [Subobject] {

which calls the decode initializer of the Subobject

required init(coder aDecoder: NSCoder) {
            degree1 = aDecoder.decodeObject(forKey: degree1Key) as! CLLocationDegrees // get nil error here

This leads me to conclude I must be encoding and decoding the subobject incorrectly, but I don't know what I'm doing wrong in terms of encoding.

If anyone could let me know what I am doing wrong that would be greatly appreciated!

Code

Function to test archive and unarchive:

func unarchiveSubobjects() -> [Subobject] {
    //Manually encode a subobject
    let subobjects = [Subobject(name: "testing", degree1: CLLocationDegrees(45))]
    let data = NSKeyedArchiver.archivedData(withRootObject: subobjects)
    userDefaults.set(data, forKey: Key.UserDefaults.subobjects)
    
    // Decode object does not work and crashes with nil error
    if let subobjects = userDefaults.value(forKey: Key.UserDefaults.subobjects) as? Data,
        let subobjectsArray = NSKeyedUnarchiver.unarchiveObject(with: subobjects) as? [Subobject] { // crashes on the 2nd let statement
        return subobjectsArray
    }
    return [Subobject]()
}

Subobject.swift

class Subobject: Superobject {
    var degree1: CLLocationDegrees
    
    private let degree1Key = "degree1"
    
    init(name: String, degree1: CLLocationDegrees) {
        self.degree1 = degree1
        
        super.init(name: name)
    }
    
    // MARK: NSCoding
    
    required init(coder aDecoder: NSCoder) {
        degree1 = aDecoder.decodeObject(forKey: degree1Key) as! CLLocationDegrees // get nil error here
        
        super.init(coder: aDecoder)
    }
    
    public override func encode(with aCoder: NSCoder) {        
        aCoder.encode(self.degree1, forKey: degree1Key)
        
        super.encode(with: aCoder)
    }
}

Superobject.swift

class Superobject: NSObject, NSCoding {

    var name: String
    
    private let nameKey = "name"
    
    init(name: String){
        self.name = name
    }
    
    // MARK: NSCoding
    
    required init(coder aDecoder: NSCoder) {
        name = aDecoder.decodeObject(forKey: nameKey) as! String
    }
    
    public func encode(with aCoder: NSCoder) {
        aCoder.encode(self.name, forKey: nameKey)
    }
}
1

There are 1 answers

3
vadian On

Rather than decoding an object decode the actual type Double with

degree1 = aDecoder.decodeDouble(forKey: degree1Key)!

As CLLocationDegrees is a type alias of Double it should work or you need to bridge cast the value.