Developers that are new to Swift or user defaults are often confused why some of the data stored in the user's defaults database isn't persisted. To understand why and when this might happen, you first need to know more about the defaults system and how it stores data.

Storing Data in User Defaults

The defaults system is nothing more than a key-value store. The user's defaults database is stored on disk as a property list or plist. A property list or plist is an XML file. At runtime, the UserDefaults class keeps the contents of the property list in memory to improve performance.

Changes are made synchronously within the process of your application. This is fast and efficient because the contents of the property list are in memory. The changes in memory also need to be written to disk to make sure they are persisted. That process takes place asynchronously and that is where the behavior of the defaults system becomes interesting and possibly confusing.

Synchronous Versus Asynchronous Execution

You can read more about synchronous and asynchronous execution in Mastering Grand Central Dispatch. The idea is simple, though.

Writing the contents of the user's defaults database from memory to disk takes time and resources. This operation should not impact the performance of your application and that is (1) why the UserDefaults class doesn't perform this operation every time the user's defaults database changes and (2) why this operation takes place asynchronously. This optimization has a few consequences, though.

Writing to Disk

It is possible that a change in the user's defaults database isn't written to disk. With the above in mind, you may have an idea why and when that can happen. The typical scenario takes place during development. You launch the application, perform a number of actions, and one or more key-value pairs are modified in the user's defaults database.

You then stop the application by clicking the stop button in the top left. This immediately terminates the application. Because the user's defaults database is written to disk asynchronously, it is possible that the changes you made weren't written to disk at the time the application was terminated by clicking the stop button in the top left.

Terminating the application unexpectedly may result in data loss. | User Defaults

This scenario should not take place in production, though. The user cannot click the stop button of your application. The UserDefaults class is clever enough to write any changes to disk before the application is terminated by the system.

The problem can also occur in another scenario. A crash. When your application crashes, it is abruptly terminated and the UserDefaults class doesn't have an opportunity to write the changes to disk. This means that any uncommitted changes are lost. This also applies to other persistence solutions, such as Core Data.

Synchronizing Explicitly

It used to be possible to explicitly wait for pending asynchronous updates to the defaults database by invoking the synchronize() method of the UserDefaults class. The documentation clearly states that invoking the synchronize() method is unnecessary and should not be used.

Data loss isn't something you need to worry about if you're working with the defaults system. The UserDefaults class ensures any changes are written to disk at the appropriate time without impacting the performance of your application. It goes without saying that unexpected terminations of your application break this behavior, but that applies to any persistence solution.