NSKeyedUnarchiver.decodeObject will cause a crash / SIGABRT if the original class is unknown. The only solution I have seen to catching this issue
When NSKeyedUnarchiver encounters unknown classes, unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:) delegate method is called.
The delegate may, for example, load some code to introduce the class to the runtime and return the class, or substitute a different class object. If the delegate returns
nil, unarchiving aborts and the method raises anNSInvalidUnarchiveOperationException.
So, you can implement the delegate like this:
class MyUnArchiverDelegate: NSObject, NSKeyedUnarchiverDelegate {
// This class is placeholder for unknown classes.
// It will eventually be `nil` when decoded.
final class Unknown: NSObject, NSCoding {
init?(coder aDecoder: NSCoder) { super.init(); return nil }
func encodeWithCoder(aCoder: NSCoder) {}
}
func unarchiver(unarchiver: NSKeyedUnarchiver, cannotDecodeObjectOfClassName name: String, originalClasses classNames: [String]) -> AnyClass? {
return Unknown.self
}
}
Then:
let unarchiver = NSKeyedUnarchiver(forReadingWithData: dat)
let delegate = MyUnArchiverDelegate()
unarchiver.delegate = delegate
unarchiver.decodeObjectForKey("root")
// -> `nil` if the root object is unknown class.
ADDED:
I didn't noticed that NSCoder has extension with more swifty methods:
extension NSCoder {
@warn_unused_result
public func decodeObjectOfClass(cls: DecodedObjectType.Type, forKey key: String) -> DecodedObjectType?
@warn_unused_result
@nonobjc public func decodeObjectOfClasses(classes: NSSet?, forKey key: String) -> AnyObject?
@warn_unused_result
public func decodeTopLevelObject() throws -> AnyObject?
@warn_unused_result
public func decodeTopLevelObjectForKey(key: String) throws -> AnyObject?
@warn_unused_result
public func decodeTopLevelObjectOfClass(cls: DecodedObjectType.Type, forKey key: String) throws -> DecodedObjectType?
@warn_unused_result
public func decodeTopLevelObjectOfClasses(classes: NSSet?, forKey key: String) throws -> AnyObject?
}
You can:
do {
try unarchiver.decodeTopLevelObjectForKey("root")
// OR `unarchiver.decodeTopLevelObject()` depends on how you archived.
}
catch let (err) {
print(err)
}
// -> emits something like:
// Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (MyProject.MyClass) for key (root); the class may be defined in source code or a library that is not linked" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (MyProject.MyClass) for key (root); the class may be defined in source code or a library that is not linked}