Skip to content

Realm Swift

Databases & CachingDatabaseSwift

What it is

Realm is an embedded object database for Swift with live, auto-updating objects, reactive queries and optional device-to-cloud sync.

Subclass Object with @Persisted properties. Queries return live, lazily-loaded results that update automatically and can be observed.

Installation

.package(url: "https://github.com/realm/realm-swift.git", from: "20.0.0")

Getting started

The smallest useful thing you can do with it, and what each part means.

Models, writes and live results
final class Book: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var id: ObjectId
    @Persisted(indexed: true) var title: String
    @Persisted var year: Int
    @Persisted var author: Author?
}

let realm = try await Realm()

try await realm.asyncWrite {
    realm.add(Book(value: ["title": "Dune", "year": 1965]))
}

// Live and lazy — not loaded into memory until accessed.
let recent = realm.objects(Book.self)
    .where { $0.year >= 1990 }
    .sorted(by: \.year, ascending: false)

let token = recent.observe { changes in
    switch changes {
    case .initial(let books): render(books)
    case .update(_, let deletions, let insertions, let modifications):
        applyDiff(deletions, insertions, modifications)
    case .error(let error): report(error)
    }
}
The change notification carries index-level diffs, which is exactly what a table or collection view needs for animated updates rather than a full reload.

Advanced usage

Where the library earns its place over a simpler alternative.

Thread confinement and migrations
// Realm objects are confined to the thread that created them.
// Passing one across threads throws — use a ThreadSafeReference.
let reference = ThreadSafeReference(to: book)
DispatchQueue.global().async {
    let realm = try! Realm()
    guard let book = realm.resolve(reference) else { return }
    try! realm.write { book.year = 1966 }
}

let config = Realm.Configuration(
    schemaVersion: 2,
    migrationBlock: { migration, oldVersion in
        if oldVersion < 2 {
            migration.enumerateObjects(ofType: Book.className()) { old, new in
                new?["isbn"] = old?["legacyIsbn"] ?? ""
            }
        }
    }
)
Realm.Configuration.defaultConfiguration = config
Thread confinement is Realm's sharpest edge. It is the source of most crashes in Realm codebases, and ThreadSafeReference is the sanctioned way across boundaries.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Realm accessed from incorrect thread
An object or Realm instance crossed a thread boundary. Open a new Realm on that thread and resolve a ThreadSafeReference.
Migration is required due to the following errors
The model changed without a schema version bump. Increment schemaVersion and supply a migrationBlock.

Best practices

  • Never pass Realm objects between threads; use ThreadSafeReference or re-query.
  • Bump schemaVersion and write a migration for every model change.
  • Invalidate observation tokens when the observer is deallocated, or they leak.
  • Keep write transactions short — they block other writers.

Background

Why it exists, and what it was reacting to.

Realm stores objects directly rather than mapping them to rows, so there is no ORM layer. Its objects are live views onto the database, updating in place when the underlying data changes.