✍️ Note
Some codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts
Reference
- https://developer.apple.com/documentation/foundation/nscoding
- https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Archiving/Archiving.html#//apple_ref/doc/uid/10000047i
Archives and serializations are two ways in which you can create architecture-independent byte streams of hierarchical data. Byte streams can then be written to a file or transmitted to another process, perhaps over a network. When the byte stream is decoded, the hierarchy is regenerated. Archives provide a detailed record of a collection of interrelated objects and values. Serializations record only the simple hierarchy of property-list values
Apple
Example 1, Conforms NSCoding
class Person: NSObject, NSCoding {
var name: String
var age: Int
var friend: Person?
init(name: String, age: Int, friend: Person? = nil) {
self.name = name
self.age = age
self.friend = friend
}
required convenience init?(coder: NSCoder) {
guard let name = coder.decodeObject(forKey: "name") as? String
else {
return nil
}
let age = coder.decodeInteger(forKey: "age")
let friend = coder.decodeObject(forKey: "friend") as? Person
self.init(name: name, age: age, friend: friend)
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(age, forKey: "age")
coder.encode(friend, forKey: "friend")
}
}
func savePerson(_ person: Person, to path: String) {
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: person, requiringSecureCoding: false)
try data.write(to: URL(fileURLWithPath: path))
print("Saved Person")
} catch {
print("Failed: \(error)")
}
}
func loadPerson(from path: String) -> Person? {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path))
//Instead unarchiveTopLevelObjectWithData You can use unarchivedObject(ofClass:from:) when you conforms NSSecureCoding protocol
if let loadedPerson = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Person {
return loadedPerson
}
} catch {
print("Failed: \(error)")
}
return nil
}
let friend = Person(name: "Bob", age: 35)
let person = Person(name: "Alice", age: 30, friend: friend)
let filePath = NSTemporaryDirectory() + "person_with_friend.archive"
savePerson(person, to: filePath)
if let loadedPerson = loadPerson(from: filePath) {
if let loadedFriend = loadedPerson.friend {
print("Person: \(loadedPerson.name), \(loadedPerson.age)")
print("Friend: \(loadedFriend.name), \(loadedFriend.age)")
} else {
print("No friends")
}
}

Example 2. Conforms NSSecureCoding

In example 1, You may see this warning. unarchiveTopLevelObjectWithData was deprecated. To resolve it, let’s conforms NSSecureCoding.


class Person: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
var name: String
var age: Int
var friend: Person?
init(name: String, age: Int, friend: Person? = nil) {
self.name = name
self.age = age
self.friend = friend
}
required convenience init?(coder: NSCoder) {
guard let name = coder.decodeObject(forKey: "name") as? String
else {
return nil
}
let age = coder.decodeInteger(forKey: "age")
let friend = coder.decodeObject(forKey: "friend") as? Person
self.init(name: name, age: age, friend: friend)
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(age, forKey: "age")
coder.encode(friend, forKey: "friend")
}
}
func savePerson(_ person: Person, to path: String) {
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: person, requiringSecureCoding: true)
try data.write(to: URL(fileURLWithPath: path))
print("Saved Person")
} catch {
print("Failed: \(error)")
}
}
func loadPerson(from path: String) -> Person? {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path))
if let loadedPerson = try NSKeyedUnarchiver.unarchivedObject(ofClass: Person.self, from: data) {
return loadedPerson
}
} catch {
print("Failed: \(error)")
}
return nil
}
let friend = Person(name: "Bob", age: 35)
let person = Person(name: "Alice", age: 30, friend: friend)
let filePath = NSTemporaryDirectory() + "person_with_friend.archive"
savePerson(person, to: filePath)
if let loadedPerson = loadPerson(from: filePath) {
if let loadedFriend = loadedPerson.friend {
print("Person: \(loadedPerson.name), \(loadedPerson.age)")
print("Friend: \(loadedFriend.name), \(loadedFriend.age)")
} else {
print("No friends")
}
}

Leave a comment