Saving Game Data with NSCoding in Swift

I reached a point in developing LetterMiner where I wanted to save the game when the player quit so they could pick up where they left off. I didn’t find a lot of information online on saving data in SpriteKit games so I’ve decided to share what I’ve learned. I hope it helps other people.

Classes

To save data with NSCoding in Swift, that data must be in Swift classes. Swift structs don’t conform to the NSCoding protocol. If you want to use Swift structs in your game, create a SaveGame class with properties for each piece of data you want to save and do all your data reading and writing in that class.

The following example shows a simple Player class that conforms to the NSCoding protocol:

You noticed my Player class also inherits from NSObject. NSObject is the base class for the Cocoa and Cocoa Touch frameworks. If you have a class that is going to work with Apple’s frameworks, it’s usually good to inherit from NSObject.

Writing Data

To write data the class must implement the method encodeWithCoder. Call the NSCoder method encodeObject to save a piece of data. The following code shows an example of writing data for the Player class:

Make sure you remember the key you use when writing the data. You must use the same key to read the data.

I’ve kept the example simple with one string property and one integer property. You can also write structs, classes, and arrays with the encodeObject method. NSCoder also has methods to write primitive data types like integers and floating-point numbers. What I like about encodeObject is it works on any data type.

Reading Data

To read data the class must implement the initWithCoder method. Call the NSCoder method decodeObjectForKey to read the data. The following code shows an example of reading data for the Player class:

Your saveGame method doesn’t have to take a notification argument. I use the argument because I want my game to save when the player quits the game. The following code lets you register for the notification when the player quits a Mac game:

Where to Save the Game

Mac games should place saved game files in the Application Support folder. You will have to create a folder with the name of your game in the Application Support folder and place the saved game files in the folder you create. You can also place iOS saved games files in the Application Support folder, but you can place them in the Documents folder if you want.

The NSFileManager class has methods to find and create directories. Call the URLForDirectory method to find the Application Support folder. The following code finds the user’s Application Support directory:

The URLForDirectory function throws an error in Swift 2. That’s why the code has to be wrapped in a do block.

At this point you have access to the Application Support directory. The first time you save you must create a directory for your game inside the Application Support folder. Call the createDirectoryAtURL method to create a directory.

Now you’re in your game’s Application Support folder. The final step is to create a file name for the saved game. The NSURL method URLByAppendingPathComponent lets you add the file name to the path you’ve built.

Now you have a URL you can use to save games and load saved games. In my saveGame and loadGame functions I referred to a saveLocation variable that was the result of calling a function called saveFileLocation. The savePath variable is what saveFileLocation returns.

Most games have a Game class as the main class in the game. The loadGame() and saveGame() functions would go there. If the only game data you’re saving is player data, you could load and save the game in the Player class. But most games need to save more data than player data. You should load and save the game in a class that has access to everything you need to save.

To save the books array you create an NSKeyedArchiver object. You call the archiver’s encode() function to save the books array. Something like the following:

archiver.encode(books, forKey: "books")

To load the books array create an NSKeyedUnarchiver object. Call the unarchiver’s decode() function to load the books array. Something like the following code:

books = unarchiver.decodeObject(forKey: "books") as! [Book]

Take a look at the Saving Data to a File and Loading Data from a File sections in the article. The code is similar. Instead of encoding and decoding a single value you’re encoding and decoding an array. Give your books array a key. The fact that you have an array doesn’t have any effect on the archiving and unarchiving.

Thanks for the answer. I would like to use the NSCoding to save relationships between different classes, such as one to many relationships.

I’ve been able to get a working solution on github, however I am unsure if I got it right.

As I understand it, the parent encodes its array of children, but
the child does not attempt to encode the parent and it required to be a weak reference so that it does not stay in retain cycle. Further, when reading it should add the children.