Apps are becoming more and more complex. Users expect great user experience, even without internet connection. An app that is not usable and treats offline as an error will not leave good impression. Internet connection is something that is not always and everywhere available. That’s why we have to save the most relevant user data locally, on the device. While there are situations where you can get away with some caching mechanism (saving JSON/XML files locally on the file system), most of the time you would need some kind of a mobile database.
There are a lot of options for developers to do this. You can use the good old SQLite, Apple’s Core Data, or some other solutions like Realm or Firebase. In this post, I will share my experiences with some of them.
SQLite is a relational database, that’s being used both on iOS and Android. It has been around for a long time. You have to write SQL queries in order to get the needed data. This means that you have to write a lot of code to use it. When you type the queries, it’s easy to make mistakes and this is something the compiler won’t detect. On iOS, there are a lot of wrappers around it, like for example FMDB.
Here’s a glimpse on how to use the SQLite database with Objective-C. As you can see, there’s a lot of code and this is only part of it. On top of this, you have to provide SQL queries, in form of NSStrings, with placeholders. I will not list the executeUpdate:arguments method, you can find it here and see for yourself how big of a method that is.
I have used SQLite in one project, which is still on the App Store. There are no bigger issues, the performance is good. But you can imagine how hard it is for maintenance and fixing/improving things. There have to be more sophisticated options, especially solutions that provide object-relational mapping.
CoreData is Apple’s framework for managing persistent model objects on iOS. It is built on top of SQLite database. According to Apple, it decreases the amount of code you have to write up to 70 percent. It supports most of the things you would expect from such framework. Core Data is not a classic relational database, it doesn’t store the data in tables and the data is not retrieved with SQL queries. Queries are created using NSPredicate, in a more object-oriented way. If you make changes in the database schema, there are migration tools that allow you to perform the migration. It provides features like full normalization and synchronous consistency across multiple isolated contexts.
Some basic concepts of Core Data are:
- Managed Object Model – represents a schema, which is basically a collection of entities (data models) that you use in your application.
- Managed Object Context – an object representing a single object space or scratch pad that you use to fetch, create, and save managed objects.
- Persistent Store Coordinator – A coordinator that associates persistent stores with a model (or a configuration of a model) and that mediates between the persistent stores and the managed object contexts.
The following image from the Apple documentation provides an overview of the Core Data architecture and the basic concepts.
I have used it in two projects and the general impression is that it has many good features and you don’t have to write a lot of code, like in the SQLite case. However, the project becomes too dependent of Core Data. It’s also not very reactive – you have to explicitly update the user interface whenever the model is updated.
Also, the managed object context is not thread safe. The default one is associated with the main thread, but there are cases where you would want to have multiple contexts in several threads in order not to block the user interface while performing some longer operations. I remember some weird and hard to debug crashes when having more than one context. Check this recent great tutorial on how to handle multiple managed object contexts in a Core Data application.
Facebook has also pointed out performance issues when using Core Data, for caching data in their News Feed. When they have replaced Core Data with their custom immutable data layer, they have experienced nearly 50% faster News feed.
In general, it’s good product, used by many iOS apps. Apple is behind it, which means it will be supported and improved in newer versions of the iOS SDK.
Active record is an approach to accessing data in a database. A database table or view is wrapped into a class; thus an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database; when an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view.
With MagicalRecord, the Core Data setup is faster and a lot of boilerplate code is already implemented in the framework. It allows simple, one-line fetches. Fetch requests can still be modified when request optimizations are needed. You can check this tutorial, for more info on MagicalRecord.
I have used it in one project few years ago, and it really is a nice Core Data wrapper. However, when I look at the git repository now, it’s not updated for 2 years, so I’m not sure whether it will be supported in the future.
You’ve all probably heard about Realm, the company that is really active in the community, by sharing videos from the most popular mobile conferences. Most recently, it is also known by the Realm Academy, which provides excellent learning paths (with videos), about the most relevant topics in the mobile world, such as architecture, reactive extensions, offline first design etc.
The core products of the company are the Realm Database and the Realm Platform. These products are integrated in apps that are used by over two billion users. The developers’ experiences are also very positive, so in our latest project we have decided to try the Realm Database.
Realm is very fast, it’s not built upon Core Data or SQLite – they have their proprietary data storage solution. They say that their solution is not object-relational mapper, their custom storage engine doesn’t need to convert data to and from object graphs. Objects are persisted on disk with very few type and structure conversions. It’s a Live Object Database, the accessed data is always up-to-date. There is no notion of “fetching” data from disk, which needs to be constantly reloaded. There is no need to duplicate your data model from disk into memory.
The Realm database is reactive – which means you can subscribe to notifications when the model is changed and update your user interface accordingly. There’s no need to explicitly call reload data or other setup methods.
Realm has sync mechanism between the data on the devices and the server. Everyone who has implemented this manually (myself included), knows how difficult it is to get this right, with a lot of edge cases, conflicts etc.
There are a lot of other cool features that the product supports that you can explore in the docs. The one that has influenced the decision to use it in our project is the possibility the database to be shared between the main app and its extensions.
You can create a database with configuration that supports app groups, and all the data will be available to your main iOS app, watch or widget. With the concept of live objects, you don’t have to worry about the consistency between those objects. It’s simply impressive.
Here’s an example on how to create such configuration:
The API is very simple, you can integrate it with very few lines of code. And the promise of fast is really fulfilled. Check this post for some comparisons with SQLite.
It’s also easy to get started – there are a lot of videos, tutorials and conference talks. In my opinion, it’s definitely the best option out there when it comes to mobile databases. One honourable mention is the Firebase Realtime Database, which I haven’t used so far (only used the Firebase notifications / analytics part). It also seems very good, but one restriction is that you can’t host Firebase on your own servers.
At the end, the database selection depends on many factors, specific to the project. It’s good to know that you have a lot of options and approaches that leave you out of I-dont-support-offline types of excuses.