** Jump to Swift 3 code **
As a beginner, I had no idea what a coder was or how I might use it to initialize a class. But in fact there's nothing whatsoever to be scared of, as is demonstrated in this excellent post by the NSHipster site from 2013 (updated with Swift examples).
Taking that code and adding it to a SwiftFiles Playground (while updating for Swift 1.2 and making a few adaptations)
class Book: NSObject, NSCoding { var title: String var author: String var pageCount: Int var categories: [String] var available: Bool init(title:String, author: String, pageCount:Int, categories:[String],available:Bool) { self.title = title self.author = author self.pageCount = pageCount self.categories = categories self.available = available } // MARK: NSCoding required convenience init(coder decoder: NSCoder) { let title = decoder.decodeObjectForKey("title") as! String let author = decoder.decodeObjectForKey("author")as! String let categories = decoder.decodeObjectForKey("categories") as! [String] let available = decoder.decodeBoolForKey("available") let pageCount = decoder.decodeIntegerForKey("pageCount") self.init(title:title, author:author,pageCount:pageCount,categories: categories,available:available) } func encodeWithCoder(coder: NSCoder) { coder.encodeObject(self.title, forKey: "title") coder.encodeObject(self.author, forKey: "author") coder.encodeInt(Int32(self.pageCount), forKey: "pageCount") coder.encodeObject(self.categories, forKey: "categories") coder.encodeBool(self.available, forKey: "available") } }we first of all create a class that can be initialized using an NSCoder instance and encode itself into an instance of NSCoder. And once we have this functionality, which is required by the NSCoding protocol, it is possible to create an instance that can be archived
let book = Book(title: "MyBook", author: "Me", pageCount: 10, categories: ["Fabulousness"], available: true) let filePath = FileSave.buildPath("bookdata", inDirectory: NSSearchPathDirectory.CachesDirectory, subdirectory: "archive") NSKeyedArchiver.archiveRootObject(book, toFile: filePath)and de-archived.
if let bookData = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as? Book { bookData.available // true bookData.author // Me }And equally easy to create an array of instances that can be archived and de-archived:
let books = [Book(title: "MyBook", author: "Me", pageCount: 10, categories: ["Fabulousness"], available: true),Book(title: "YourBook", author: "You", pageCount: 10, categories: ["Fabulousness","Creativity"], available: true)] let filePath = FileSave.buildPath("bookdata", inDirectory: NSSearchPathDirectory.CachesDirectory, subdirectory: "archive") NSKeyedArchiver.archiveRootObject(books, toFile: filePath) if let bookData = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as? [Book] { bookData[1].available // true bookData[1].title // YourBook }Notice that we never once have to call init(coder:) or encodeWithCoder(), since this happens automatically when we use NSKeyedArchiver and NSKeyedUnarchiver.
Conclusion
We see very quickly the utility of NSCoding, and how simply we can save to disk data that we can avoid (or delay) reconstructing from the file-system or from server data. Explained in this way (thanks to NSHipster) it becomes almost as straightforward as using NSUserDefaults but far more powerful for restoring the state of an app.Swift 3, Xcode 8 GM
The book class rendered in Swift 3 looks like this:class Book: NSObject, NSCoding { var title: String var author: String var pageCount: Int var categories: [String] var available: Bool init(title:String, author: String, pageCount:Int, categories:[String],available:Bool) { self.title = title self.author = author self.pageCount = pageCount self.categories = categories self.available = available } // MARK: NSCoding public convenience required init?(coder aDecoder: NSCoder) { let title = aDecoder.decodeObject(forKey: "title") as! String let author = aDecoder.decodeObject(forKey: "author") as! String let categories = aDecoder.decodeObject(forKey: "categories") as! [String] let available = aDecoder.decodeBool(forKey: "available") let pageCount = aDecoder.decodeInteger(forKey: "pageCount") self.init(title:title, author:author,pageCount:pageCount,categories: categories,available:available) } func encode(with aCoder: NSCoder) { aCoder.encode(title, forKey: "title") aCoder.encode(author, forKey: "author") aCoder.encodeCInt(Int32(pageCount), forKey: "pageCount") aCoder.encode(categories, forKey: "categories") aCoder.encode(available, forKey: "available") } }Full code for all examples can be found on GitHub. This has the SwiftFiles library (updated for Swift 3) included and so saves and loads the data as well.
Hello! Thanks for the post. I'm getting the message "use of unresolved identifier FileSave"... could you please help me with this?
ReplyDeleteThis has now been fixed, you need to update your version of SwiftFiles - https://github.com/sketchytech/SwiftFiles/tree/Swift_2
Delete