Realm is a mobile object-oriented NoSQL database developed as an alternative to SQLite and Core Data for iOS and Android. Data is stored in its own format with direct access to objects in memory, providing high read and write speed. Thanks to reactive notifications, automatic synchronization through Realm Sync and a cross-platform data model, Realm is used on over 1 billion devices (MongoDB data, 2025).
Key Takeaways
Realm is an object-oriented database created specifically for mobile applications. Unlike SQLite, Realm does not use SQL or a relational model — data is stored as live objects (Live Objects), accessed directly without an intermediate ORM layer. The developer works with regular language objects (Swift, Kotlin, Dart) that are automatically saved to the database.
Realm appeared in 2014 as a startup Realm Inc., and in 2019 was acquired by MongoDB Inc. Since then, Realm has been developed as MongoDB Realm — part of the unified Atlas Device Sync platform for mobile data synchronization. The basic local Realm database remains free and open source (Apache 2.0 license), while Realm Sync (cloud synchronization) is distributed by subscription.
According to the MongoDB Developer Report (2025), Realm is used in 15% of mobile applications as the primary local storage. The main use cases are applications with offline mode, complex object models and the need for synchronization between devices, where Realm outperforms SQLite in development speed and ease of working with objects.
Live Objects — each Realm object is linked to disk via memory-mapped file. Changing an object in any thread is immediately reflected in all other threads without manual notification. The UI automatically redraws when data changes through Realm notifications. Zero-copy — Realm does not copy data from the database into objects; objects are the data in memory.
Realm core is written in C++ and uses B+ Tree for data indexing. Unlike SQLite, where each row is a record in a relational table, Realm stores objects as nodes in a graph structure with direct references between them (forward pointers). Relationships between objects are not foreign keys but direct pointers, eliminating the need for JOIN operations.
Realm files (.realm extension) are memory-mapped — the OS itself manages loading file pages into memory as they are accessed. This allows Realm to work with databases larger than available RAM and provides read latency at the level of native language data structures (Swift Array or Kotlin List) for data that fits in RAM.
Memory-mapped files — Realm's key technology. Instead of copying data from the database file into language objects (as SQLite does via sqlite3_step + sqlite3_column_*), Realm maps the database file directly into the process address space. Language objects are simply pointers to data in mapped memory. Changing an object modifies the database on disk without a separate save call.
| Characteristic | Realm (zero-copy) | SQLite (copying) |
|---|---|---|
| Read | Direct memory access (nanoseconds) | Copy to object (microseconds) |
| Write | Immediate write to memory-mapped file | save() with SQL INSERT/UPDATE transaction |
| Relationships | Direct pointers (forward pointers) | Foreign key + JOIN |
| Memory consumption | File + OS page cache | Objects + SQLite page cache |
| Live objects | Yes (automatic update) | No (requires new fetch) |
Memory consumption is higher in Realm during active reading (the entire file or a large portion is mapped into memory), but lower during write operations since no memory allocation for object copies is required. For databases up to 1 GB on mobile devices, the memory-mapped approach provides better overall performance than SQLite with its intermediate copying.
SQLite is a relational DBMS with SQL and ACID transactions. Realm is an object NoSQL database with memory-mapped access. Core Data is Apple's ORM layer on top of SQLite. Each of the three approaches has its own application area and strengths. Realm wins in direct object access speed and reactivity, SQLite in versatility and maturity, Core Data in integration with the Apple ecosystem.
Realm's read performance is 10-50% higher than SQLite thanks to zero-copy access. Realm is also faster on writes due to the absence of SQL parsing and direct modification of memory-mapped pages. However, Realm may consume more RAM when working with large databases (>500 MB), since the file is fully mapped into the address space.
| Criterion | Realm | SQLite | Core Data |
|---|---|---|---|
| Type | NoSQL object DB | Relational DBMS (SQL) | ORM on top of SQLite |
| Read speed | High (zero-copy) | Medium | Medium (ORM overhead) |
| Reactivity | Live Objects (built-in) | Need wrapper (Room/GRDB) | NSFetchedResultsController |
| Synchronization | Realm Sync (MongoDB Atlas) | Custom implementation needed | NSPersistentCloudKitContainer |
| Platforms | iOS, Android, Flutter, RN, Node.js | iOS, Android, Web, Desktop | iOS/macOS only |
| Library size | ~4 MB | ~600 KB | Built into SDK |
The choice between Realm and SQLite often comes down to priorities: development speed and reactivity (Realm) versus control and versatility (SQLite). Realm is especially good for MVPs and prototypes where the data model changes frequently and it is important to get live UI updates quickly. SQLite/Room is preferable for mature products with large data volumes and complex analytical queries.
Realm Kotlin SDK is the official library for Android, written in Kotlin (not a wrapper over Java SDK). The data model is defined through classes inheriting from RealmObject, with the @PrimaryKey annotation for a unique identifier. Realm Kotlin supports Kotlin Coroutines, Flow and KSP (Kotlin Symbol Processing) for compile-time code generation.
RealmQuery provides a type-safe API for filtering without SQL. Queries are built using a method chain: query → filter → sort → find. The result can be returned as a list (loaded into memory) or as a Flow (reactive stream updating when data changes). Realm Kotlin also supports nested objects and lists (RealmList).
Data model Realm is defined as a data class implementing the RealmObject interface. The _id field is marked with @PrimaryKey for unique identification. A one-to-many relationship is implemented through RealmList — a list of other RealmObjects. All write operations (create, update, delete) are performed inside a write transaction block.
class Project : RealmObject {
@PrimaryKey
var _id: ObjectId = ObjectId().generate()
var name: String = ""
var tasks: RealmList<Task> = realmListOf()
}
class Task : RealmObject {
@PrimaryKey
var _id: ObjectId = ObjectId().generate()
var title: String = ""
var isComplete: Boolean = false
}
val config = Realm.Configuration.Builder(
schema = setOf(Project::class, Task::class)
).build()
val realm = Realm.open(config)
realm.write { transactionRealm ->
val project = copyToRealm(Project().apply {
name = "Mobile App"
})
project.tasks.add(copyToRealm(Task().apply {
title = "Design UI"
}))
}
val projects: Flow<RealmResults<Project>> =
realm.query<Project>()
.sort(Project::name, Sort.ASCENDING)
.asFlow()
Flow-returning query automatically emits new results upon any data change in Realm: adding, updating or deleting a Project or Task. The Flow approach pairs perfectly with Compose: collectAsState() in ViewModel automatically recomposes the UI when data changes without manually updating list adapters or using LiveData.
Realm Swift SDK provides a native Swift API with support for async/await, Combine Publishers and Swift Concurrency. Data models are defined as classes inheriting from Object, using @Persisted for properties. Realm Swift automatically updates objects on changes, and notifications (NotificationToken) allow subscribing to changes of individual objects or collections.
@ObservedRealmObject and @ObservedResults are Property Wrappers for SwiftUI that automatically redraw the View when Realm data changes. @ObservedResults works with query results, @ObservedRealmObject works with a specific object. Both properties cancel the subscription when the View is deinitialized.
SwiftUI integration Realm is one of the SDK's strong points. The @ObservedResults Property Wrapper links a Realm query with SwiftUI display. When any task changes, SwiftUI automatically recomposes the list. realm.writeAsync performs transactions on a background thread without blocking the UI.
import RealmSwift
class TaskItem: Object, Identifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var title: String = ""
@Persisted var isDone: Bool = false
@Persisted var priority: Int = 0
}
struct TaskListView: View {
@ObservedResults(TaskItem.self,
sortDescriptor: SortDescriptor(["priority"]))
var tasks
var body: some View {
List {
ForEach(tasks) { task in
TaskRow(task: task)
}
}
Button("Add Task") {
let realm = try! Realm()
try! realm.write {
realm.add(TaskItem(value: ["title": "New task"]))
}
}
}
}
@ObservedResults automatically manages the Realm query lifecycle: when the View is created, a subscription to changes is created; when destroyed, it is cancelled. The Property Wrapper accepts an optional predicate (NSPredicate) and sort descriptor. This eliminates the need to manually write fetch requests, create ViewModels and subscribe to Realm notifications.
Offline applications — Realm is ideal for scenarios where data must be available without internet. Built-in synchronization (Realm Sync with MongoDB Atlas) automatically resolves conflicts when connecting to the network. The app continues working with local data, and background sync updates it when a connection becomes available.
Reactive interfaces — Live Objects and Flow/Combine notifications make Realm a convenient choice for applications with frequently updating data: chats, task feeds, collaborative documents. The UI automatically updates when data is added, changed or deleted without manually calling reloadData or invalidate.
Cross-platform projects — Realm supports Kotlin Multiplatform (KMP), Flutter, React Native and Xamarin. The data model is defined once and used on all platforms. This reduces code duplication and guarantees schema consistency between iOS and Android applications with shared business logic.
Realm Sync is a cloud service built into MongoDB Atlas. The application automatically synchronizes data between the user's devices and the server. Conflicts are resolved using the "last write wins" strategy or through custom Conflict Resolution Functions on the server. Realm Sync supports partial synchronization (subset of data) to reduce traffic.
class SyncRepository {
private val app = App(
AppConfiguration(
appId = "my-realm-app-id"
)
)
suspend fun syncData() {
val user = app.login(Credentials.anonymous())
val config = SyncConfiguration.Builder(
user, setOf(Project::class)
).build()
val syncedRealm = Realm.open(config)
// Automatic synchronization is active
}
}
SyncConfiguration configures the connection to MongoDB Atlas and defines the set of models to synchronize. After user authentication, Realm automatically loads data from the cloud and merges it with local changes. Synchronization runs on a background thread and does not require manual calls — simply create a Realm instance with SyncConfiguration.
Frequently Asked Questions
Yes, the local Realm database is completely free (Apache 2.0 license). Realm Sync (cloud synchronization) via MongoDB Atlas is a paid service included in the Atlas Device Sync subscription. The free Atlas tier includes the first 500 MB of data and 1 million operations per month.
Yes, migration is possible by exporting data from SQLite to JSON and importing it into Realm on the first app launch. For large volumes, use streaming migration: write data to both databases in parallel while users transition to the new app version, then delete the SQLite file.
Realm Sync by default uses the "last write wins" strategy — the last write overwrites the previous one. For complex conflicts, configure custom Conflict Resolution Functions on the MongoDB Atlas server that merge changes from different devices according to the application's business logic (e.g., merging values instead of replacing).
Realm does not support SQL aggregations (SUM, AVG, GROUP BY) at the database level. For analytical queries, it is recommended to export data from Realm to MongoDB Atlas via Realm Sync and run aggregations through the MongoDB Aggregation Pipeline. For simple counts, use realm.query<T>().count().
Realm does not encrypt data by default. For encryption, provide a 64-byte key in the configuration: Realm.Configuration(encryptionKey: key). Encryption uses AES-256 + SHA-2 HMAC and increases operation overhead by 10-20%. Without encryption, the .realm file can be read by any process with file system access.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also