Local Data Persistence Using Realm - codepath/ios_guides GitHub Wiki

Local Data Persistence - Using Realm

Overview

Realm Swift is a mobile object database that persists data on-disk and lets you read and write Swift objects without an ORM. This guide covers using Realm for local, on-device persistence in an iOS app.

Status (June 2026): MongoDB deprecated Atlas Device Sync and ended that hosted service on September 30, 2025. The local Realm database remains available as an open-source project. Active development happens on the community branch of realm/realm-swift; the most recent release at the time of writing is v20.0.4. If you only need on-device persistence (no cloud sync), Realm still works — but for new projects you may also want to evaluate Apple's native SwiftData and Core Data before committing.

Requirements

Realm Swift's supported Xcode range moves with each release — see the install guide and the releases page for the values that match the version you're integrating. As of writing, the project ships two active release lines: a 10.54.x line that requires Xcode 16.3+, and a newer 20.x line that requires Xcode 26+. Pick the line whose Xcode requirement matches your build environment.

Other requirements (stable across recent releases):

  • Deployment target of at least iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 4.0, or visionOS 1.0.
  • Reflection enabled — do not set SWIFT_REFLECTION_METADATA_LEVEL = none in your build settings, or Realm will not see your model properties.

Project Setup — Swift Package Manager (recommended)

Swift Package Manager is the simplest and most current way to add Realm to an Xcode project.

  1. In Xcode, choose File ▸ Add Package Dependencies….

  2. In the search box, paste the repository URL:

    https://github.com/realm/realm-swift.git
    
  3. Set the Dependency Rule to Up to Next Major Version and leave the version at the latest release (see the releases page). Click Add Package.

  4. When Xcode prompts you to pick a package product, select RealmSwift (the Swift API) and click Add Package. As of 10.49.3, you add either RealmSwift or Realm, not both — pick Realm only if your project is Objective-C-only.

  5. (Optional, but recommended for App Store submissions.) To include Realm's bundled Apple Privacy Manifest, build RealmSwift as a dynamic framework: in your target's General tab, expand Frameworks and Libraries, and change RealmSwift's embed setting from Do Not Embed to Embed & Sign.

Project Setup — CocoaPods (alternative)

If your project already uses CocoaPods (see the CocoaPods guide), you can integrate Realm that way. You need CocoaPods 1.10.1 or newer.

  1. From your project directory, refresh the CocoaPods spec repos:

    pod repo update
    
  2. If you don't already have one, create a Podfile:

    pod init
    
  3. Open the Podfile and add RealmSwift to your app target. The install guide currently uses a ~>10 pin in its example; if you're on the newer 20.x line, substitute ~>20 instead. A minimal Podfile looks like:

    platform :ios, '12.0'
    
    target 'MyApp' do
      use_frameworks!
      pod 'RealmSwift', '~>10'
    end
    
  4. Install the dependency:

    pod install
    
  5. Close the .xcodeproj and open the generated .xcworkspace instead — that's the file you use from now on.

No post_install Swift-version override is needed for modern Realm versions. The legacy config.build_settings['SWIFT_VERSION'] = '3.0' block that older guides recommended was a workaround for Xcode 8 and is obsolete; do not add it.

Define an Object Model

Import RealmSwift in any file that uses Realm, and subclass Object for each model. Mark stored properties with the @Persisted property wrapper (introduced in Realm Swift 10.10.0, this is the current API and replaces the older @objc dynamic / dynamic style):

import RealmSwift

class Todo: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var name: String = ""
    @Persisted var status: String = ""
    @Persisted var ownerId: String = ""

    convenience init(name: String, ownerId: String) {
        self.init()
        self.name = name
        self.ownerId = ownerId
    }
}

You can model relationships by referencing other Object types directly, including List<T> for to-many relationships.

Opening a Realm

The simplest setup uses the default on-disk realm:

let realm = try! Realm()

For more control (custom file URL, in-memory storage, schema migration) construct a Realm.Configuration and pass it to Realm(configuration:).

Saving Data

All mutations to a realm — inserts, updates, deletes — must happen inside a write transaction. The common pattern is realm.write { ... }:

let todo = Todo(name: "Do laundry", ownerId: "alice")

try! realm.write {
    realm.add(todo)
}

To update an existing object, mutate its properties inside another write block:

try! realm.write {
    todo.status = "InProgress"
}

To remove an object, call realm.delete(_:) inside a write block:

try! realm.write {
    realm.delete(todo)
}

Retrieving Data

Read queries return live, auto-updating collections. Use realm.objects(_:) to get every instance of a type:

let todos = realm.objects(Todo.self)

Filter with the type-safe .where API (Realm Swift's modern replacement for NSPredicate string queries):

let inProgress = todos.where {
    $0.status == "InProgress"
}

Because the returned Results are live, the same todos reference reflects later writes from any thread without re-querying.

Observing Changes

You can subscribe to changes on a realm, a collection, or an individual object using observe(_:). Retain the returned notification token for as long as you want updates:

let token = todos.observe { changes in
    switch changes {
    case .initial:
        // First snapshot — Results are populated.
        break
    case .update(_, let deletions, let insertions, let modifications):
        print("deleted: \(deletions), inserted: \(insertions), modified: \(modifications)")
    case .error(let error):
        fatalError("\(error)")
    }
}

// When you no longer want updates:
token.invalidate()

Threading Notes

A Realm instance is tied to the thread or actor on which it was opened. To use Realm from a background thread, open a fresh Realm() on that thread, perform the write, and either pass an object's thread-safe reference (ThreadSafeReference) back to the main thread or re-fetch by primary key. For Swift concurrency / actor isolation, see Realm's actor support docs.

Further Reading