NSUserDefaults is not a database

Thoughts By 3 years ago

The NSUserDefaults is designed for storing small bits of information, such as if the user turned off sounds in your app, or the current level a user is up to in a game, and other settings. In general, primitives and very small arrays and dictionaries is all you should store in the defaults.

It seems far too many people see the ease of storing and retrieving data in the NSUserDefaults as an excuse to, and abuse is at a simple data store for huge chunks of data. I’ve seen people store full blown relational databases in the NSUserDefaults… I’m going to explain why this is a bad idea.

Loaded even if you don’t use it

Because the NSUserDefaults is a shared object, there are other parts of your app that will use it too. This means that when you launch your app, the defaults plist file used to store the data to disk is loaded and parsed, regardless of whether you want to use something from it immediately.

If you’re storing info that isn’t required until a user visits a particular screen, usually you want to lazy load that info, so it’s only retrieved when required.

You can’t thread

As mentioned above, the defaults is loaded for you on app launch. You don’t choose when and how it’s loaded. You can’t do any smart threading if it becomes huge (say you put 10000 objects in there).

Everything is loaded

If a user needed to jump to a particular product page in your app, the most efficient way would be to only load the information related to that product. Too bad this is not how the defaults work, and everything has to be loaded even if you just want a single entry.

The solution

There are two commonly used solutions to this problem. What you use depends on what type of data you store – you need to figure this out.

A real database

Either a CoreData or sqlite database. These are great for relational objects that belong to a hierarchy. If you only want to retrieve a single object, you can do so easily without reading over the entire database. You can also thread queries to the database to keep the UI responsive.

A separate plist file

Using the NSArray and NSDictionary writeToFile: and initWithContentsOfFile: methods allow you to easily store data to disk as a plist. You can thread reading of the saved file and load it when you need. Note that the entire plist still needs to be loaded even if you just want to get a single item.