Swift has support for serializing to JSON and property lists, but sometimes you need an archive, and these simple serialization formats won’t do. Archives are better for complex object graphs. They contain type information. They can contain Data
without round-tripping through a Base-64 string. And they can resolve multiple references to an object. NSKeyedArchiver
and NSKeyedUnarchiver
support Codable
types. But how do you use them? If you’re used to using the convenience class methods, you might try something like this:
class CodableClass: Codable { var answer: Int var name: String public init(answer: Int, name: String) { self.answer = answer self.name = name } } let myClass = CodableClass(answer: 42, name: "Arthur Dent") let data = NSKeyedArchiver.archivedData(withRootObject: myClass) // "NSInvalidArgumentException", "-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x103a6b500"
As commented, this produces a runtime exception because the convenience methods are expecting conformance to NSCoding
, and CodableClass
doesn’t implement NSCoding
.
When Codable
was released, Foundation added an instance method to NSKeyedArchiver
: encodeEncodable(_, forKey:)
and one to NSKeyedUnarchiver
: decodeDecodable(_, forKey:)
. To use these, you need to create an instance of NSKeyedArchiver
and NSKeyedUnarchiver
, respectively.
let myClass = CodableClass(answer: 42, name: "Arthur Dent") let archiver = NSKeyedArchiver(requiringSecureCoding: true) do { try archiver.encodeEncodable(myClass, forKey: NSKeyedArchiveRootObjectKey) } catch { fatalError(error.localizedDescription) } archiver.finishEncoding() let unarchiver = NSKeyedUnarchiver(forReadingWith: archiver.encodedData) unarchiver.decodingFailurePolicy = .setErrorAndReturn let decoded = unarchiver.decodeDecodable(CodableClass.self, forKey: NSKeyedArchiveRootObjectKey)! unarchiver.finishDecoding() if let error = unarchiver.error { fatalError(error.localizedDescription) } else { assert(decoded!.name == "Arthur Dent") assert(decoded!.answer == 42) }
As a bonus, since these are Swift methods, they are well-typed. So decodeDecodable(_, forKey:)
returns an object of the type you are decoding. In the example above, decoded
is of type CodableClass
.
The example uses a class, but these methods also work for Swift structs that implement Codable
.