There are many approaches and technical options for permanently storing data on mobile devices. Databases—and SQL databases in particular—are very common in this context. However, they often include features that are only truly relevant for use cases outside of apps. While complex queries and aggregations may be standard in many cloud and server applications, access speed is often far more important on mobile devices. Realm from MongoDB has established itself specifically for this purpose. It is a NoSQL/object database optimized for access.
Delimitation
As a database for mobile devices, Realm offers various mechanisms for local live updates as well as cloud-based synchronization across multiple devices and operating systems. However, that is not the focus of this article. Instead, our experts will highlight the key considerations when using Realm as a pure storage solution.
Realm as an intuitive, minimalist interface
In object-oriented programming, it has now become standard practice in many applications to separate different layers when creating classes—for example, separating business logic from the storage layer or from a web interface. The reasons for this can be attributed to the formats of their properties and the Single Responsibility Principle (SRP). For storage and web interfaces, these classes are known as Data Transfer Objects, or DTOs for short. In Realm, DTOs are generated in the background based on the application’s own data model, and all fields are treated as lazy access. This goes hand in hand with Realm’s NoSQL approach. Data is therefore only read and written when the fields of the DTOs are accessed—and only those specific fields. To save or update data, there are effectively two options:
- Create a new DTO and use the primary key to create a new object or update an existing one, and populate all relevant fields with data
- Load an existing DTO from the database using a query and populate its fields with new values
Although Realm only writes fields lazily, when a new DTO is created, all fields are still written to the database—even those that developers did not populate themselves. Fields left as null would therefore still overwrite existing fields of the same object in the database during an update using a new DTO. Updating individual fields must therefore always be performed via a preceding query.
A simple Realm object might look like this:

Object Dependencies and Nested Object Graph
Since Realm is an object database and explicitly supports nested objects, complex graphs—or even cycles—can very quickly emerge as storage options in Realm.
Example: Nested Objects:

Internally, Realm works behind the scenes with the generated DTOs using references to memory addresses that point to the same memory locations. However, this only applies to simple properties. References to other nested objects are mapped to their own instances through wrapping in DTOs. When used in, for example, HashSets or lists, this could quickly lead to unwanted duplicates. To revisit the code example from earlier, two different ShoppingCarts could, for instance, reference the same ShopItem. Realm would create two instances of the ShopItem, both of which would refer to the same properties; consequently, a change to one would also cause a change to the other.

DTO vs. Models
Realm is designed for direct UI binding through its own adapters, which respond to changes in the database and update the UI. While this may seem very convenient at first glance, it quickly turns out to be a technical hurdle—due to the ever-increasing memory requirements of the live database, which is kept open. Furthermore, direct binding of DTOs contradicts a clean abstraction between the database representation and an app’s model layer. The common incompatibilities between various data types are just one of the typical problems (e.g., File/URI vs. String). Volatile properties, which are used only at runtime and are not saved—and therefore must be explicitly marked as “ignored”—pose another problem that can quickly become a pitfall during automatic database migration and lead to an incompatible database schema. On iOS in particular, the Swift API for Realm is designed to automatically perform migrations from one database schema version to the next without offering developers the option of explicit migration.
Example of a business logic model based on the DTO described above:

Converting DTOs into model objects presents the challenge of the nested object graph described in the previous section. Identical objects represented by separate DTO instances could easily be instantiated as distinct objects during conversion. Changes to their properties could be made independently of one another at runtime, but when writing back to the database, a reverse conversion to DTOs would result in two DTOs with different properties and the same primary key.
The actual result would depend on the order of processing and would therefore be considered undefined. A caching strategy for model instances during the DTO-to-model conversion step would be a way out of this situation. This way, all objects would reference the same instances of nested objects, and there would be no duplicates. Although a reverse conversion back to DTOs would still generate multiple DTOs, their properties would all be identical. The only overhead to consider here is that certain DTOs might be written to the database multiple times, depending on the number of their references.
Example:

Cyclic Nested Objects
As in any complex system, a cycle between various objects is conceivable here as well. The code example above shows a cycle between `User` and `ShoppingCart`, which, while useful for accessing data, poses a challenge for DTO conversion. Internally, Realm uses a backlink from table columns to nested objects, creating a doubly chained reference. For example, if we look at the `UserDTO`, Realm uses a column within the `ShoppingCartDTO` that references back to the `UserDTO`. Unfortunately, this backlink is not available through Realm’s high-level language libraries in Swift or Kotlin. Therefore, the DTOs contain only a single reference from the `UserDTO` to the `ShoppingCartDTO`—in other words, a tree.
Technically, a cyclic graph could be created using an additional reference. Realm itself is capable of handling direct cyclic references. So you could introduce a reference to UserDTO in ShoppingCartDTO. However, in the context of a DTO-to-model conversion, this would not be recommended. A common approach here is reminiscent of the foreign key mechanisms used in SQL databases. One possible solution would be to include a property in the ShoppingCartDTO that contains the user’s Universally Unique Identifier (UUID). During conversion, the User object could then be loaded from the cache or from Realm. Another option would be to avoid having a back reference, provided that the conversion always starts with the User object and moves deeper into the tree. For example, when converting the `ShoppingCartDTO`, it would be possible to assign the already-converted user instance to the `ShoppingCart`. In this case, it would make sense to implement `ShoppingCart` as a `WeakProperty`.
In the application's model layer, this could be represented exactly this way and modeled as a direct reference between objects. During the DTO-to-model conversion, in combination with model caching, it is important to ensure that the parent object `User` is first stored in the cache before the nested object `ShoppingCart` is loaded. Otherwise, the ShoppingCart would not be able to find the parent object User in the cache based on the primary key and would create a new instance, resulting in two independent User instances.
Outlook
The details discussed so far are inherent to Realm and can be found in this or a similar form in other databases as well. In addition, Realm also has some hurdles that are not inherent but rather stem from bugs and internal issues. This applies, for example, to the maintained ChangeLog for the Kotlin/Realm API, and it’s definitely worth taking a closer look at it. As for the causes and bugs, MongoDB/Realm is always playing catch-up when it comes to fixing them.
Although, in our experience, Realm may not be the most stable of all databases, it is an extremely reliable tool when used with strictly sequential access. Our experts believe that Realm is particularly well-suited for large data sets with very simple access patterns.