Main menu

You are here

iPhone - Saving game or app state using NSUserDefaults

Submitted by bensmiley on Wed, 05/09/2012 - 17:40

In this article I'm going to explain how to store complex game or app data using the NSUserDefaults object in iOS. User defaults is ideal for storing structured information like: game state, high scores, game progress information. If you need to store unstructured information which you will be accessing by using SQL queries then you should use a built in database instead.

NSUserDefaults is the standard way of persisting data on the iPhone or iPad - much like Preferences API in Java. It stores the data in a .plist file in a safe directory. This is good for three reasons. First, your data will not be deleted when the app is updated. Second, your data will automatically be backed up during a synch with iTunes. Third, .plist is a tried and tested format which will not change. This means that an upgrade to iOS can't mess up your saved data. For more information look here.

Because user defaults uses a .plist file only certain data types are supported:

NSString

NSNumber

NSData

NSDate

NSArray

NSDictionary

As long as your data is stored in one of these formats it will be supported by NSUserDefaults. Lots of problems are caused by people trying to save incompatible objects. It is also possible to encode objects and store them in a NSData object as I'll show later.

Saving data with NSUserDefaults

Saving and retrieving data is very straightforward. The easiest way to think about this is to treat the NSUserDefaults object like an NSMutableDictionary which persists between App runs because this is exactly what it is. A standard NSMutableDictionary is stored in RAM which is cleared down when the app closes. With NSUserDefaults however, the system automatically writes the dictionary from RAM to a .plist file when the app closes. When the app opens again the system writes the .plist file to RAM again. With this in mind, using NSUserDefaults is very logical. Get a pointer to the dictionary and add some information. When you want to retrieve the information, get another pointer and read the information!

So it's as easy as that! Save and load your game state with the knowledge that it will be safely synched with the users computer.! It can get a bit more complex when you start encoding custom objects but I'll cover that in the next tutorial.

Update: Problems updating NSUserDefaults

NSUserDefaults is great but it's not particularly clever. I had a problem recently where I was trying to save a complex set of nested data into the defaults. I had a NSMutableArray containing some dictionaries which in turn contained an NSMutableArray. The first thing I discovered is that when you retrieve the NSMutableArray it's no longer mutable! To get around this you need to do a mutableCopy of the array and then re-insert it into the dictionary. The next problem I found was that even though this was working fine and when I printed my NSUserDefaults my new value was there and I was calling synchronise after making the change; it wasn't actually being saved. Eventually I found the solution. NSUserDefaults doen't notice if you make a change to a nested structure. Because it doesn't notice it doesn't save your new nested structure (even if it appears to be there when you print the NSUserDefaults!). To solve this I just added the root element of the nested structure to NSUserDefaults again.

Comments

If you notice that your preferences are not being saved when the user presses the home button and then quits the app you need to add this line to teh AppDelegate inside the application DidEnterBackground method: