✍️ 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

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

Quote of the week

"People ask me what I do in the winter when there's no baseball. I'll tell you what I do. I stare out the window and wait for spring."

~ Rogers Hornsby