czekhoff czekhoff - 16 days ago 8
Swift Question

Swift can't unarchive Data

I have a UIView subclass which contains the following:

class someClass: UIView {
var someString = ""
var imageView: UIImageView
var position: CGPoint
var dimensions: CGFloat

init(position: CGPoint, dimensions: CGFloat, someString: String) {
self.position = position
self.dimensions = dimensions
self.someString = someString
self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: dimensions, height: dimensions))
super.init(frame: CGRect(x: position.x, y: position.y, width: dimensions, height: dimensions))
addSubview(imageView)
}
}


Also I added required init?(coder aDecoder: NSCoder) and encodeWithCoder(coder: NSCoder):

required init?(coder aDecoder: NSCoder) {
self.someString = aDecoder.decodeObject(forKey: "someString") as! String
self.position = aDecoder.decodeObject(forKey: "position") as! CGPoint
self.dimensions = aDecoder.decodeObject(forKey: "dimensions") as! CGFloat
self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: dimensions, height: dimensions))
super.init(frame: CGRect(x: position.x, y: position.y, width: dimensions, height: dimensions))
self.addSubview(imageView)
}

func encodeWithCoder(coder: NSCoder) {
coder.encode(self.position, forKey: "position")
coder.encode(self.dimensions, forKey: "dimensions")
coder.encode(self.someString, forKey: "someString")
}


So, I created an object of the class:

let someObject = someClass(position: CGPoint(x: 2.0, y: 3.0), dimensions: 10.0, someString: "Hello")


I'm trying to archive the object:

let archivedObject = NSKeyedArchiver.archivedData(withRootObject: someObject)


And then to unarchive:

let unarchivedObject = NSKeyedUnarchiver.unarchiveObject(with: archivedObject) as! someClass


Here I'm getting the error:

my error

If print unarchivedObject it will be "fatal error: unexpectedly found nil while unwrapping an Optional value".

Can somebody help me with this? What is wrong?

Answer

It looks like the problem is that you're not properly overriding encodeWithEncoder. If you put a breakpoint inside that function, you'll see that it's never getting called, which is why the object is nil when you try to decode it - it never got encoded!

You'll also run into an error trying to use decodeObject on a CGPoint. Make sure to change that line too and then you should be in business.

By the way, it is common practice to capitalize class names so that they are distinct from instances of that class, so I changed someClass to SomeClass.

class SomeClass: UIView {
var someString = ""
var imageView: UIImageView
var position: CGPoint
var dimensions: CGFloat

init(position: CGPoint, dimensions: CGFloat, someString: String) {
    self.position = position
    self.dimensions = dimensions
    self.someString = someString
    self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: dimensions, height: dimensions))
    super.init(frame: CGRect(x: position.x, y: position.y, width: dimensions, height: dimensions))
    addSubview(imageView)
}

required init?(coder aDecoder: NSCoder) {
    self.someString = aDecoder.decodeObject(forKey: "someString") as! String
    self.position = aDecoder.decodeCGPoint(forKey: "position")
    self.dimensions = aDecoder.decodeObject(forKey: "dimensions") as! CGFloat
    self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: dimensions, height: dimensions))
    super.init(frame: CGRect(x: position.x, y: position.y, width: dimensions, height: dimensions))
    self.addSubview(imageView)
}

override func encode(with aCoder: NSCoder) {
    aCoder.encode(self.position, forKey: "position")
    aCoder.encode(self.dimensions, forKey: "dimensions")
    aCoder.encode(self.someString, forKey: "someString")
}

}