Local Data Storage on iOS – When and How to Use it? Keychain, Core Data, and Other Options
When we think about mobile apps, we assume that they have constant access to the Internet and data exchange with servers is always possible. Sometimes, however, we just need to save some data locally.
For instance, when we want the app to work offline, when devices must store log data in their memory, or when we shorten the loading time with a data buffer.
There are several methods to save data in apps. From simple UserDefaults to safer Keychain or much more powerful Core Data. Which ones should you use? It depends on the type and size of this data. That’s why it’s best to think about it at the beginning of the project. No matter where you save them though, you must always ascertain that they’re stored safely.
In this article, I will present to you some of the available options, explain how they differ from each other, and how to use them.
Popular methods of implementing local memory on iOS
Local memory stores the data of the internet apps on the device using specified platforms, tools, and methods characteristic of different systems. In the case of iOS, the choice depends on the type of data you intend to store and its quantity.
These are the most popular methods of implementing local memory in iOS:
- Property list in Swift
- File System
- UserDefaults
- Keychain
- Core Data
What do you need to know about them?
Property list in Swift
Property list file, better known as plist, is a special file format. It’s an XML file in which you can store data in key-value pairs. In macOS and iOS apps, plist is often used as Info.plist. It stores values of settings and configuration, and also info about the app. It’s possible to add more objects on demand. We can compare it with the dictionary in Swift. Files with property lists can be a perfect choice when you want to save small amounts of data.
In plist files, you can store such objects as:
- Strings
- Numbers (Int, Floats)
- Bools
- Dates (ISO 8601 formatted string)
- Data (NSData)
- Arrays
- Dictionaries.
After opening the plist file in a text editor, the data doesn’t look clear. That’s because it is organized in a format readable by computers:
We can open this file also in the form of a graphic editor that presents the data in a much more accessible manner.
You can read the info stored in Info.plist file also with some help from Bundle:
Plist files are easily interpreted by both computers and humans. They allow us to determine the environmental values of the app or user settings.
File system
The file system supports constant storage of data files, apps, and files related to the operating system. That’s why the file system is one of the elementary resources used by all processors. In the iOS system, it works in the same way as on the computers – it uses paths and URL addresses to identify resources on the disc.
We are going to focus on a few preferred folders:
- Documents – it’s a perfect place to save user-created content.
- tmp – in this folder, we save files that apps need only temporarily. The operating system can delete them when the app isn’t running.
- Documents/Inbox – we can use this catalog to access files that the app requested to open via external entities, such as email attachments.
- Library – it’s a high-level catalog for all files that aren’t user data. We can store in it, for example, a binary file that delivers data to the app.
- Library\Caches – subfolder of the folder for all buffer files. We can use it to save files that should be needed soon, such as product images.
It’s easy to work with files in iOS apps. Each and every one of them has the Sandbox catalog called the Documents where they can store files. At this point, FileManager comes in handy. It’s a comfortable interface for the content of the file system and a basic method that enables interactions with them.
The file system is useful in certain cases. It allows us to store big objects on the disc (e.g., product images) when the device has enough local memory, translations, error codes, or other required data. The interface of the FileManager is a safe solution for threads. Unfortunately, creating the URL paths itself can be prone to errors.
UserDefaults
It’s the interface for the default user database. It is used to store small fragments of data. Key-value pairs are constantly stored during the app launch. The key is always a string, and the value is one of the following types: data, string, number, date, array, or dictionary.
UserDefaults is an NSObject subclass that gives us synchronous readings and records (on the cache level) and also an asynchronous persistence level. This way, even if the app is closed or the device relaunches, the data remains available.
UserDefaults saves its data in a local plist file on a disc. They are also stored for backup and restore. Currently, the tvOS platform is the only one with a limit of 1MB. Working with UserDefaults in the iOS app is very easy – both when you save data and read it.
UserDefaults is a great choice when you work with user settings or small amounts of JSON. It’s ideal when we need to store basic data types, because it’s easy to use and doesn’t require any other libraries or frameworks. It is also safe for threads (it enables reading and saving data from every thread). However, this solution is not perfect. UserDefaults isn’t encrypted. What’s more, it’s prone to incorrect overwriting, because it relies on the keys added by software developers.
Keychain
Keychain by Apple is a mechanism that allows the safe storage of confidential data, such as passwords, cryptographic keys, or user tokens. It’s implemented as an SQLite database. We can access it only via API Keychain interfaces.
In the macOS system, every app can create as many Keychains as they want. The structure of this Keychain is different in iOS. There’s just one Keychain available to all apps.
If you want to try Keychain, consider a ready-to-use library. In my case, it’s KeychainAccess. With this library, it’s easy to save the elements. All I have to do is to create a Keychain object and then, assign the value to a proper key:
A reading of the value is even more simple because after creating a Keychain object we only need to address the element that can be found under the selected key:
Keychain works well as a specialized database to save metadata and confidential information. Unfortunately, direct interaction with Keychain is complicated, especially in Swift. We need to use a Security framework that is mostly written in C language. This often means that we have to use external libraries that facilitate work with Keychain.
Core Data
Core Data is a framework for managing the model layer in your iOS app. This platform enables saving, following, modifying, and filtering data. It relies mostly on creating Core Data entities and relations between them. It uses SQLite as its constant storage but this framework itself is not a database. It ensures general and automated solutions to typical tasks related to object lifecycles and managing object graphs, including sustainability. By default, the Core Data framework doesn’t encrypt its data.
Using Core Data is much more complicated compared to the methods mentioned earlier and we could write a whole book about it. That’s why I will only describe the pros and cons of this solution now.
Core Data is an excellent choice in case of big tasks when we need to follow an object model with many units and relations. It has an undo and repeat mechanism, automatically handles light migrations, and it’s also very efficient and optimized.
When it comes to cons, I must mention an advanced thread policy, lack of built-in encryption, or higher entry threshold compared to other methods.
You know the basic ways of storing local data. Now, it’s time to find out how to choose the best ones in certain cases.
Keychain vs UserDefaults
The basic difference between these two methods is encryption.
Keychain is an encrypted container with passwords to many apps and safe services. Data is protected with a class structure similar to the one used to encrypt files. The elements added to the bunch of keys are coded as binary plist and encrypted with a 128-bit AES key for every element.
UserDefaults are stored as regular text in a bundlename.plist file on a device. Every person with access to a device can open or copy this file, and read unencrypted info.
For this reason, Keychain is a decidedly better option for storing passwords, tokens, and other authentication data.
The protection of user privacy is one of the basic elements of app development, so it’s crucial that confidential data is safe.
Tasks for Core Data
The first thing to do when working with elementary data is to create a data model file. The goal is to define a structure of app objects, including their types, properties, and relations. Then, to use Core Data you need to configure the Core Data stack that consists of:
- managed object model – it determines a data structure
- persistent store coordinator – it handles the connection with data storage
- managed object context – it controls the operations of creating, reading, updating, and deleting the objects.
Example
1. Local library:
- Librarians can add or delete books.
- They can filter the books by genre or author.
- They can sort the books by publication date.
- They can reserve books.
2. Singer and their tracks:
- 2 entities: singer and track.
- Relation: one to many between them.
Example: manager for Keychain
Summary
Data persistence in mobile apps is essential. That’s the reason why many of them take advantage of local data storage. Choosing the right method is extremely important because it can impact safety, performance time, or complexity of service.
Property List is great to store a small amount of data, and FileSystem should be considered, for example, when buffering the elements. UserDefaults, on the other hand, is perfect for saving user preferences, while Keychain ensures the safety of all the data that requires it. Last, but not least, there’s also Core Data which will handle the most complex tasks that involve many units and relations.
All these options have their benefits and limitations, so make sure you choose the one that serves you the best.