realm-swift Documentation

Realm is a mobile database: a replacement for Core Data & SQLite

16,601 stars Objective-C 1 file · ~100,690 tokens #database#ios#mobile#mobile-database#objective-c#realtime#swift#sync
RAW Doc

Technical Documentation: realm/realm-swift

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

About Realm Database

Realm is a mobile database that runs directly inside phones, tablets or wearables.
This repository holds the source code for the iOS, macOS, tvOS & watchOS versions of Realm Swift & Realm Objective-C.

Why Use Realm

  • Intuitive to Developers: Realm’s object-oriented data model is simple to learn, doesn’t need an ORM, and lets you write less code.
  • Built for Mobile: Realm is fully-featured, lightweight, and efficiently uses memory, disk space, and battery life.
  • Designed for Offline Use: Realm’s local database persists data on-disk, so apps work as well offline as they do online.

Object-Oriented: Streamline Your Code

Realm was built for mobile developers, with simplicity in mind. The idiomatic, object-oriented data model can save you thousands of lines of code.

swift
// Define your models like regular Swift classes
class Dog: Object {
    @Persisted var name: String
    @Persisted var age: Int
}
class Person: Object {
    @Persisted(primaryKey: true) var _id: String
    @Persisted var name: String
    @Persisted var age: Int
    // Create relationships by pointing an Object field to another Class
    @Persisted var dogs: List<Dog>
}
// Use them like regular Swift objects
let dog = Dog()
dog.name = "Rex"
dog.age = 1
print("name of dog: \(dog.name)")

// Get the default Realm
let realm = try! Realm()
// Persist your data easily with a write transaction
try! realm.write {
    realm.add(dog)
}

Live Objects: Build Reactive Apps

Realm’s live objects mean data updated anywhere is automatically updated everywhere.

swift
// Open the default realm.
let realm = try! Realm()

var token: NotificationToken?

let dog = Dog()
dog.name = "Max"

// Create a dog in the realm.
try! realm.write {
    realm.add(dog)
}

//  Set up the listener & observe object notifications.
token = dog.observe { change in
    switch change {
    case .change(let properties):
        for property in properties {
            print("Property '\(property.name)' changed to '\(property.newValue!)'");
        }
    case .error(let error):
        print("An error occurred: (error)")
    case .deleted:
        print("The object was deleted.")
    }
}

// Update the dog's name to see the effect.
try! realm.write {
    dog.name = "Wolfie"
}

SwiftUI

Realm integrates directly with SwiftUI, updating your views so you don't have to.

swift
struct ContactsView: View {
    @ObservedResults(Person.self) var persons

    var body: some View {
        List {
            ForEach(persons) { person in
                Text(person.name)
            }
            .onMove(perform: $persons.move)
            .onDelete(perform: $persons.remove)
        }.navigationBarItems(trailing:
            Button("Add") {
                $persons.append(Person())
            }
        )
    }
}

Fully Encrypted

Data can be encrypted in-flight and at-rest, keeping even the most sensitive data secure.

swift
// Generate a random encryption key
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { (pointer: UnsafeMutableRawBufferPointer) in
    guard let baseAddress = pointer.baseAddress else {
        fatalError("Failed to obtain base address")
    }
    SecRandomCopyBytes(kSecRandomDefault, 64, baseAddress)
}

// Add the encryption key to the config and open the realm
let config = Realm.Configuration(encryptionKey: key)
let realm = try Realm(configuration: config)

// Use the Realm as normal
let dogs = realm.objects(Dog.self).filter("name contains 'Fido'")

Getting Started

We support installing Realm via Swift Package Manager, CocoaPods, Carthage, or by importing a dynamic XCFramework.

For more information, see our Quick Start.

Documentation

The documentation can be found in the docs/ directory.

The API reference can be generated from source using
jazzy by running sh build.sh docs from the root of this repository.

Getting Help

  • Need help with your code?: Look for previous questions with therealm tag on Stack Overflow or ask a new question. For general discussion that might be considered too broad for Stack Overflow, use the Community Forum.
  • Have a bug to report? Open a GitHub issue. If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
  • Have a feature request? Open a GitHub issue. Tell us what the feature should do and why you want the feature.

Building Realm

In case you don't want to use the precompiled version, you can build Realm yourself from source.

Prerequisites:

  • Building Realm requires Xcode 15.3 or newer.
  • Building Realm documentation requires jazzy

Once you have all the necessary prerequisites, building Realm just takes a single command: sh build.sh build.
You'll need an internet connection the first time you build Realm to download the core binary.
This will produce Realm.xcframework and RealmSwift.xcframework in build/Release/.

Run sh build.sh help to see all the actions you can perform (build ios/osx, generate docs, test, etc.).

Contributing

See CONTRIBUTING.md for more details!

Code of Conduct

This project adheres to the MongoDB Code of Conduct.
By participating, you are expected to uphold this code. Please report
unacceptable behavior to [email protected].

License

Realm Objective-C & Realm Swift are published under the Apache 2.0 license.
Realm Core is also published under the Apache 2.0 license and is available
here.

Feedback

If you use Realm and are happy with it, please consider sending out a tweet mentioning @realm to share your thoughts!

And if you don't like it, please let us know what you would like improved, so we can fix it!


Overview (`docs/README.md`)

Realm SDK for Swift

Use the Realm SDK for Swift to develop iOS, macOS, watchOS and tvOS
apps in Swift and Objective-C.

Get Started with the Swift SDK

These docs contain minimal-explanation code examples of how to work
with the Swift SDK.

To get started with SwiftUI, see: SwiftUI Quick Start

Install the Swift SDK

Use Swift Package Manager, CocoaPods, or Carthage to
Install the SDK for iOS, macOS, tvOS, and watchOS in your project.

Import RealmSwift in your project files to get started.

Define an Object Schema

Use Swift to idiomatically define an object schema.

Open a Database

The SDK's database - Realm - stores objects in files on your
device. Or you can open an in-memory database which does not
create a file.

Configure and open a database to specify the options for your database file.

Read and Write Data

  • Create, read, update, and delete objects from the device database.
  • Filter data using the SDK's type-safe .where syntax, or construct an NSPredicate.

React to Changes

Live objects mean that your data is always up-to-date.
You can register a notification handler
to watch for changes and perform some logic, such as updating
your UI. Or in SwiftUI, use the Swift property wrappers
to update Views when data changes.

Realm SwiftUI

The Swift SDK offers property wrappers and convenience
features designed to make it easier to work with SwiftUI.
For example View code that demonstrates common SwiftUI
patterns, check out the SwiftUI documentation.

swift
struct SearchableDogsView: View {
    @ObservedResults(Dog.self) var dogs
    @State private var searchFilter = ""

    var body: some View {
        NavigationView {
            // The list shows the dogs in the realm.
            List {
                ForEach(dogs) { dog in
                    DogRow(dog: dog)
                }
            }
            .searchable(text: $searchFilter,
                        collection: $dogs,
                        keyPath: \.name) {
                ForEach(dogs) { dogsFiltered in
                    Text(dogsFiltered.name).searchCompletion(dogsFiltered.name)
                }
            }
        }
    }
}

Generating API Reference Docs

You can generate the API docs locally by running sh build.sh docs from the root of this repository.
This requires installation of jazzy.
You will find the output in docs/swift_output/ and docs/objc_output/.


Overview (`examples/README.md`)

Realm Examples

Included in this folder are sample iOS/OSX apps using Realm.

iOS (Objective-C)

The following examples are located in the ios/objc/RealmExamples.xcodeproj project:

Simple

This app covers several introductory concepts about Realm. Without any UI distractions, just a little console output.

TableView

This app demonstrates how Realm can be the data source for UITableViews.

You can add rows by tapping the add button and remove rows by swiping right-to-left.

The application also demonstrates how to import data in a background thread.

GroupedTableView

A sample app to demonstrate how to use Realm to populate a table view with sections.

Migration

This example showcases Realm's migration features.

REST

Using data from FourSquare, this example demonstrates how to populate a Realm with external json data.

Encryption

This simple app shows how to use an encrypted realm.

This simple app demonstrates how to define models with inverse relationships using -linkingObjectsOfClass:forProperty:.

Installation Instructions

  1. Download the macOS version of the Realm Mobile Platform.
  2. Run a local instance of the Realm Mobile Platform.
  3. Open the Realm Object Server Dashboard in your browser by visiting 'http://localhost:9080'.
  4. Create a user account with the email '[email protected]' and the password 'password'.
  5. Build the Draw app and deploy it to iOS devices on the same network as your Mac.

iOS (Swift)

In the ios/swift/RealmExamples.xcodeproj project, you will find the following examples:

GettingStarted.playground

This is a Swift Playground that goes over a few Realm basics.

Simple

This app covers several introductory concepts about Realm. Without any UI distractions, just a little console output.

TableView

This app demonstrates how Realm can be the data source for UITableViews.

You can add rows by tapping the add button and remove rows by swiping right-to-left.

The application also demonstrates how to import data in a background thread.

GroupedTableView

A sample app to demonstrate how to use Realm to populate a table view with sections.

Migration

This example showcases Realm's migration features.

Encryption

This simple app shows how to use an encrypted realm.

This simple app demonstrates how to define models with inverse relationships using linkingObjectsOfClass(_:forProperty:).

Projections

This app demonstrates how to define Projection on Realm Object and how to use it in SwiftUI application.

AppClip / AppClipParent

These two targets demonstrate how to use Realm to persist data between an App Clip and its parent.

Example Usage

For the purpose of this example, the app clip invocation and parent application download is simulated by running each target.

For more information on complete App Clip flow see: Responding to invocations and Launch Experience.

Note: When testing App Group Entitlements on MacOS (including the iOS simulator), containerURL(forSecurityApplicationGroupIdentifier:) will always return the shared directory URL, even when the group identifier is invalid. Be sure to test on physical devices with non-simulated iOS for expected security behavior. See Return Value.

OSX (Objective-C)

In the osx/objc/RealmExamples.xcodeproj project, you will find the following examples:

JSONImport

This is a small OS X command-line program which demonstrates how to import data from JSON into a Realm.

Open the project in Xcode, and press "Run" to build and run the program. It will write output to the console.

Installation Examples

The installation/ directory contains example Xcode projects demonstrating how
to install Realm Objective-C and Realm Swift from all available methods defined
in https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/install/#install-realm-for-ios--macos--tvos--and-watchos.

tvOS (Objective-C)

DownloadCache

A tvOS app that demonstrates how to use Realm to store data and display data from a REST API.

PreloadedData

A tvOS app that demonstrates how to use a Realm file included in your app bundle.

tvOS (Swift)

DownloadCache

A tvOS app that demonstrates how to use Realm to store data and display data from a REST API.

PreloadedData

A tvOS app that demonstrates how to use a Realm file included in your app bundle.


Overview (`plugin/README.md`)

Realm Plugin

The Realm Plugin for Xcode adds several useful features for developing with Realm:

  1. A LLDB script which adds support for inspecting the property values of
    persisted RLMObjects in the debugger pane.
  2. File templates for RLMObject subclasses.
  3. A menu item in Xcode's 'File' menu to quickly launch the Realm Browser.
    Note that this item will only appear in Xcode 7 and in unsigned versions of
    Xcode 8 or later (not recommended).

To install the Realm Plugin, open RealmPlugin.xcodeproj and Build. This will
prompt for your password. After building the plugin, restart Xcode.


Overview (`examples/ios/swift/Migration/README.md`)

Migration Examples

The Migration project shows several examples of migrations and migration blocks.

The purpose of this example is to provide a more in depth view of certain problems and pitfalls users might face when
migrations get more complex and the number of versions increases.

How to use the example

You can build and run the project as is. Migrations from all prior version to the current version will be checked:

  • v0 -> v5
  • v1 -> v5
  • v2 -> v5
  • v3 -> v5
  • v4 -> v5

The files to look at are located in the Examples folder. Every file contains an extract of everything necessary for
this version (schema version, objects, migration and migration checks).

If you want to compare older versions among each other (i.e. v2 -> v3) you can do so by enabling the schema version, for example:

  • Deactivate version 5 by setting #define SCHEMA_VERSION_5 1 to #define SCHEMA_VERSION_5 0 in Example_v5.h.
  • Activate version 3 by seting #define SCHEMA_VERSION_3 0 to #define SCHEMA_VERSION_3 1 in Example_v3.h.

Example Projects (`docs/example-projects.md`)

Realm SDK Example Projects

Explore engineering and expert-provided example projects to learn best
practices and common development patterns for the Swift SDK.

Project Name Description Source Code
Integrating In-App Purchases Use [DELETE ME]'s efficient data management and synchronization capabilities to build a recipes library with in-app purchases (IAP) using StoreKit. Swift
RTicket Build a simple issue ticket system with Realm and SwiftUI. Swift
RCurrency Use Realm to cache data retrieved from an API and access the data offline. Swift
RChat Build a simple chat app with SwiftUI and Realm. Swift

Install (`docs/install.md`)

Install the SDK for iOS, macOS, tvOS, and watchOS

Overview

Realm SDK for Swift enables you to build iOS, macOS, tvOS,
and watchOS applications using either the Swift or Objective-C programming
languages. This page details how to install the SDK in your project and get
started.

Prerequisites

Before getting started, ensure your development environment
meets the following prerequisites:

  • Your project uses an Xcode version and minimum OS version listed in the
    OS Support section of this page.
  • Reflection is enabled in your project. The Swift SDK uses reflection to
    determine your model's properties. Your project must not set
    SWIFT_REFLECTION_METADATA_LEVEL = none, or the SDK cannot see properties
    in your model. Reflection is enabled by default if your project does
    not specifically set a level for this setting.

Installation

You can use SwiftPM, CocoaPods, or Carthage to add the
Swift SDK to your project.

Tip:
The SDK uses Realm Core database for device data persistence. When you
install the Swift SDK, the package names reflect Realm naming.

Swiftpm

Add Package Dependency

In Xcode, select File > Add Packages....

Specify the Repository

Copy and paste the following into the search/input box.

sh
https://github.com/realm/realm-swift.git

Specify Options

In the options for the realm-swift package, we recommend setting
the Dependency Rule to Up to Next Major Version,
and enter the current Realm Swift SDK version . Then, click Add Package.

Select the Package Products

Version changed: 10.49.3
Instead of adding both, only add one package.

Select either RealmSwift or Realm, then click Add Package.

  • If you use Swift or Swift and Objective-C APIs, add RealmSwift.
  • If you use only Objective-C APIs, add Realm.

(Optional) Build RealmSwift as a Dynamic Framework

To use the Privacy Manifest supplied by the SDK, build RealmSwift
as a dynamic framework. If you build RealmSwift as a static
framework, you must supply your own Privacy Manifest.

To build RealmSwift as a dynamic framework:

  1. In your project Targets, select your build target.
  2. Go to the General tab.
  3. Expand the Frameworks and Libraries element.
  4. For the RealmSwift framework, change the
    Embed option from "Do Not Embed" to "Embed & Sign."

Now, Xcode builds RealmSwift dynamically, and can provide the
SDK-supplied Privacy Manifest.

Cocoapods

If you are installing with CocoaPods, you need CocoaPods 1.10.1 or later.

Update the CocoaPods repositories

On the command line, run pod repo update to ensure
CocoaPods can access the latest available Realm versions.

Initialize CocoaPods for Your Project

If you do not already have a Podfile for your project,
run pod init in the root directory of your project to
create a Podfile for your project. A Podfile allows you
to specify project dependencies to CocoaPods.

Add the SDK as a Dependency in Your Podfile

Objective-C

Add the line pod 'Realm', '~>10' to your main and test
targets.

Add the line use_frameworks! as well if it is not
already there.

When done, your Podfile should look something like this:

text
# Uncomment the next line to define a global platform for your project
# platform :ios, '11.0'

target 'MyDeviceSDKProject' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!

# Pods for MyDeviceSDKProject
pod 'Realm', '~>10'

target 'MyRealmProjectTests' do
   inherit! :search_paths
   # Pods for testing
   pod 'Realm', '~>10'
end

end

Swift

Add the line use_frameworks! if it is not
already there.

Add the line pod 'RealmSwift', '~>10' to your main and test
targets.

When done, your Podfile should look something like this:

text
platform :ios, '12.0'

target 'MyDeviceSDKProject' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for MyDeviceSDKProject
  pod 'RealmSwift', '~>10'

end

Install the Dependencies

From the command line, run pod install to fetch the
dependencies.

Use the CocoaPods-Generated File

Use the CocoaPods-Generated .xcworkspace File

CocoaPods generates an .xcworkspace file for you. This
file has all of the dependencies configured. From now on,
open this file -- not the .xcodeproj file -- to work
on your project.

Carthage

If you are installing with Carthage, you need Carthage 0.33 or later.

Add the SDK as a Dependency in Your Cartfile

Add the SDK as a dependency by appending the line github "realm/realm-swift" to your Cartfile.

You can create a Cartfile or append to an existing one by
running the following command in your project directory:

bash
echo 'github "realm/realm-swift"' >> Cartfile

Install the Dependencies

From the command line, run carthage update --use-xcframeworks
to fetch the dependencies.

Add the Frameworks to Your Project

Carthage places the built dependencies in the Carthage/Build
directory.

Open your project's xcodeproj file in Xcode. Go to
the Project Navigator panel and click your application
name to open the project settings editor. Select the
General tab.

In Finder, open the Carthage/Build/ directory. Drag the
RealmSwift.xcframework and Realm.xcframework files
found in that directory to the Frameworks,
Libraries, and Embedded Content section of your
project's General settings.

Dynamic Framework

Download and Extract the Framework

Download the latest release of the Swift SDK and extract the zip.

Copy Framework(s) Into Your Project

Drag Realm.xcframework and RealmSwift.xcframework (if using)
to the File Navigator of your Xcode project. Select the
Copy items if needed checkbox and press Finish.

Tip:
If using the Objective-C API within a Swift project, we
recommend you include both Realm Swift and Realm Objective-C in your
project. Within your Swift files, you can access the Swift API and
all required wrappers. Using the RealmSwift API in mixed
Swift/Objective-C projects is possible because the vast majority of
RealmSwift types are directly aliased from their Objective-C
counterparts.

Import the SDK

Tip:
The SDK uses Realm Core database for device data persistence. When you
import the Swift SDK, the package names reflect Realm naming.

Add the following line at the top of your source files to use the SDK:

Objective-C

objectivec
#include <Realm/Realm.h>

Swift

swift
import RealmSwift

App Download File Size

The SDK should only add around 5 to 8 MB to your app's download
size. The releases we distribute are significantly larger because they
include support for the iOS, watchOS and tvOS simulators, some debug symbols,
and bitcode, all of which are stripped by the App Store automatically when
apps are downloaded.

Troubleshooting

If you have build issues after using one of these methods to install
the SDK, see our troubleshooting guidelines
for information about resolving those issues.

OS Support

Important:
There are special considerations when using the SDK with
tvOS. See Build for tvOS for more information.

Xcode 15

Version changed: 10.50.0
Minimum required Xcode version is 15.1

Supported OS Realm
iOS 12.0+ X
macOS 10.14+ X
tvOS 12.0+ X
watchOS 4.0+ X
visionOS 1.0+ X

Xcode 14

Version changed: 10.50.0
Removed support for Xcode 14.

Swift SDK version 10.50.0 drops support for Xcode 14. For v10.49.3 and earlier,
these Xcode 14 requirements apply:

  • Xcode version 14.1 or higher.
  • When using Xcode 14, a target of iOS 11.0 or higher, macOS 10.13 or higher, tvOS 11.0 or higher, or watchOS 4.0 or higher.

Swift Concurrency Support

The Swift SDK supports Swift's concurrency-related language features.
For best practices on using the Swift SDK's concurrency features, refer
to the documentation below.

Async/Await Support

Starting with Realm Swift SDK Versions 10.15.0 and 10.16.0, many of the
Realm APIs support the Swift async/await syntax. Projects must
meet these requirements:

Swift SDK Version Swift Version Requirement Supported OS
10.25.0 Swift 5.6 iOS 13.x
10.15.0 or 10.16.0 Swift 5.5 iOS 15.x

If your app accesses Realm in an async/await context, mark the code
with @MainActor to avoid threading-related crashes.

For more information about async/await support in the Swift SDK, refer
to Swift Concurrency: Async/Await APIs.

Actor Support

The Swift SDK supports actor-isolated realm instances. For more information,
refer to Use Realm with Actors - Swift SDK.

Apple Privacy Manifest

Version changed: 10.49.3
Build RealmSwift as a dynamic framework to include the Privacy Manifest.

Apple requires apps that use RealmSwift to provide a privacy manifest
containing details about the SDK's data collection and use practices. The
bundled manifest file must be included when submitting new apps or app updates
to the App Store. For more details about Apple's requirements, refer to
Upcoming third-party SDK requirements
on the Apple Developer website.

Starting in Swift SDK version 10.46.0, the SDK ships with privacy manifests
for Realm and RealmSwift. Each package contains its own privacy manifest
with Apple's required API disclosures and the reasons for using those APIs.

You can view the privacy manifests in each package, or in the realm-swift
GitHub repository:

To include these manifests in a build target that uses RealmSwift, you must
build RealmSwift as a dynamic framework. For details, refer to the Swift
Package Manager Installation instructions step
(Optional) Build RealmSwift as a Dynamic Framework.

The Swift SDK does not include analytics code in builds for the App Store.

You may need to add additional disclosures to your app's privacy manifest
detailing your data collection and use practices when using these APIs.

For more information, refer to Apple's
Privacy manifest files documentation.


Guides Logging (`docs/guides/logging.md`)

Logging - Swift SDK

You can set or change your app's log level when developing or debugging
your application. You might want to change the log level to log different
amounts of data depending on your development needs.

Set or Change the Realm Log Level

You can set the level of detail reported by the Realm Swift SDK. Set the
log level for the default logger with Logger.shared.level:

swift
Logger.shared.level = .trace

The RLMLogLevel enum represents the different levels of logging you can configure.

You can change the log level to increase or decrease verbosity at different
points in your code.

swift
// Set a log level that isn't too verbose
Logger.shared.level = .warn

// Later, when trying to debug something, change the log level for more verbosity
Logger.shared.level = .trace

Turn Off Logging

The default log threshold level for the Realm Swift SDK is .info. This
displays some information in the console. You can disable logging entirely
by setting the log level to .off:

swift
Logger.shared.level = .off

Customize the Logging Function

Initialize an instance of a Logger and define the function to use for logging.

swift
// Create an instance of `Logger` and define the log function to invoke.
let logger = Logger(level: .detail) { level, message in
    // You may pass log information to a logging service, or
    // you could simply print the logs for debugging. Define
    // the log function that makes sense for your application.
    print("REALM DEBUG: \(Date.now) \(level) \(message) \n")
}

Tip:
To diagnose and troubleshoot errors while developing your application, set the
log level to debug or trace. For production deployments, decrease the
log level for improved performance.

You can set a logger as the default logger for your app with Logger.shared.
After you set the default logger, you can change the log level during the app
lifecycle as needed.

swift
let logger = Logger(level: .info) { level, message in
    // You may pass log information to a logging service, or
    // you could simply print the logs for debugging. Define
    // the log function that makes sense for your application.
    print("REALM DEBUG: \(Date.now) \(level) \(message) \n")
}

// Set a logger as the default
Logger.shared = logger

// After setting a default logger, you can change
// the log level at any point during the app lifecycle
Logger.shared.level = .debug

Guides Quick Start (`docs/guides/quick-start.md`)

Quick Start - Swift SDK

This Quick Start demonstrates how to use Realm with the Realm Swift SDK.
Before you begin, ensure you have installed the Swift SDK.

Seealso:
If your app uses SwiftUI, check out the SwiftUI Quick Start.

Import Realm

Near the top of any Swift file that uses Realm, add the following import
statement:

swift
import RealmSwift

Define Your Object Model

For a local realm, you can define your object model directly in code.

swift
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
   }
}

Open a Realm

In a local realm, the simplest option to open a realm
is to use the default realm with no configuration parameter:

swift
// Open the default realm
let realm = try! Realm()

You can also specify a Realm.Configuration
parameter to open a realm at a specific file URL, in-memory, or with a
subset of classes.

For more information, see: Configure and Open a Realm.

Create, Read, Update, and Delete Objects

Once you have opened a realm, you can modify it and its objects
in a write transaction block.

To create a new Todo object, instantiate the Todo class and add it to the realm in a write block:

swift
let todo = Todo(name: "Do laundry", ownerId: user.id)
try! realm.write {
    realm.add(todo)
}

You can retrieve a live collection of all todos in the realm:

swift
// Get all todos in the realm
let todos = realm.objects(Todo.self)

You can also filter that collection using where:

swift
let todosInProgress = todos.where {
    $0.status == "InProgress"
}
print("A list of all todos in progress: \(todosInProgress)")

To modify a todo, update its properties in a write transaction block:

swift
// All modifications to a realm must happen in a write block.
let todoToUpdate = todos[0]
try! realm.write {
    todoToUpdate.status = "InProgress"
}

Finally, you can delete a todo:

swift
// All modifications to a realm must happen in a write block.
let todoToDelete = todos[0]
try! realm.write {
    // Delete the Todo.
    realm.delete(todoToDelete)
}

Watch for Changes

You can watch a realm, collection, or object for changes with the observe method.

swift
// Retain notificationToken as long as you want to observe
let notificationToken = todos.observe { (changes) in
    switch changes {
    case .initial: break
        // Results are now populated and can be accessed without blocking the UI
    case .update(_, let deletions, let insertions, let modifications):
        // Query results have changed.
        print("Deleted indices: ", deletions)
        print("Inserted indices: ", insertions)
        print("Modified modifications: ", modifications)
    case .error(let error):
        // An error occurred while opening the Realm file on the background worker thread
        fatalError("\(error)")
    }
}

Be sure to retain the notification token returned by observe as
long as you want to continue observing. When you are done observing,
invalidate the token to free the resources:

swift
// Invalidate notification tokens when done observing
notificationToken.invalidate()

Guides Swift Concurrency (`docs/guides/swift-concurrency.md`)

Swift Concurrency - Swift SDK

Swift's concurrency system provides built-in support for writing asynchronous
and parallel code in a structured way. For a detailed overview of the
Swift concurrency system, refer to the Swift Programming Language Concurrency
topic
.

While the considerations on this page broadly apply to using realm with
Swift concurrency features, Realm Swift SDK version 10.39.0 adds support
for using Realm with Swift Actors. You can use Realm isolated to a single
actor or use Realm across actors.

Realm's actor support simplifies using Realm in a MainActor and background actor
context, and supersedes much of the advice on this page regarding concurrency
considerations. For more information, refer to Use Realm with Actors - Swift SDK.

Realm Concurrency Caveats

As you implement concurrency features in your app, consider this caveat
about Realm's threading model and Swift concurrency threading behaviors.

Suspending Execution with Await

Anywhere you use the Swift keyword await marks a possible suspension
point in the execution of your code. With Swift 5.7, once your code suspends,
subsequent code might not execute on the same thread. This means that
anywhere you use await in your code, the subsequent code could be
executed on a different thread than the code that precedes or follows it.

This is inherently incompatible with Realm's live object paradigm. Live objects, collections, and realm instances are
thread-confined: that is, they are only valid on the thread on which
they were created. Practically speaking, this means you cannot pass live
instances to other threads. However, Realm offers several
mechanisms for sharing objects across threads. These mechanisms typically require
your code to do some explicit handling to safely pass data across threads.

You can use some of these mechanisms, such as frozen objects or the ThreadSafeReference, to safely use Realm objects and instances
across threads with the await keyword. You can also avoid
threading-related issues by marking any asynchronous Realm code with
@MainActor to ensure your apps always execute this code on the main
thread.

As a general rule, keep in mind that using Realm in an await context
without incorporating threading protection may yield inconsistent behavior.
Sometimes, the code may succeed. In other cases, it may throw an error
related to writing on an incorrect thread.

Perform Background Writes

A commonly-requested use case for asynchronous code is to perform write
operations in the background without blocking the main thread.

Realm has two APIs that allow for performing asynchronous writes:

  • The writeAsync()
    API allows for performing async writes using Swift completion handlers.
  • The asyncWrite()
    API allows for performing async writes using Swift async/await syntax.

Both of these APIs allow you to add, update, or delete objects in the
background without using frozen objects or passing a thread-safe reference.

With the writeAsync() API, waiting to obtain the write lock and
committing a transaction occur in the background. The write block itself
runs on the calling thread. This provides thread-safety without requiring
you to manually handle frozen objects or passing references across threads.

However, while the write block itself is executed, this does block new
transactions on the calling thread. This means that a large write using
the writeAsync() API could block small, quick writes while it executes.

The asyncWrite() API suspends the calling task while waiting for its
turn to write rather than blocking the thread. In addition, the actual
I/O to write data to disk is done by a background worker thread. For small
writes, using this function on the main thread may block the main thread
for less time than manually dispatching the write to a background thread.

For more information, including code examples, refer to: Perform a Background Write.

Tasks and TaskGroups

Swift concurrency provides APIs to manage Tasks
and TaskGroups. The Swift concurrency
documentation

defines a task as a unit of work that can be run asynchronously as part of
your program. Task allows you to specifically define a unit of asynchronous
work. TaskGroup lets you define a collection of Tasks to execute as a unit
under the parent TaskGroup.

Tasks and TaskGroups provide the ability to yield the thread to other
important work or to cancel a long-running task that could be blocking
other operations. To get these benefits, you might be tempted to use Tasks
and TaskGroups to manage realm writes in the background.

However, the thread-confined constraints described in Suspending
Execution with Await above apply in
the Task context. If your Task contains await points, subsequent code
might run or resume on a different thread and violate Realm's thread confinement.

You must annotate functions that you run in a Task context with @MainActor
to ensure code that accesses Realm only runs on the main thread. This negates
some of the benefits of using Tasks, and may mean this is not a good design
choice for apps that use Realm unless you are using Tasks solely for
networking activities like managing users.

Actor Isolation

Seealso:
The information in this section is applicable to Realm SDK versions earlier
than 10.39.0. Starting in Realm Swift SDK version 10.39.0 and newer,
the SDK supports using Realm with Swift Actors and related async functionality.

For more information, refer to Use Realm with Actors - Swift SDK.

Actor isolation provides the perception of confining Realm access to a
dedicated actor, and therefore seems like a safe way to manage Realm
access in an asynchronous context.

However, using Realm in a non-@MainActor async function is currently
not supported.

In Swift 5.6, this would often work by coincidence. Execution after an
await would continue on whatever thread the awaited thing ran on.
Using await Realm() in an async function would result in the code
following that running on the main thread until your next call to an
actor-isolated function.

Swift 5.7 instead hops threads whenever changing actor isolation contexts.
An unisolated async function always runs on a background thread instead.

If you have code which uses await Realm() and works in 5.6, marking
the function as @MainActor will make it work with Swift 5.7. It will
function how it did - unintentionally - in 5.6.

Most often, the error you see related to accessing Realm through concurrency
code is Realm accessed from incorrect thread. This is due to the
thread-isolation issues described on this page.

To avoid threading-related issues in code that uses Swift concurrency features:

  • Upgrade to a version of the Realm Swift SDK that supports actor-isolated realms,
    and use this as an alternative to manually managing threading. For more
    information, refer to Use Realm with Actors - Swift SDK.
  • Do not change execution contexts when accessing a realm. If you open a realm
    on the main thread to provide data for your UI, annotate subsequent functions
    where you access the realm asynchronously with @MainActor to ensure it
    always runs on the main thread. Remember that await marks a suspension
    point that could change to a different thread.
  • Apps that do not use actor-isolated realms can use the writeAsync API to
    perform a background write. This manages realm
    access in a thread-safe way without requiring you to write specialized code
    to do it yourself. This is a special API that outsources aspects of the
    write process - where it is safe to do so - to run in an async context.
    Unless you are writing to an actor-isolated realm, you do not use this
    method with Swift's async/await syntax. Use this method synchronously
    in your code. Alternately, you can use the asyncWrite API with Swift's
    async/await syntax when awaiting writes to asynchronous realms.
  • If you want to explicitly write concurrency code that is not actor-isolated
    where accessing a realm is done in a thread-safe way, you can explicitly
    pass instances across threads where
    applicable to avoid threading-related crashes. This does require a good
    understanding of Realm's threading model, as well as being mindful of
    Swift concurrency threading behaviors.

Sendable, Non-Sendable and Thread-Confined Types

The Realm Swift SDK public API contains types that fall into three broad
categories:

  • Sendable
  • Not Sendable and not thread confined
  • Thread-confined

You can share types that are not Sendable and not thread confined between
threads, but you must synchronize them.

Thread-confined types, unless frozen, are confined to an isolation context.
You cannot pass them between these contexts even with synchronization.


Guides Swiftui (`docs/guides/swiftui.md`)

SwiftUI - Swift SDK

Overview

The Realm Swift SDK offers features that integrate into SwiftUI.
This documentation provides an overview of those features.

Requirements

  • Xcode project using the SwiftUI "App" template. To use all of the Realm
    Swift SDK's SwiftUI features, the minimum iOS target is 15.0. Some features
    are compatible with older versions of iOS.
  • Install the Swift SDK. Use the most recent version of
    the Realm Swift SDK to get all of the features and enhancements for SwiftUI.

Guides Test And Debug (`docs/guides/test-and-debug.md`)

Test and Debug - Swift SDK

Testing

Test Using a Default Realm

The easiest way to use and test Realm-backed applications
is to use the default realm. To avoid overriding application data or
leaking state between tests, set the default realm to a new file for
each test.

swift
// A base class which each of your Realm-using tests should inherit from rather
// than directly from XCTestCase
class TestCaseBase: XCTestCase {
    override func setUp() {
        super.setUp()

        // Use an in-memory Realm identified by the name of the current test.
        // This ensures that each test can't accidentally access or modify the data
        // from other tests or the application itself, and because they're in-memory,
        // there's nothing that needs to be cleaned up.
        Realm.Configuration.defaultConfiguration.inMemoryIdentifier = self.name
    }
}

Injecting Realm Instances

Another way to test Realm-related code is to have all the
methods you'd like to test accept a realm instance as an argument. This
enables you to pass in different realms when running the app and when
testing it.

For example, suppose your app has a method to GET a user profile
from a JSON API. You want to test that the local profile is properly
created.

Simplify Testing with Class Projections

Version added: 10.21.0

If you want to work with a subset of an object's properties for testing,
you can create a class projection.
A class projection is a model abstraction where you can pass through, rename,
or exclude realm object properties. While this feature simplifies view
model implementation, it also simplifies testing with Realm.

Example:
This example uses the object models
and the class projection from the
Define and Use Class Projections page.

In this example, we create a realm object using the full object model.
Then, we view retrieve the object as a class projection, working with
only a subset of its properties.

With this class projection, we don't need to access or account for
properties that we don't need to test.

swift
func testWithProjection() {
    let realm = try! Realm()
    // Create a Realm object, populate it with values
    let jasonBourne = Person(value: ["firstName": "Jason",
                                                       "lastName": "Bourne",
                                                       "address": [
                                                        "city": "Zurich",
                                                        "country": "Switzerland"]])
    try! realm.write {
        realm.add(jasonBourne)
    }

    // Retrieve all class projections of the given type `PersonProjection`
    // and filter for the first class projection where the `firstName` property
    // value is "Jason"
    let person = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })!
    // Verify that we have the correct PersonProjection
    XCTAssert(person.firstName == "Jason")
    // See that `homeCity` exists as a projection property
    // Although it is not on the object model
    XCTAssert(person.homeCity == "Zurich")

    // Change a value on the class projection
    try! realm.write {
        person.firstName = "David"
    }

    // Verify that the projected property's value has changed
    XCTAssert(person.firstName == "David")
}

Test Targets

Don't link the Realm framework directly to your test target.
This can cause your tests to fail with an exception message "Object type
'YourObject' is not managed by the Realm." Unlinking Realm
from your test target should resolve this issue.

Compile your model class files in your application or framework targets;
don't add them to your unit test targets. Otherwise, those classes are
duplicated when testing, which can lead to difficult-to-debug issues.

Expose all the code that you need for testing to your unit test
targets. Use the public access modifier or @testable.

Since you're using Realm as a dynamic framework, you'll
need to make sure your unit test target can find Realm.
Add the parent path to RealmSwift.framework to your unit test's
"Framework Search Paths".

Debugging

Debug Using Realm Studio

Realm Studio enables you to open and edit local
realms. It supports Mac, Windows and Linux.

LLDB

Debugging apps using Realm's Swift API must be done through
the LLDB console.

Although the LLDB script allows inspecting the contents of your realm
variables in Xcode's UI, this doesn't yet work for Swift. Those
variables will show incorrect data. Instead, use LLDB's po
command to inspect the contents of data stored in a realm.

Troubleshooting

Resolve Build Issues

Some developers experience build issues after installing the Realm Swift SDK via
CocoaPods or Carthage. Common causes of these issues include:

  • Installation issues: Initial install failedUsing an unsupported version of the dependency manager
  • Build tool issues: Build tools have stale cachesUpdating build tool versions
  • Making changes to your project setup, such as: Adding a new targetSharing dependencies across targets

A fix that often clears these issues is to delete derived data
and clean the Xcode build folder.

Cocoapods

Reset the Cocoapods Integration State

Run these commands in the terminal, in the root of your project:

bash
pod cache clean Realm
pod cache clean RealmSwift
pod deintegrate || rm -rf Pods
pod install --repo-update --verbose
# Assumes the default DerivedData location:
rm -rf ~/Library/Developer/Xcode/DerivedData

Clean the Xcode Build Folder

With your project open in Xcode, go to the Product drop-down menu,
and select Clean Build Folder.

Carthage

Reset Carthage-managed Dependency State

Run these commands in the terminal, in the root of your project:

bash
rm -rf Carthage
# Assumes default DerivedData location:
rm -rf ~/Library/Developer/Xcode/DerivedData
carthage update

Clean the Xcode Build Folder

With your project open in Xcode, go to the Product drop-down menu,
and select Clean Build Folder.

Issues Opening Realm Before Loading the UI

You may open a realm and immediately see crashes with error messages
related to properties being optional or required. Issues with your
object model can cause
these types of crashes. These errors occur after you open a realm,
but before you get to the UI.

Realm has a "schema discovery" phase when a realm opens on the device.
At this time, Realm examines the schema for any objects that it manages.
You can specify that a given realm should manage only a subset
of objects in your
application.

If you see errors related to properties during schema discovery, these are
likely due to schema issues and not issues with data from a specific object.
For example, you may see schema discovery errors if you define a to-one
relationship as required
instead of optional.

To debug these crashes, check the schema you've defined.

You can tell these are schema discovery issues because they occur before
the UI loads. This means that no UI element is attempting to incorrectly
use a property, and there aren't any objects in memory that could have
bad data. If you get errors related to properties after the UI loads,
this is probably not due to invalid schema. Instead, those errors are
likely a result of incorrect, wrongly-typed or missing data.

No Properties are Defined for Model

The Realm Swift SDK uses the Swift language reflection feature to determine
the properties in your model at runtime. If you get a crash similar to
the following, confirm that your project has not disabled reflection metadata:

shell
Terminating app due to uncaught exception 'RLMException', reason: 'No properties are defined for 'ObjectName'.

If you set SWIFT_REFLECTION_METADATA_LEVEL = none, Realm cannot
discover children of types, such as properties and enums. Reflection is
enabled by default if your project does not specifically set a level for
this setting.

Bad Alloc/Not Enough Memory Available

In iOS or iPad devices with little available memory, or where you have a
memory-intensive application that uses multiple realms or many notifications,
you may encounter the following error:

console
libc++abi: terminating due to an uncaught exception of type std::bad_alloc: std::bad_alloc

This error typically indicates that a resource cannot be allocated because
not enough memory is available.

If you are building for iOS 15+ or iPad 15+, you can add the
Extended Virtual Addressing Entitlement
to resolve this issue.

Add these keys to your Property List, and set the values to true:

xml
<key>com.apple.developer.kernel.extended-virtual-addressing</key>
<true/>
<key>com.apple.developer.kernel.increased-memory-limit</key>
<true/>

Swift Package Target Cannot be Built Dynamically

Version changed: 10.49.3

Swift SDK v10.49.3 changed the details for installing the package with
Swift Package Manager (SPM). When you update from an older version of the
package to v10.49.3 or newer, you may get a build error similar to:

console
Swift package target `Realm` is linked as a static library by `TargetName`
and `Realm`, but cannot be built dynamically because there is a package
product with the same name.

To resolve this error, unlink either the Realm or the RealmSwift
package from your build target. You can do this in Xcode by following these
steps:

  1. In your project Targets, select your build target.
  2. Go to the Build Phases tab.
  3. Expand the Link Binary With Libraries element.
  4. Select either Realm or RealmSwift, and click the Remove items
    (-) button to remove the unneeded binary. If you use Swift or Swift
    and Objective-C APIs, keep RealmSwift. If you use only Objective-C
    APIs, keep Realm.

Now your target should build without this error.


Guides Use Realm With Actors (`docs/guides/use-realm-with-actors.md`)

Use Realm with Actors - Swift SDK

Starting with Realm Swift SDK version 10.39.0, Realm supports built-in
functionality for using Realm with Swift Actors. Realm's actor support
provides an alternative to managing threads or dispatch queues to perform
asynchronous work. You can use Realm with actors in a few different ways:

  • Work with realm only on a specific actor with an actor-isolated realm
  • Use Realm across actors based on the needs of your application

You might want to use an actor-isolated realm if you want to restrict all
realm access to a single actor. This negates the need to pass data across
the actor boundary, and can simplify data race debugging.

You might want to use realms across actors in cases where you want to
perform different types of work on different actors. For example, you might
want to read objects on the MainActor but use a background actor for large
writes.

For general information about Swift actors, refer to Apple's Actor
documentation
.

Prerequisites

To use Realm in a Swift actor, your project must:

  • Use Realm Swift SDK version 10.39.0 or later
  • Use Swift 5.8/Xcode 14.3

In addition, we strongly recommend enabling these settings in your project:

  • SWIFT_STRICT_CONCURRENCY=complete: enables strict concurrency checking
  • OTHER_SWIFT_FLAGS=-Xfrontend-enable-actor-data-race-checks: enables
    runtime actor data-race detection

About the Examples on This Page

The examples on this page use the following model:

swift
class Todo: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var name: String
    @Persisted var owner: String
    @Persisted var status: String
}

Open an Actor-Isolated Realm

You can use the Swift async/await syntax to await opening a realm.

Initializing a realm with try await Realm() opens a MainActor-isolated
realm. Alternately, you can explicitly specify an actor when opening a
realm with the await syntax.

swift
@MainActor
func mainThreadFunction() async throws {
    // These are identical: the async init produces a
    // MainActor-isolated Realm if no actor is supplied
    let realm1 = try await Realm()
    let realm2 = try await Realm(actor: MainActor.shared)

    try await useTheRealm(realm: realm1)
}

You can specify a default configuration or customize your configuration when
opening an actor-isolated realm:

swift
@MainActor
func mainThreadFunction() async throws {
    let username = "Galadriel"

    // Customize the default realm config
    var config = Realm.Configuration.defaultConfiguration
    config.fileURL!.deleteLastPathComponent()
    config.fileURL!.appendPathComponent(username)
    config.fileURL!.appendPathExtension("realm")

    // Open an actor-isolated realm with a specific configuration
    let realm = try await Realm(configuration: config, actor: MainActor.shared)

    try await useTheRealm(realm: realm)
}

For more general information about configuring a realm, refer to
Configure & Open a Realm.

Define a Custom Realm Actor

You can define a specific actor to manage Realm in asynchronous contexts.
You can use this actor to manage realm access and perform write operations.

text
/* Detailed source-code truncated for AI context efficiency. */

An actor-isolated realm may be used with either local or global actors.

swift
// A simple example of a custom global actor
@globalActor actor BackgroundActor: GlobalActor {
    static var shared = BackgroundActor()
}

@BackgroundActor
func backgroundThreadFunction() async throws {
    // Explicitly specifying the actor is required for anything that is not MainActor
    let realm = try await Realm(actor: BackgroundActor.shared)
    try await realm.asyncWrite {
        _ = realm.create(Todo.self, value: [
            "name": "Pledge fealty and service to Gondor",
            "owner": "Pippin",
            "status": "In Progress"
        ])
    }
    // Thread-confined Realms would sometimes throw an exception here, as we
    // may end up on a different thread after an `await`
    let todoCount = realm.objects(Todo.self).count
    print("The number of Realm objects is: \(todoCount)")
}

@MainActor
func mainThreadFunction() async throws {
    try await backgroundThreadFunction()
}

Use a Realm Actor Synchronously in an Isolated Function

When a function is confined to a specific actor, you can use the actor-isolated
realm synchronously.

swift
func createObject(in actor: isolated RealmActor) async throws {
    // Because this function is isolated to this actor, you can use
    // realm synchronously in this context without async/await keywords
    try actor.realm.write {
        actor.realm.create(Todo.self, value: [
            "name": "Keep it secret",
            "owner": "Frodo",
            "status": "In Progress"
        ])
    }
    let taskCount = actor.count
    print("The actor currently has \(taskCount) tasks")
}

let actor = try await RealmActor()

try await createObject(in: actor)

Use a Realm Actor in Async Functions

When a function isn't confined to a specific actor, you can use your Realm actor
with Swift's async/await syntax.

swift
func createObject() async throws {
    // Because this function is not isolated to this actor,
    // you must await operations completed on the actor
    try await actor.createTodo(name: "Take the ring to Mount Doom", owner: "Frodo", status: "In Progress")
    let taskCount = await actor.count
    print("The actor currently has \(taskCount) tasks")
}

let actor = try await RealmActor()

try await createObject()

Write to an Actor-Isolated Realm

Actor-isolated realms can use Swift async/await syntax for asynchronous
writes. Using try await realm.asyncWrite { ... } suspends the current task,
acquires the write lock without blocking the current thread, and then invokes
the block. Realm writes the data to disk on a background thread and resumes
the task when that completes.

This function from the example RealmActor defined above shows how you might
write to an actor-isolated realm:

swift
func createTodo(name: String, owner: String, status: String) async throws {
    try await realm.asyncWrite {
        realm.create(Todo.self, value: [
            "_id": ObjectId.generate(),
            "name": name,
            "owner": owner,
            "status": status
        ])
    }
}

And you might perform this write using Swift's async syntax:

swift
func createObject() async throws {
    // Because this function is not isolated to this actor,
    // you must await operations completed on the actor
    try await actor.createTodo(name: "Take the ring to Mount Doom", owner: "Frodo", status: "In Progress")
    let taskCount = await actor.count
    print("The actor currently has \(taskCount) tasks")
}

let actor = try await RealmActor()

try await createObject()

This does not block the calling thread while waiting to write. It does
not perform I/O on the calling thread. For small writes, this is safe to
use from @MainActor functions without blocking the UI. Writes that
negatively impact your app's performance due to complexity and/or platform
resource constraints may still benefit from being done on a background thread.

Asynchronous writes are only supported for actor-isolated Realms or in
@MainActor functions.

Pass Realm Data Across the Actor Boundary

Realm objects are not Sendable,
and cannot cross the actor boundary directly. To pass Realm data across
the actor boundary, you have two options:

  • Pass a ThreadSafeReference to or from the actor
  • Pass other types that are Sendable, such as passing values directly
    or by creating structs to pass across actor boundaries

Pass a ThreadSafeReference

You can create a ThreadSafeReference on an
actor where you have access to the object. In this case, we create a
ThreadSafeReference on the MainActor. Then, pass the ThreadSafeReference to the destination actor.

swift
// We can pass a thread-safe reference to an object to update it on a different actor.
let todo = todoCollection.where {
    $0.name == "Arrive safely in Bree"
}.first!
let threadSafeReferenceToTodo = ThreadSafeReference(to: todo)
try await backgroundActor.deleteTodo(tsrToTodo: threadSafeReferenceToTodo)

On the destination actor, you must resolve() the reference within a
write transaction before you can use it. This retrieves a version of the
object local to that actor.

swift
actor BackgroundActor {
    public func deleteTodo(tsrToTodo tsr: ThreadSafeReference<Todo>) throws {
        let realm = try! Realm()
        try realm.write {
            // Resolve the thread safe reference on the Actor where you want to use it.
            // Then, do something with the object.
            let todoOnActor = realm.resolve(tsr)
            realm.delete(todoOnActor!)
        }
    }
}

Important:
You must resolve a ThreadSafeReference exactly once. Otherwise,
the source realm remains pinned until the reference gets
deallocated. For this reason, ThreadSafeReference should be
short-lived.

If you may need to share the same realm object across actors more than
once, you may prefer to share the primary key
and query for it on
the actor where you want to use it. Refer to the "Pass a Primary Key
and Query for the Object on Another Actor" section on this page for an example.

Pass a Sendable Type

While Realm objects are not Sendable, you can work around this by passing
Sendable types across actor boundaries. You can use a few strategies to
pass Sendable types and work with data across actor boundaries:

  • Pass Sendable Realm types or primitive values instead of complete Realm objects
  • Pass an object's primary key and query for the object on another actor
  • Create a Sendable representation of your Realm object, such as a struct

Pass Sendable Realm Types and Primitive Values

If you only need a piece of information from the Realm object, such as a
String or Int, you can pass the value directly across actors instead
of passing the Realm object. For a full list of which Realm types are Sendable,
refer to Sendable, Non-Sendable and Thread-Confined Types.

swift
@MainActor
func mainThreadFunction() async throws {
    // Create an object in an actor-isolated realm.
    // Pass primitive data to the actor instead of
    // creating the object here and passing the object.
    let actor = try await RealmActor()
    try await actor.createTodo(name: "Prepare fireworks for birthday party", owner: "Gandalf", status: "In Progress")

    // Later, get information off the actor-confined realm
    let todoOwner = await actor.getTodoOwner(forTodoNamed: "Prepare fireworks for birthday party")
}

Pass a Primary Key and Query for the Object on Another Actor

If you want to use a Realm object on another actor, you can share the
primary key and
query for it on the actor
where you want to use it.

swift
// Execute code on a specific actor - in this case, the @MainActor
@MainActor
func mainThreadFunction() async throws {
    // Create an object off the main actor
    func createObject(in actor: isolated BackgroundActor) async throws -> ObjectId {
        let realm = try await Realm(actor: actor)
        let newTodo = try await realm.asyncWrite {
            return realm.create(Todo.self, value: [
                "name": "Pledge fealty and service to Gondor",
                "owner": "Pippin",
                "status": "In Progress"
            ])
        }

        // Share the todo's primary key so we can easily query for it on another actor
        return newTodo._id
    }

    // Initialize an actor where you want to perform background work
    let actor = BackgroundActor()
    let newTodoId = try await createObject(in: actor)
    let realm = try await Realm()
    let todoOnMainActor = realm.object(ofType: Todo.self, forPrimaryKey: newTodoId)
}

Create a Sendable Representation of Your Object

If you need to work with more than a simple value, but don't want the
overhead of passing around ThreadSafeReferences or querying objects on
different actors, you can create a struct or other Sendable representation
of your data to pass across the actor boundary.

For example, your actor might have a function that creates a struct
representation of the Realm object.

swift
struct TodoStruct {
    var id: ObjectId
    var name, owner, status: String
}

func getTodoAsStruct(forTodoNamed name: String) -> TodoStruct {
    let todo = realm.objects(Todo.self).where {
        $0.name == name
    }.first!
    return TodoStruct(id: todo._id, name: todo.name, owner: todo.owner, status: todo.status)
}

Then, you can call a function to get the data as a struct on another actor.

swift
@MainActor
func mainThreadFunction() async throws {
    // Create an object in an actor-isolated realm.
    let actor = try await RealmActor()
    try await actor.createTodo(name: "Leave the ring on the mantle", owner: "Bilbo", status: "In Progress")

    // Get information as a struct or other Sendable type.
    let todoAsStruct = await actor.getTodoAsStruct(forTodoNamed: "Leave the ring on the mantle")
}

Observe Notifications on a Different Actor

You can observe notifications on an actor-isolated realm using Swift's
async/await syntax.

Calling await object.observe(on: Actor) or
await collection.observe(on: Actor) registers a block to be called
each time the object or collection changes.

The SDK asynchronously calls the block on the given actor's executor.

For write transactions performed on different threads or in different
processes, the SDK calls the block when the realm is (auto)refreshed
to a version including the changes. For local writes, the SDK calls the block
at some point in the future after the write transaction is committed.

Like other Realm notifications, you can
only observe objects or collections managed by a realm. You must retain the
returned token for as long as you want to watch for updates.

If you need to manually advance the state of an observed realm on the main
thread or on another actor, call await realm.asyncRefresh().
This updates the realm and outstanding objects managed by the Realm to point to
the most recent data and deliver any applicable notifications.

Observation Limitations

You cannot call the .observe() method:

  • During a write transaction
  • When the containing realm is read-only
  • On an actor-confined realm from outside the actor

Register a Collection Change Listener

The SDK calls a collection notification block after each write transaction which:

  • Deletes an object from the collection.
  • Inserts an object into the collection.
  • Modifies any of the managed properties of an object in the collection. This
    includes self-assignments that set a property to its existing value.

Important:
In collection notification handlers, always apply changes
in the following order: deletions, insertions, then
modifications. Handling insertions before deletions may
result in unexpected behavior.

These notifications provide information about the actor on which the change
occurred. Like non-actor-isolated collection notifications, they also provide
a change parameter that reports which objects are deleted, added, or
modified during the write transaction. This RealmCollectionChange
resolves to an array of index paths that you can pass to a UITableView's
batch update methods.

text
/* Detailed source-code truncated for AI context efficiency. */

Register an Object Change Listener

The SDK calls an object notification block after each write transaction which:

  • Deletes the object.
  • Modifies any of the managed properties of the object. This includes
    self-assignments that set a property to its existing value.

The block is passed a copy of the object isolated to the requested actor,
along with information about what changed. This object can be safely used
on that actor.

By default, only direct changes to the object's properties produce notifications.
Changes to linked objects do not produce notifications. If a non-nil, non-empty
keypath array is passed in, only changes to the properties identified by those
keypaths produce change notifications. The keypaths may traverse link
properties to receive information about changes to linked objects.

text
/* Detailed source-code truncated for AI context efficiency. */

Guides Xcode Playgrounds (`docs/guides/xcode-playgrounds.md`)

Use Realm in Xcode Playgrounds

Prerequisites

You can only use Swift packages within Xcode projects that have at least one
scheme and target. To use Realm in Xcode Playgrounds, you must first have
an Xcode project where you have Installed the Swift SDK.

Create a Playground

Within a project, go to File > New > Playground. Select the type of
Playground you want. For this example, we've used a Blank iOS
Playground.

Name and save the playground in the root of your
project. Be sure to add it to the project.

You should see your new Playground in your Project navigator.

Import Realm

Add the following import statement to use Realm in the playground:

swift
import RealmSwift

Experiment with Realm

Experiment with Realm. For this example, we'll:

  • Define a new Realm object type
  • Create a new object of that type and write it to realm
  • Query objects of the type, and filter them
swift
class Drink: Object {
    @Persisted var name = ""
    @Persisted var rating = 0
    @Persisted var source = ""
    @Persisted var drinkType = ""
}

let drink = Drink(value: ["name": "Los Cabellos", "rating": 10, "source": "AeroPress", "drinkType": "Coffee"])

let realm = try! Realm(configuration: config)

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

let drinks = realm.objects(Drink.self)

let coffeeDrinks = drinks.where {
    $0.drinkType == "Coffee"
}

print(coffeeDrinks.first?.name)

Managing the Realm File in Your Playground

When you work with a default realm
in a Playground, you might run into a situation where you need to delete the
realm. For example, if you are experimenting with an object type and add
properties to the object, you may get an error that you must migrate the
realm.

You can specify Realm.configuration details to open the file at a specific
path, and delete the realm if it exists at the path.

swift
var config = Realm.Configuration()

config.fileURL!.deleteLastPathComponent()
config.fileURL!.appendPathComponent("playgroundRealm")
config.fileURL!.appendPathExtension("realm")

if Realm.fileExists(for: config) {
    try Realm.deleteFiles(for: config)
    print("Successfully deleted existing realm at path: \(config.fileURL!)")
} else {
    print("No file currently exists at path")
}

Alternately, you can open the realm in-memory only, or use the
deleteRealmIfMigrationNeeded
method to automatically delete a realm when migration is needed.


Guides Crud Create (`docs/guides/crud/create.md`)

CRUD - Create - Swift SDK

Create a New Object

About The Examples On This Page

The examples on this page use the following models:

Objective-C

objectivec
// DogToy.h
@interface DogToy : RLMObject
@property NSString *name;
@end

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@property int age;
@property NSString *color;

// To-one relationship
@property DogToy *favoriteToy;

@end

// Enable Dog for use in RLMArray
RLM_COLLECTION_TYPE(Dog)

// Person.h
// A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs.
@interface Person : RLMObject
@property int _id;
@property NSString *name;

// To-many relationship - a person can have many dogs
@property RLMArray<Dog *><Dog> *dogs;

// Inverse relationship - a person can be a member of many clubs
@property (readonly) RLMLinkingObjects *clubs;
@end

RLM_COLLECTION_TYPE(Person)

// DogClub.h
@interface DogClub : RLMObject
@property NSString *name;
@property RLMArray<Person *><Person> *members;
@end

// Dog.m
@implementation Dog
@end

// DogToy.m
@implementation DogToy
@end

// Person.m
@implementation Person
// Define the primary key for the class
+ (NSString *)primaryKey {
    return @"_id";
}

// Define the inverse relationship to dog clubs
+ (NSDictionary *)linkingObjectsProperties {
    return @{
        @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"],
    };
}
@end

// DogClub.m
@implementation DogClub
@end

Swift

swift
class DogToy: Object {
    @Persisted var name = ""
}

class Dog: Object {
    @Persisted var name = ""
    @Persisted var age = 0
    @Persisted var color = ""
    @Persisted var currentCity = ""
    @Persisted var citiesVisited: MutableSet<String>
    @Persisted var companion: AnyRealmValue

    // To-one relationship
    @Persisted var favoriteToy: DogToy?

    // Map of city name -> favorite park in that city
    @Persisted var favoriteParksByCity: Map<String, String>
}

class Person: Object {
    @Persisted(primaryKey: true) var id = 0
    @Persisted var name = ""

    // To-many relationship - a person can have many dogs
    @Persisted var dogs: List<Dog>

    // Embed a single object.
    // Embedded object properties must be marked optional.
    @Persisted var address: Address?

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

class Address: EmbeddedObject {
    @Persisted var street: String?
    @Persisted var city: String?
    @Persisted var country: String?
    @Persisted var postalCode: String?
}

Create an Object

Objective-C

To add an object to a realm, instantiate it as you would any other
object and then pass it to -[RLMRealm addObject:] inside
of a write transaction.

objectivec
// Get the default realm.
// You only need to do this once per thread.
RLMRealm *realm = [RLMRealm defaultRealm];

// Instantiate the class.
Dog *dog = [[Dog alloc] init];
dog.name = @"Max";
dog.age = 5;

// Open a thread-safe transaction.
[realm transactionWithBlock:^() {
    // Add the instance to the realm.
    [realm addObject:dog];
}];

Swift

To add an object to a realm, instantiate it as you would any other
object and then pass it to Realm.add(_:update:)
inside of a write transaction.

swift
// Instantiate the class and set its values.
let dog = Dog()
dog.name = "Rex"
dog.age = 10

// Get the default realm. You only need to do this once per thread.
let realm = try! Realm()
// Open a thread-safe transaction.
try! realm.write {
    // Add the instance to the realm.
    realm.add(dog)
}

Initialize Objects with a Value

You can initialize an object by passing an initializer value to
Object.init(value:).
The initializer value can be a key-value coding
compliant object, a dictionary, or an array containing one element for
each managed property.

Note:
When using an array as an initializer value, you must include all
properties in the same order as they are defined in the model.

Objective-C

objectivec
// (1) Create a Dog object from a dictionary
Dog *myDog = [[Dog alloc] initWithValue:@{@"name" : @"Pluto", @"age" : @3}];

// (2) Create a Dog object from an array
Dog *myOtherDog = [[Dog alloc] initWithValue:@[@"Pluto", @3]];

RLMRealm *realm = [RLMRealm defaultRealm];

// Add to the realm with transaction
[realm transactionWithBlock:^() {
    [realm addObject:myDog];
    [realm addObject:myOtherDog];
}];

Swift

swift
// (1) Create a Dog object from a dictionary
let myDog = Dog(value: ["name": "Pluto", "age": 3])

// (2) Create a Dog object from an array
let myOtherDog = Dog(value: ["Fido", 5])

let realm = try! Realm()
// Add to the realm inside a transaction
try! realm.write {
    realm.add([myDog, myOtherDog])
}

You can even initialize related or
embedded objects by nesting initializer
values:

Objective-C

objectivec
// Instead of using pre-existing dogs...
Person *aPerson = [[Person alloc]
    initWithValue:@[@123, @"Jane", @[aDog, anotherDog]]];

// ...we can create them inline
Person *anotherPerson = [[Person alloc]
    initWithValue:@[@123, @"Jane", @[@[@"Buster", @5], @[@"Buddy", @6]]]];

Swift

swift
// Instead of using pre-existing dogs...
let aPerson = Person(value: [123, "Jane", [aDog, anotherDog]])

// ...we can create them inline
let anotherPerson = Person(value: [123, "Jane", [["Buster", 5], ["Buddy", 6]]])

Some Property Types are Only Mutable in a Write Transaction

Some property types are only mutable in a write transaction. For example,
you can instantiate an object with a MutableSet
property, but you can only set that property's value in a write transaction.
You cannot initialize the object with a value for that property unless
you do so inside a write transaction.

Create an Object with JSON

Realm does not directly support JSON, but you can use
JSONSerialization.jsonObject(with:options:) to
convert JSON into a value that you can pass to
Realm.create(_:value:update:).

Objective-C

objectivec
// Specify a dog toy in JSON
NSData *data = [@"{\"name\": \"Tennis ball\"}" dataUsingEncoding: NSUTF8StringEncoding];
RLMRealm *realm = [RLMRealm defaultRealm];

// Insert from NSData containing JSON
[realm transactionWithBlock:^{
    id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
    [DogToy createInRealm:realm withValue:json];
}];

Swift

swift
// Specify a dog toy in JSON
let data = "{\"name\": \"Tennis ball\"}".data(using: .utf8)!
let realm = try! Realm()
// Insert from data containing JSON
try! realm.write {
    let json = try! JSONSerialization.jsonObject(with: data, options: [])
    realm.create(DogToy.self, value: json)
}

Nested objects or arrays in the JSON map to to-one or to-many relationships.

The JSON property names and types must match the destination
object schema exactly. For example:

  • float properties must be initialized with float-backed NSNumbers.
  • Date and Data properties cannot be inferred from strings. Convert them to the appropriate type before passing to Realm.create(_:value:update:).
  • Required properties cannot be null or missing in the JSON.

Realm ignores any properties in the JSON not defined in the
object schema.

Tip:
If your JSON schema doesn't exactly align with your Realm objects,
consider using a third-party framework to transform your JSON. There
are many model mapping frameworks that work with Realm.
See a partial list in the realm-swift repository.

Create an Embedded Object

To create an embedded object, assign an instance of the embedded object
to a parent object's property:

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    Address *address = [[Address alloc] init];
    address.street = @"123 Fake St.";
    address.city = @"Springfield";
    address.country = @"USA";
    address.postalCode = @"90710";

    Contact *contact = [Contact contactWithName:@"Nick Riviera"];

    // Assign the embedded object property
    contact.address = address;

    [realm addObject:contact];

    NSLog(@"Added contact: %@", contact);
}];

Swift

swift
// Open the default realm
let realm = try! Realm()

try! realm.write {
    let address = Address()
    address.street = "123 Fake St"
    address.city = "Springfield"
    address.country = "USA"
    address.postalCode = "90710"
    let contact = Person(name: "Nick Riviera", address: address)
    realm.add(contact)
}

Create an Object with a Map Property

When you create an object that has a map property, you can set the values for keys in a few ways:

  • Set keys and values on the object and then add the object to the realm
  • Set the object's keys and values directly inside a write transaction
  • Use key-value coding to set or update keys and values inside a write transaction
swift
let realm = try! Realm()
// Record a dog's name and current city
let dog = Dog()
dog.name = "Wolfie"
dog.currentCity = "New York"
// Set map values
dog.favoriteParksByCity["New York"] = "Domino Park"
// Store the data in a realm
try! realm.write {
    realm.add(dog)
    // You can also set map values inside a write transaction
    dog.favoriteParksByCity["Chicago"] = "Wrigley Field"
    dog.favoriteParksByCity.setValue("Bush Park", forKey: "Ottawa")
}

Realm disallows the use of . or $ characters in map keys.
You can use percent encoding and decoding to store a map key that contains
one of these disallowed characters.

text
// Percent encode . or $ characters to use them in map keys
let mapKey = "New York.Brooklyn"
let encodedMapKey = "New York%2EBrooklyn"

Create an Object with a MutableSet Property

You can create objects that contain MutableSet properties as you would any Realm object, but you
can only mutate a MutableSet within a write transaction. This means you can
only set the value(s) of a mutable set property within a write transaction.

swift
let realm = try! Realm()

// Record a dog's name and current city
let dog = Dog()
dog.name = "Maui"
dog.currentCity = "New York"

// Store the data in a realm. Add the dog's current city
// to the citiesVisited MutableSet
try! realm.write {
    realm.add(dog)
    // You can only mutate the MutableSet in a write transaction.
    // This means you can't set values at initialization, but must do it during a write.
    dog.citiesVisited.insert(dog.currentCity)
}

// You can also add multiple items to the set.
try! realm.write {
    dog.citiesVisited.insert(objectsIn: ["Boston", "Chicago"])
}

print("\(dog.name) has visited: \(dog.citiesVisited)")

Create an Object with an AnyRealmValue Property

When you create an object with an AnyRealmValue property, you must specify the type of the value you store in
the property. The Realm Swift SDK provides an AnyRealmValue enum that iterates through all of the types the
AnyRealmValue can store.

Later, when you read an AnyRealmValue,
you must check the type before you do anything with the value.

swift
// Create a Dog object and then set its properties
let myDog = Dog()
myDog.name = "Rex"
// This dog has no companion.
// You can set the field's type to "none", which represents `nil`
myDog.companion = .none

// Create another Dog whose companion is a cat.
// We don't have a Cat object, so we'll use a string to describe the companion.
let theirDog = Dog()
theirDog.name = "Wolfie"
theirDog.companion = .string("Fluffy the Cat")

// Another dog might have a dog as a companion.
// We do have an object that can represent that, so we can specify the
// type is a Dog object, and even set the object's value.
let anotherDog = Dog()
anotherDog.name = "Fido"
// Note: this sets Spot as a companion of Fido, but does not set
// Fido as a companion of Spot. Spot has no companion in this instance.
anotherDog.companion = .object(Dog(value: ["name": "Spot"]))

// Add the dogs to the realm
let realm = try! Realm()
try! realm.write {
    realm.add([myDog, theirDog, anotherDog])
}
// After adding these dogs to the realm, we now have 4 dog objects.
let dogs = realm.objects(Dog.self)
XCTAssertEqual(dogs.count, 4)

Create an Object Asynchronously

You can use Swift concurrency features to write asynchronously to an
actor-isolated realm.

This function from the example RealmActor defined on the
Use Realm with Actors page shows how you might
write to an actor-isolated realm:

swift
func createTodo(name: String, owner: String, status: String) async throws {
    try await realm.asyncWrite {
        realm.create(Todo.self, value: [
            "_id": ObjectId.generate(),
            "name": name,
            "owner": owner,
            "status": status
        ])
    }
}

And you might perform this write using Swift's async syntax:

swift
func createObject() async throws {
    // Because this function is not isolated to this actor,
    // you must await operations completed on the actor
    try await actor.createTodo(name: "Take the ring to Mount Doom", owner: "Frodo", status: "In Progress")
    let taskCount = await actor.count
    print("The actor currently has \(taskCount) tasks")
}

let actor = try await RealmActor()

try await createObject()

This operation does not block or perform I/O on the calling thread. For
more information about writing to realm using Swift concurrency features,
refer to Use Realm with Actors - Swift SDK.

Copy an Object to Another Realm

Objective-C

To copy an object from one realm to another, pass the original
object to +[RLMObject createInRealm:withValue:]:

objectivec
RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
configuration.inMemoryIdentifier = @"first realm";
RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];

[realm transactionWithBlock:^{
    Dog *dog = [[Dog alloc] init];
    dog.name = @"Wolfie";
    dog.age = 1;
    [realm addObject:dog];
}];

// Later, fetch the instance we want to copy
Dog *wolfie = [[Dog objectsInRealm:realm where:@"name == 'Wolfie'"] firstObject];

// Open the other realm
RLMRealmConfiguration *otherConfiguration = [RLMRealmConfiguration defaultConfiguration];
otherConfiguration.inMemoryIdentifier = @"second realm";
RLMRealm *otherRealm = [RLMRealm realmWithConfiguration:otherConfiguration error:nil];
[otherRealm transactionWithBlock:^{
    // Copy to the other realm
    Dog *wolfieCopy = [[wolfie class] createInRealm:otherRealm withValue:wolfie];
    wolfieCopy.age = 2;

    // Verify that the copy is separate from the original
    XCTAssertNotEqual(wolfie.age, wolfieCopy.age);
}];

Swift

To copy an object from one realm to another, pass the original
object to Realm.create(_:value:update:)::

swift
let realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "first realm"))

try! realm.write {
    let dog = Dog()
    dog.name = "Wolfie"
    dog.age = 1
    realm.add(dog)
}

// Later, fetch the instance we want to copy
let wolfie = realm.objects(Dog.self).first(where: { $0.name == "Wolfie" })!

// Open the other realm
let otherRealm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "second realm"))
try! otherRealm.write {
    // Copy to the other realm
    let wolfieCopy = otherRealm.create(type(of: wolfie), value: wolfie)
    wolfieCopy.age = 2

    // Verify that the copy is separate from the original
    XCTAssertNotEqual(wolfie.age, wolfieCopy.age)
}

Important:
The create methods do not support handling cyclical object
graphs. Do not pass in an object containing relationships involving
objects that refer back to their parents, either directly or
indirectly.


Guides Crud Crud (`docs/guides/crud/crud.md`)

CRUD - Swift SDK

Write Transactions

Realm uses a highly efficient storage engine
to persist objects. You can create objects in a realm,
update objects in a realm, and eventually delete
objects from a realm. Because these operations modify the
state of the realm, we call them writes.

Realm handles writes in terms of transactions. A
transaction is a list of read and write operations that
Realm treats as a single indivisible operation. In other
words, a transaction is all or nothing: either all of the
operations in the transaction succeed or none of the
operations in the transaction take effect.

All writes must happen in a transaction.

A realm allows only one open transaction at a time. Realm
blocks other writes on other threads until the open
transaction is complete. Consequently, there is no race
condition when reading values from the realm within a
transaction.

When you are done with your transaction, Realm either
commits it or cancels it:

  • When Realm commits a transaction, Realm writes
    all changes to disk.
  • When Realm cancels a write transaction or an operation in
    the transaction causes an error, all changes are discarded
    (or "rolled back").

Run a Transaction

The Swift SDK represents each transaction as a callback function
that contains zero or more read and write operations. To run
a transaction, define a transaction callback and pass it to
the realm's write method. Within this callback, you are
free to create, read, update, and delete on the realm. If
the code in the callback throws an exception when Realm runs
it, Realm cancels the transaction. Otherwise, Realm commits
the transaction immediately after the callback.

Important:
Since transactions block each other, it is best to avoid
opening transactions on both the UI thread and a
background thread. If a background transaction
blocks your UI thread's transaction, your app may appear
unresponsive.

Example:
The following code shows how to run a transaction with
the realm's write method. If the code in the callback
throws an exception, Realm cancels the transaction.
Otherwise, Realm commits the transaction.

Objective-C

objectivec
// Open the default realm.
RLMRealm *realm = [RLMRealm defaultRealm];

// Open a thread-safe transaction.
[realm transactionWithBlock:^() {
    // ... Make changes ...
    // Realm automatically cancels the transaction in case of exception.
    // Otherwise, Realm automatically commits the transaction at the
    // end of the code block.
}];

Swift

swift
// Open the default realm.
let realm = try! Realm()

// Prepare to handle exceptions.
do {
    // Open a thread-safe transaction.
    try realm.write {
        // Make any writes within this code block.
        // Realm automatically cancels the transaction
        // if this code throws an exception. Otherwise,
        // Realm automatically commits the transaction
        // after the end of this code block.
    }
} catch let error as NSError {
    // Failed to write to realm.
    // ... Handle error ...
}

Interface-Driven Writes

Realm always delivers notifications asynchronously, so they
never block the UI thread. However, there are situations when the UI
must reflect changes instantly. If you update the UI directly at the
same time as the write, the eventual notification could double that
update. This could lead to your app crashing due to inconsistent state
between the UI and the backing data store. To avoid this, you can write
without sending a notification to a specific handler. We call this type
of transaction an interface-driven write.

Example:
Say we decide to manage a table view's data source manually, because
our app design requires an instantaneous response to UI-driven table
updates. As soon as a user adds an item to the table view, we insert
it to our data source, which writes to the realm but also
immediately kicks off the animation. However, when Realm
delivers the change notification for this insertion a little later,
it indicates that an object has been added. But we already updated
the table view for it! Rather than writing complicated code to handle
this case, we can use interface-driven writes to prevent a specific
notification handler from firing for that specific write.

Interface-driven writes, also known as silent writes, are especially
useful when using fine-grained collection notifications. While you use
interface-driven writes for the current user's updates and update the UI
immediately, the sync process can use standard notifications to update
the UI.


Guides Crud Delete (`docs/guides/crud/delete.md`)

CRUD - Delete - Swift SDK

Delete Realm Objects

Deleting Realm Objects must occur within write transactions. For
more information about write transactions, see: Transactions.

If you want to delete the Realm file itself, see: Delete a Realm.

Important:
You cannot access or modify an object after you have deleted it from
a realm. If you try to use a deleted object, Realm throws an
error.

About The Examples On This Page

The examples on this page use the following models:

Objective-C

objectivec
// DogToy.h
@interface DogToy : RLMObject
@property NSString *name;
@end

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@property int age;
@property NSString *color;

// To-one relationship
@property DogToy *favoriteToy;

@end

// Enable Dog for use in RLMArray
RLM_COLLECTION_TYPE(Dog)

// Person.h
// A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs.
@interface Person : RLMObject
@property int _id;
@property NSString *name;

// To-many relationship - a person can have many dogs
@property RLMArray<Dog *><Dog> *dogs;

// Inverse relationship - a person can be a member of many clubs
@property (readonly) RLMLinkingObjects *clubs;
@end

RLM_COLLECTION_TYPE(Person)

// DogClub.h
@interface DogClub : RLMObject
@property NSString *name;
@property RLMArray<Person *><Person> *members;
@end

// Dog.m
@implementation Dog
@end

// DogToy.m
@implementation DogToy
@end

// Person.m
@implementation Person
// Define the primary key for the class
+ (NSString *)primaryKey {
    return @"_id";
}

// Define the inverse relationship to dog clubs
+ (NSDictionary *)linkingObjectsProperties {
    return @{
        @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"],
    };
}
@end

// DogClub.m
@implementation DogClub
@end

Swift

swift
class Dog: Object {
    @Persisted var name = ""
    @Persisted var age = 0
    @Persisted var color = ""
    @Persisted var currentCity = ""
    @Persisted var citiesVisited: MutableSet<String>
    @Persisted var companion: AnyRealmValue

    // Map of city name -> favorite park in that city
    @Persisted var favoriteParksByCity: Map<String, String>
}

Delete an Object

Objective-C

To delete an object from a realm, pass the object to
-[RLMRealm deleteObject:]
inside of a write transaction.

objectivec
[realm transactionWithBlock:^() {
    // Delete the instance from the realm.
    [realm deleteObject:dog];
}];

Swift

To delete an object from a realm, pass the object to
Realm.delete(_:)
inside of a write transaction.

swift
// Previously, we've added a dog object to the realm.
let dog = Dog(value: ["name": "Max", "age": 5])

let realm = try! Realm()
try! realm.write {
    realm.add(dog)
}

// Delete the instance from the realm.
try! realm.write {
    realm.delete(dog)
}

Delete Multiple Objects

Swift

Version added: 10.19.0

To delete a collection of objects from a realm, pass the
collection to Realm.delete(_:)
inside of a write transaction.

swift
let realm = try! Realm()
try! realm.write {
    // Find dogs younger than 2 years old.
    let puppies = realm.objects(Dog.self).where {
        $0.age < 2
    }

    // Delete the objects in the collection from the realm.
    realm.delete(puppies)
}

Swift Nspredicate

To delete a collection of objects from a realm, pass the
collection to Realm.delete(_:)
inside of a write transaction.

swift
let realm = try! Realm()
try! realm.write {
    // Find dogs younger than 2 years old.
    let puppies = realm.objects(Dog.self).filter("age < 2")

    // Delete the objects in the collection from the realm.
    realm.delete(puppies)
}

Objective-C

To delete a collection of objects from a realm, pass the
collection to -[Realm deleteObjects:]
inside of a write transaction.

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

[realm transactionWithBlock:^() {
    // Find dogs younger than 2 years old.
    RLMResults<Dog *> *puppies = [Dog objectsInRealm:realm where:@"age < 2"];

    // Delete all objects in the collection from the realm.
    [realm deleteObjects:puppies];
}];

Sometimes, you want to delete related objects when you delete the parent
object. We call this a chaining delete. Realm does not delete
the related objects for you. If you do not delete the objects yourself,
they remain orphaned in your realm. Whether or not this is a problem
depends on your application's needs.

The best way to delete dependent objects is to iterate through
the dependencies and delete them before deleting the parent object.

Objective-C

objectivec
[realm transactionWithBlock:^() {
    // Delete Ali's dogs.
    [realm deleteObjects:[ali dogs]];
    // Delete Ali.
    [realm deleteObject:ali];
}];

Swift

swift
let person = realm.object(ofType: Person.self, forPrimaryKey: 1)!
try! realm.write {
    // Delete the related collection
    realm.delete(person.dogs)
    realm.delete(person)
}

Delete All Objects of a Specific Type

Objective-C

To delete all objects of a given object type from a realm, pass
the result of [+[YourRealmObjectClass
allObjectsInRealm:]]
to -[Realm deleteObjects:]
inside of a write transaction. Replace YourRealmObjectClass
with your Realm object class name.

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

[realm transactionWithBlock:^() {
    // Delete all instances of Dog from the realm.
    RLMResults<Dog *> *allDogs = [Dog allObjectsInRealm:realm];
    [realm deleteObjects:allDogs];
}];

Swift

To delete all objects of a given object type from a realm, pass
the result of Realm.objects(_)
for the type you wish to delete to Realm.delete(_:)
inside of a write transaction.

swift
let realm = try! Realm()

try! realm.write {
    // Delete all instances of Dog from the realm.
    let allDogs = realm.objects(Dog.self)
    realm.delete(allDogs)
}

Delete All Objects in a Realm

Objective-C

To delete all objects from the realm, call [-[RLMRealm
deleteAllObjects]]
inside of a write transaction. This clears the realm of all object
instances but does not affect the realm's schema.

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

[realm transactionWithBlock:^() {
    // Delete all objects from the realm.
    [realm deleteAllObjects];
}];

Swift

To delete all objects from the realm, call
Realm.deleteAll() inside of a
write transaction. This clears the realm of all object instances
but does not affect the realm's schema.

swift
let realm = try! Realm()

try! realm.write {
    // Delete all objects from the realm.
    realm.deleteAll()
}

Delete Map Keys/Values

You can delete map entries in a few ways:

  • Use removeObject(for:) to remove the key and the value
  • If the dictionary's value is optional, you can set the value of the key to
    nil to keep the key.
swift
let realm = try! Realm()

// Find the dog we want to update
let wolfie = realm.objects(Dog.self).where {
    $0.name == "Wolfie"
}.first!

// Delete an entry
try! realm.write {
    // Use removeObject(for:)
    wolfie.favoriteParksByCity.removeObject(for: "New York")
    // Or assign `nil` to delete non-optional values.
    // If the value type were optional (e.g. Map<String, String?>)
    // this would assign `nil` to that entry rather than deleting it.
    wolfie.favoriteParksByCity["New York"] = nil
}
XCTAssertNil(wolfie.favoriteParksByCity["New York"])

Delete MutableSet Elements

You can delete specific elements from a MutableSet, or clear all of the elements from the set.
If you are working with multiple sets, you can also remove elements in one
set from the other set; see: Update a MutableSet Property.

swift
let realm = try! Realm()

// Record a dog's name and list of cities he has visited.
let dog = Dog()
dog.name = "Maui"
let dogCitiesVisited = ["New York", "Boston", "Toronto"]
try! realm.write {
    realm.add(dog)
    dog.citiesVisited.insert(objectsIn: dogCitiesVisited)
}
XCTAssertEqual(dog.citiesVisited.count, 3)

// Later... we decide the dog didn't really visit Toronto
// since the plane just stopped there for a layover.
// Remove the element from the set.
try! realm.write {
    dog.citiesVisited.remove("Toronto")
}
XCTAssertEqual(dog.citiesVisited.count, 2)

// Or, in the case where the person entered the data for
// the wrong dog, remove all elements from the set.
try! realm.write {
    dog.citiesVisited.removeAll()
}
XCTAssertEqual(dog.citiesVisited.count, 0)

Delete the Value of an AnyRealmValue

To delete the value of an AnyRealmValue, set it to .none.

swift
let realm = try! Realm()

// Wolfie's companion is "Fluffy the Cat", represented by a string.
// Fluffy has gone to visit friends for the summer, so Wolfie has no companion.
let wolfie = realm.objects(Dog.self).where {
    $0.name == "Wolfie"
}.first!

try! realm.write {
    // You cannot set an AnyRealmValue to nil; you must set it to `.none`, instead.
    wolfie.companion = .none
}

Delete an Object Asynchronously

You can use Swift concurrency features to asynchronously delete objects
using an actor-isolated realm.

This function from the example RealmActor defined on the
Use Realm with Actors page shows how you might
delete an object in an actor-isolated realm:

swift
func deleteTodo(id: ObjectId) async throws {
    try await realm.asyncWrite {
        let todoToDelete = realm.object(ofType: Todo.self, forPrimaryKey: id)
        realm.delete(todoToDelete!)
    }
}

And you might perform this deletion using Swift's async syntax:

swift
let actor = try await RealmActor()
let todoId = await actor.getObjectId(forTodoNamed: "Keep Mr. Frodo safe from that Gollum")

try await actor.deleteTodo(id: todoId)
let updatedTodoCount = await actor.count
if updatedTodoCount == todoCount - 1 {
    print("Successfully deleted the todo")
}

This operation does not block or perform I/O on the calling thread. For
more information about writing to realm using Swift concurrency features,
refer to Use Realm with Actors.


Guides Crud Filter Data (`docs/guides/crud/filter-data.md`)

Filter Data - Swift SDK

Overview

To filter data in your realm, you can leverage
Realm's query engine.

Version added: 10.19.0

The Realm Swift Query API offers an
idiomatic way for Swift developers to query data. Use Swift-style syntax
to query a realm with the benefits of auto-completion and
type safety. The Realm Swift Query API does not replace the NSPredicate
Query API in newer SDK versions; instead, you can use either.

For SDK versions prior to 10.19.0, or for Objective-C developers,
Realm's query engine supports NSPredicate Query.

About the Examples on This Page

The examples in this page use a simple data set for a
task list app. The two Realm object types are Project
and Task. A Task has a name, assignee's name, and
completed flag. There is also an arbitrary number for
priority -- higher is more important -- and a count of
minutes spent working on it. Finally, a Task can have one
or more string labels and one or more integer ratings.

A Project has zero or more Tasks.

See the schema for these two classes, Project and
Task, below:

Objective-C

objectivec
// Task.h
@interface Task : RLMObject
@property NSString *name;
@property bool isComplete;
@property NSString *assignee;
@property int priority;
@property int progressMinutes;
@end
RLM_COLLECTION_TYPE(Task)
// Task.m
@implementation Task
@end

// Project.h
@interface Project : RLMObject
@property NSString *name;
@property RLMArray<Task> *tasks;
@end
// Project.m
@implementation Project
@end

Swift

swift
class Task: Object {
    @Persisted var name = ""
    @Persisted var isComplete = false
    @Persisted var assignee: String?
    @Persisted var priority = 0
    @Persisted var progressMinutes = 0
    @Persisted var labels: MutableSet<String>
    @Persisted var ratings: MutableSet<Int>
}

class Project: Object {
    @Persisted var name = ""
    @Persisted var tasks: List<Task>
}

You can set up the realm for these examples with the following code:

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^() {
    // Add projects and tasks here
}];

RLMResults *tasks = [Task allObjectsInRealm:realm];
RLMResults *projects = [Project allObjectsInRealm:realm];

Swift

swift
let realm = try! Realm()
try! realm.write {
    // Add tasks and projects here.
    let project = Project()
    project.name = "New Project"
    let task = Task()
    task.assignee = "Alex"
    task.priority = 5
    project.tasks.append(task)
    realm.add(project)
    // ...
}
let tasks = realm.objects(Task.self)
let projects = realm.objects(Project.self)

Realm Swift Query API

Version added: 10.19.0
For SDK versions older than 10.19.0, use the NSPredicate query API.

You can build a filter with Swift-style syntax using the .where
Realm Swift query API:

swift
let realmSwiftQuery = projects.where {
    ($0.tasks.progressMinutes > 1) && ($0.tasks.assignee == "Ali")
}

This query API constructs an NSPredicate
to perform the query. It gives developers a type-safe idiomatic API to
use directly, and abstracts away the NSPredicate construction.

The .where API takes a callback that evaluates to true or false. The
callback receives an instance of the type being queried, and you can
leverage the compiler to statically check that you are creating valid queries
that reference valid properties.

In the examples on this page, we use the $0 shorthand to reference
the variable passed into the callback.

Operators

There are several types of operators available to query a
Realm collection. Queries
work by evaluating an operator expression for every
object in the collection being
queried. If the expression resolves to true, Realm
Database includes the object in the results collection.

Comparison Operators

You can use Swift comparison operators with the Realm Swift
Query API (==, !=, >, >=, <, <=).

Example:
The following example uses the query engine's
comparison operators to:

  • Find high priority tasks by comparing the value of the priority property value with a threshold number, above which priority can be considered high.
  • Find long-running tasks by seeing if the progressMinutes property is at or above a certain value.
  • Find unassigned tasks by finding tasks where the assignee property is equal to null.
swift
let highPriorityTasks = tasks.where {
    $0.priority > 5
}
print("High-priority tasks: \(highPriorityTasks.count)")

let longRunningTasks = tasks.where {
    $0.progressMinutes >= 120
}
print("Long running tasks: \(longRunningTasks.count)")

let unassignedTasks = tasks.where {
    $0.assignee == nil
}
print("Unassigned tasks: \(unassignedTasks.count)")

Collections

You can query for values within a collection using the .contains operators.
You can search for individual values by element, or search within a range.

Operator Description
.in(_ collection:) Evaluates to true if the property referenced by the expression contains an element in the given array.
.contains(_ element:) Equivalent to the IN operator. Evaluates to true if the property referenced by the expression contains the value.
.contains(_ range:) Equivalent to the BETWEEN operator. Evaluates to true if the property referenced by the expression contains a value that is within the range.
.containsAny(in: ) Equivalent to the IN operator combined with the ANY operator. Evaluates to true if any elements contained in the given array are present in the collection.

Example:

  • Find tasks where the labels MutableSet collection property contains "quick win".
  • Find tasks where the progressMinutes property is within a given range of minutes.
swift
let quickWinTasks = tasks.where {
    $0.labels.contains("quick win")
}
print("Tasks labeled 'quick win': \(quickWinTasks.count)")

let progressBetween30and60 = tasks.where {
    $0.progressMinutes.contains(30...60)
}
print("Tasks with progress between 30 and 60 minutes: \(progressBetween30and60.count)")

Find tasks where the labels MutableSet collection property contains any of the elements in the given array: "quick win" or "bug".

swift
let quickWinOrBugTasks = tasks.where {
    $0.labels.containsAny(in: ["quick win", "bug"])
}
print("Tasks labeled 'quick win' or 'bug': \(quickWinOrBugTasks.count)")

Version added: 10.23.0
:The IN operator

The Realm Swift Query API now supports the IN operator. Evaluates to true if the property referenced by the expression contains the value.

Example:
Find tasks assigned to specific teammates Ali or Jamie by seeing if the assignee property is in a list of names.

swift
let taskAssigneeInAliOrJamie = tasks.where {
    let assigneeNames = ["Ali", "Jamie"]
    return $0.assignee.in(assigneeNames)
}
print("Tasks IN Ali or Jamie: \(taskAssigneeInAliOrJamie.count)")

Logical Operators

You can make compound queries using Swift logical operators (&&, !,
||).

Example:
We can use the query language's logical operators to find
all of Ali's completed tasks. That is, we find all tasks
where the assignee property value is equal to 'Ali' AND
the isComplete property value is true:

swift
let aliComplete = tasks.where {
    ($0.assignee == "Ali") && ($0.isComplete == true)
}
print("Ali's complete tasks: \(aliComplete.count)")

String Operators

You can compare string values using these string operators.
Regex-like wildcards allow more flexibility in search.

Note:
You can use the following options with string operators:

  • .caseInsensitive for case insensitivity. $0.name.contains("f", options: .caseInsensitive)
  • .diacriticInsensitive for diacritic insensitivity: Realm treats
    special characters as the base character (e.g. é -> e). $0.name.contains("e", options: .diacriticInsensitive)
Operator Description
.starts(with value: String) Evaluates to true if the collection contains an element whose value begins with the specified string value.
.contains(_ value: String) Evaluates to true if the left-hand string expression is found anywhere in the right-hand string expression.
.ends(with value: String) Evaluates to true if the collection contains an element whose value ends with the specified string value.
.like(_ value: String) Evaluates to true if the left-hand string expression matches the right-hand string wildcard string expression. A wildcard string expression is a string that uses normal characters with two special wildcard characters: The * wildcard matches zero or more of any character The ? wildcard matches any character. For example, the wildcard string "d?g" matches "dog", "dig", and "dug", but not "ding", "dg", or "a dog".
== Evaluates to true if the left-hand string is lexicographically equal to the right-hand string.
!= Evaluates to true if the left-hand string is not lexicographically equal to the right-hand string.

Example:
The following example uses the query engine's string operators to find:

  • Projects with a name starting with the letter 'e'
  • Projects with names that contain 'ie'
  • Projects with an assignee property whose value is similar to Al?x
  • Projects that contain e-like characters with diacritic insensitivity
swift
// Use the .caseInsensitive option for case-insensitivity.
let startWithE = projects.where {
    $0.name.starts(with: "e", options: .caseInsensitive)
}
print("Projects that start with 'e': \(startWithE.count)")

let containIe = projects.where {
    $0.name.contains("ie")
}
print("Projects that contain 'ie': \(containIe.count)")

let likeWildcard = tasks.where {
    $0.assignee.like("Al?x")
}
print("Tasks with assignees like Al?x: \(likeWildcard.count)")

// Use the .diacriticInsensitive option for diacritic insensitivity: contains 'e', 'E', 'é', etc.
let containElike = projects.where {
    $0.name.contains("e", options: .diacriticInsensitive)
}
print("Projects that contain 'e', 'E', 'é', etc.: \(containElike.count)")

Note:
String sorting and case-insensitive queries are only supported for
character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended
A', and 'Latin Extended B' (UTF-8 range 0-591).

Geospatial Operators

Version added: 10.47.0

Use the geoWithin operator to query geospatial data with one of the
SDK's provided shapes:

  • GeoCircle
  • GeoBox
  • GeoPolygon

This operator evaluates to true if:

  • An object has a geospatial data "shape" containing a String property
    with the value of Point and a List containing a longitude/latitude
    pair.
  • The longitude/latitude of the persisted object falls within the geospatial
    query shape.
swift
let companiesInSmallCircle = realm.objects(Geospatial_Company.self).where {
    $0.location.geoWithin(smallCircle!)
}
print("Number of companies in small circle: \(companiesInSmallCircle.count)")

For more information about querying geospatial data, refer to
Query Geospatial Data.

Aggregate Operators

You can apply an aggregate operator to a collection property
of a Realm object. Aggregate operators traverse a
collection and reduce it
to a single value.

Operator Description
.avg Evaluates to the average value of a given numerical property across a collection.
.count Evaluates to the number of objects in the given collection. This is currently only supported on to-many relationship collections and not on lists of primitives. In order to use .count on a list of primitives, consider wrapping the primitives in a Realm object.
.max Evaluates to the highest value of a given numerical property across a collection.
.min Evaluates to the lowest value of a given numerical property across a collection.
.sum Evaluates to the sum of a given numerical property across a collection.

Example:
We create a couple of filters to show different facets of
the data:

  • Projects with average tasks priority above 5.
  • Projects that contain only low-priority tasks below 5.
  • Projects where all tasks are high-priority above 5.
  • Projects that contain more than 5 tasks.
  • Long running projects.
swift
let averageTaskPriorityAbove5 = projects.where {
    $0.tasks.priority.avg > 5
}
print("Projects with average task priority above 5: \(averageTaskPriorityAbove5.count)")

let allTasksLowerPriority = projects.where {
    $0.tasks.priority.max < 5
}
print("Projects where all tasks are lower priority: \(allTasksLowerPriority.count)")

let allTasksHighPriority = projects.where {
    $0.tasks.priority.min > 5
}
print("Projects where all tasks are high priority: \(allTasksHighPriority.count)")

let moreThan5Tasks = projects.where {
    $0.tasks.count > 5
}
print("Projects with more than 5 tasks: \(moreThan5Tasks.count)")

let longRunningProjects = projects.where {
    $0.tasks.progressMinutes.sum > 100
}
print("Long running projects: \(longRunningProjects.count)")

Set Operators

A set operator uses specific rules to determine whether
to pass each input collection object to the output
collection by applying a given query expression to every element of
a given list property of
the object.

Example:
Running the following queries in projects collections returns:

  • Projects where a set of string labels contains any of "quick win", "bug".
  • Projects where any element in a set of integer ratings is greater than 3.
swift
let projectsWithGivenLabels = projects.where {
    $0.tasks.labels.containsAny(in: ["quick win", "bug"])
}
print("Projects with quick wins: \(projectsWithGivenLabels.count)")

let projectsWithRatingsOver3 = projects.where {
    $0.tasks.ratings > 3
}
print("Projects with any ratings over 3: \(projectsWithRatingsOver3.count)")

Subqueries

You can iterate through a collection property with another query using a
subquery. To form a subquery, you must wrap the expression in parentheses
and immediately follow it with the .count aggregator.

swift
(<query>).count > n

If the expression does not produce a valid subquery, you'll get an
exception at runtime.

Example:
Running the following query on a projects collection returns projects
with tasks that have not been completed by a user named Alex.

swift
let subquery = projects.where {
            ($0.tasks.isComplete == false && $0.tasks.assignee == "Alex").count > 0
}
print("Projects with incomplete tasks assigned to Alex: \(subquery.count)")

NSPredicate Queries

You can build a filter with NSPredicate:

Objective-C

objectivec
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"progressMinutes > %@ AND name == %@", @1, @"Ali"];

Swift

swift
let predicate = NSPredicate(format: "progressMinutes > 1 AND name == %@", "Ali")

Expressions

Filters consist of expressions in an NSPredicate. An expression consists of
one of the following:

  • The name (keypath) of a property of the object currently being evaluated.
  • An operator and up to two argument expression(s).
  • A value, such as a string ('hello') or a number (5).

Dot Notation

When referring to an object property, you can use dot notation to refer
to child properties of that object. You can even refer to the properties of
embedded objects and relationships with dot notation.

For example, consider a query on an object with a workplace property that
refers to a Workplace object. The Workplace object has an embedded object
property, address. You can chain dot notations to refer to the zipcode
property of that address:

objective-c
workplace.address.zipcode == 10012

Substitutions

You can use the following substitutions in your predicate format strings:

  • %@ to specify values
  • %K to specify keypaths

Objective-C

objectivec
[NSPredicate predicateWithFormat:@"%K > %@ AND %K == %@", @"progressMinutes", @1, @"name", @"Ali"];

Swift

swift
NSPredicate(format: "%K > %@ AND %K == %@", "progressMinutes", NSNumber(1), "name", "Ali")

Operators

There are several types of operators available to filter a
Realm collection. Filters
work by evaluating an operator expression for every
object in the collection being
filtered. If the expression resolves to true, Realm
Database includes the object in the results collection.

Comparison Operators

The most straightforward operation in a search is to compare
values.

Important:
The type on both sides of the operator must be equivalent. For
example, comparing an ObjectId with string will result in a precondition failure with a
message like:

text
"Expected object of type object id for property 'id' on object of type
'User', but received: 11223344556677889900aabb (Invalid value)"

You can compare any numeric type with any other numeric type.

Operator Description
between Evaluates to true if the left-hand numerical or date expression is between or equal to the right-hand range. For dates, this evaluates to true if the left-hand date is within the right-hand date range.
== , = Evaluates to true if the left-hand expression is equal to the right-hand expression.
> Evaluates to true if the left-hand numerical or date expression is greater than the right-hand numerical or date expression. For dates, this evaluates to true if the left-hand date is later than the right-hand date.
>= Evaluates to true if the left-hand numerical or date expression is greater than or equal to the right-hand numerical or date expression. For dates, this evaluates to true if the left-hand date is later than or the same as the right-hand date.
in Evaluates to true if the left-hand expression is in the right-hand list or string.
< Evaluates to true if the left-hand numerical or date expression is less than the right-hand numerical or date expression. For dates, this evaluates to true if the left-hand date is earlier than the right-hand date.
<= Evaluates to true if the left-hand numeric expression is less than or equal to the right-hand numeric expression. For dates, this evaluates to true if the left-hand date is earlier than or the same as the right-hand date.
!= , <> Evaluates to true if the left-hand expression is not equal to the right-hand expression.

Example:
The following example uses the query engine's
comparison operators to:

  • Find high priority tasks by comparing the value of the priority property value with a threshold number, above which priority can be considered high.
  • Find long-running tasks by seeing if the progressMinutes property is at or above a certain value.
  • Find unassigned tasks by finding tasks where the assignee property is equal to null.
  • Find tasks assigned to specific teammates Ali or Jamie by seeing if the assignee property is in a list of names.

Objective-C

objectivec
NSLog(@"High priority tasks: %lu",
      [[tasks objectsWithPredicate:[NSPredicate predicateWithFormat:@"priority > %@", @5]] count]);

NSLog(@"Short running tasks: %lu",
      [[tasks objectsWhere:@"progressMinutes between {1, 15}"] count]);

NSLog(@"Unassigned tasks: %lu",
      [[tasks objectsWhere:@"assignee == nil"] count]);

NSLog(@"Ali or Jamie's tasks: %lu",
      [[tasks objectsWhere:@"assignee IN {'Ali', 'Jamie'}"] count]);

NSLog(@"Tasks with progress between 30 and 60 minutes: %lu",
      [[tasks objectsWhere:@"progressMinutes BETWEEN {30, 60}"] count]);

Swift

swift
let highPriorityTasks = tasks.filter("priority > 5")
print("High priority tasks: \(highPriorityTasks.count)")

let longRunningTasks = tasks.filter("progressMinutes > 120")
print("Long running tasks: \(longRunningTasks.count)")

let unassignedTasks = tasks.filter("assignee == nil")
print("Unassigned tasks: \(unassignedTasks.count)")

let aliOrJamiesTasks = tasks.filter("assignee IN {'Ali', 'Jamie'}")
print("Ali or Jamie's tasks: \(aliOrJamiesTasks.count)")

let progressBetween30and60 = tasks.filter("progressMinutes BETWEEN {30, 60}")
print("Tasks with progress between 30 and 60 minutes: \(progressBetween30and60.count)")

Logical Operators

You can make compound predicates using logical operators.

Operator Description
and && Evaluates to true if both left-hand and right-hand expressions are true.
not ! Negates the result of the given expression.
or \ \

Example:
We can use the query language's logical operators to find
all of Ali's completed tasks. That is, we find all tasks
where the assignee property value is equal to 'Ali' AND
the isComplete property value is true:

Objective-C

objectivec
NSLog(@"Ali's complete tasks: %lu",
  [[tasks objectsWhere:@"assignee == 'Ali' AND isComplete == true"] count]);

Swift

swift
let aliComplete = tasks.filter("assignee == 'Ali' AND isComplete == true")
print("Ali's complete tasks: \(aliComplete.count)")

String Operators

You can compare string values using these string operators.
Regex-like wildcards allow more flexibility in search.

Note:
You can use the following modifiers with the string operators:

  • [c] for case insensitivity. [NSPredicate predicateWithFormat: @"name CONTAINS[c] 'f'"]``NSPredicate(format: "name CONTAINS[c] 'f'")
  • [d] for diacritic insensitivity: Realm treats special characters as the base character (e.g. é -> e). [NSPredicate predicateWithFormat: @"name CONTAINS[d] 'e'"]``NSPredicate(format: "name CONTAINS[d] 'e'")
Operator Description
beginsWith Evaluates to true if the left-hand string expression begins with the right-hand string expression. This is similar to contains, but only matches if the right-hand string expression is found at the beginning of the left-hand string expression.
contains , in Evaluates to true if the left-hand string expression is found anywhere in the right-hand string expression.
endsWith Evaluates to true if the left-hand string expression ends with the right-hand string expression. This is similar to contains, but only matches if the left-hand string expression is found at the very end of the right-hand string expression.
like Evaluates to true if the left-hand string expression matches the right-hand string wildcard string expression. A wildcard string expression is a string that uses normal characters with two special wildcard characters: The * wildcard matches zero or more of any character The ? wildcard matches any character. For example, the wildcard string "d?g" matches "dog", "dig", and "dug", but not "ding", "dg", or "a dog".
== , = Evaluates to true if the left-hand string is lexicographically equal to the right-hand string.
!= , <> Evaluates to true if the left-hand string is not lexicographically equal to the right-hand string.

Example:
We use the query engine's string operators to find
projects with a name starting with the letter 'e' and
projects with names that contain 'ie':

Objective-C

objectivec
// Use [c] for case-insensitivity.
NSLog(@"Projects that start with 'e': %lu",
  [[projects objectsWhere:@"name BEGINSWITH[c] 'e'"] count]);

NSLog(@"Projects that contain 'ie': %lu",
  [[projects objectsWhere:@"name CONTAINS 'ie'"] count]);

Swift

swift
// Use [c] for case-insensitivity.
let startWithE = projects.filter("name BEGINSWITH[c] 'e'")
print("Projects that start with 'e': \(startWithE.count)")

let containIe = projects.filter("name CONTAINS 'ie'")
print("Projects that contain 'ie': \(containIe.count)")

// [d] for diacritic insensitivty: contains 'e', 'E', 'é', etc.
let containElike = projects.filter("name CONTAINS[cd] 'e'")
print("Projects that contain 'e', 'E', 'é', etc.: \(containElike.count)")

Note:
String sorting and case-insensitive queries are only supported for
character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended
A', and 'Latin Extended B' (UTF-8 range 0-591).

Geospatial Operators

Version added: 10.47.0

You can perform a geospatial query using the IN operator with one
of the SDK's provided shapes:

  • GeoCircle
  • GeoBox
  • GeoPolygon

This operator evaluates to true if:

  • An object has a geospatial data "shape" containing a String property
    with the value of Point and a List containing a longitude/latitude
    pair.
  • The longitude/latitude of the persisted object falls within the geospatial
    query shape.
swift
let filterArguments = NSMutableArray()
filterArguments.add(largeBox)
let companiesInLargeBox = realm.objects(Geospatial_Company.self)
    .filter(NSPredicate(format: "location IN %@", argumentArray: filterArguments as? [Any]))
print("Number of companies in large box: \(companiesInLargeBox.count)")

For more information about querying geospatial data, refer to
Query Geospatial Data.

Aggregate Operators

You can apply an aggregate operator to a collection property
of a Realm object. Aggregate operators traverse a
collection and reduce it
to a single value.

Operator Description
@avg Evaluates to the average value of a given numerical property across a collection.
@count Evaluates to the number of objects in the given collection. This is currently only supported on to-many relationship collections and not on lists of primitives. In order to use @count on a list of primitives, consider wrapping the primitives in a Realm object.
@max Evaluates to the highest value of a given numerical property across a collection.
@min Evaluates to the lowest value of a given numerical property across a collection.
@sum Evaluates to the sum of a given numerical property across a collection.

Example:
We create a couple of filters to show different facets of
the data:

  • Projects with average tasks priority above 5.
  • Long running projects.

Objective-C

objectivec
NSLog(@"Projects with average tasks priority above 5: %lu",
      [[projects objectsWhere:@"[email protected] > 5"] count]);

NSLog(@"Projects where all tasks are lower priority: %lu",
      [[projects objectsWhere:@"[email protected] < 5"] count]);

NSLog(@"Projects where all tasks are high priority: %lu",
      [[projects objectsWhere:@"[email protected] > 5"] count]);

NSLog(@"Projects with more than 5 tasks: %lu",
      [[projects objectsWhere:@"tasks.@count > 5"] count]);

NSLog(@"Long running projects: %lu",
      [[projects objectsWhere:@"[email protected] > 100"] count]);

Swift

swift
let averageTaskPriorityAbove5 = projects.filter("[email protected] > 5")
print("Projects with average task priority above 5: \(averageTaskPriorityAbove5.count)")

let allTasksLowerPriority = projects.filter("[email protected] < 5")
print("Projects where all tasks are lower priority: \(allTasksLowerPriority.count)")

let allTasksHighPriority = projects.filter("[email protected] > 5")
print("Projects where all tasks are high priority: \(allTasksHighPriority.count)")

let moreThan5Tasks = projects.filter("tasks.@count > 5")
print("Projects with more than 5 tasks: \(moreThan5Tasks.count)")

let longRunningProjects = projects.filter("[email protected] > 100")
print("Long running projects: \(longRunningProjects.count)")

Set Operators

A set operator uses specific rules to determine whether
to pass each input collection object to the output
collection by applying a given predicate to every element of
a given list property of
the object.

Operator Description
ALL Returns objects where the predicate evaluates to true for all objects in the collection.
ANY, SOME Returns objects where the predicate evaluates to true for any objects in the collection.
NONE Returns objects where the predicate evaluates to false for all objects in the collection.

Example:
We use the query engine's set operators to find:

  • Projects with no complete tasks.
  • Projects with any top priority tasks.

Objective-C

objectivec
NSLog(@"Projects with no complete tasks: %lu",
  [[projects objectsWhere:@"NONE tasks.isComplete == true"] count]);

NSLog(@"Projects with any top priority tasks: %lu",
  [[projects objectsWhere:@"ANY tasks.priority == 10"] count]);

Swift

swift
let noCompleteTasks = projects.filter("NONE tasks.isComplete == true")
print("Projects with no complete tasks: \(noCompleteTasks.count)")

let anyTopPriorityTasks = projects.filter("ANY tasks.priority == 10")
print("Projects with any top priority tasks: \(anyTopPriorityTasks.count)")

Subqueries

You can iterate through a collection property with another query using the
SUBQUERY() predicate function. SUBQUERY() has the following signature:

objective-c
SUBQUERY(<collection>, <variableName>, <predicate>)
  • collection: the name of the list property to iterate through
  • variableName: a variable name of the current element to use in the subquery
  • predicate: a string that contains the subquery predicate. You can use the
    variable name specified by variableName to refer to the currently iterated
    element.

Example:
Running the following filter on a projects collection returns projects
with tasks that have not been completed by a user named Alex.

Objective-C

objectivec
NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"SUBQUERY(tasks, $task, $task.isComplete == %@ AND $task.assignee == %@).@count > 0",
                          @NO,
                          @"Alex"];
NSLog(@"Projects with incomplete tasks assigned to Alex: %lu",
  [[projects objectsWithPredicate:predicate] count]);

Swift

swift
let predicate = NSPredicate(
    format: "SUBQUERY(tasks, $task, $task.isComplete == false AND $task.assignee == %@).@count > 0", "Alex")
print("Projects with incomplete tasks assigned to Alex: \(projects.filter(predicate).count)")

Guides Crud React To Changes (`docs/guides/crud/react-to-changes.md`)

React to Changes - Swift SDK

All Realm objects are live objects, which means they
automatically update whenever they're modified. Realm emits a
notification event whenever any property changes. You can register a
notification handler to listen for these notification events, and update
your UI with the latest data.

This page shows how to manually register notification listeners in Swift.
Realm SDK for Swift offers SwiftUI property wrappers to make it easy to
automatically update the UI when data changes. For more about how to use
the SwiftUI property wrappers to react to changes, refer to
Observe an Object.

Register a Realm Change Listener

You can register a notification handler on an entire realm. Realm calls the notification
handler whenever any write transaction involving that Realm is
committed. The handler receives no information about the change.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

// Observe realm notifications. Keep a strong reference to the notification token
// or the observation will stop.
RLMNotificationToken *token = [realm addNotificationBlock:^(RLMNotification  _Nonnull notification, RLMRealm * _Nonnull realm) {
    // `notification` is an enum specifying what kind of notification was emitted.
    // ... update UI ...
}];

// ...

// Later, explicitly stop observing.
[token invalidate];

Swift

swift
let realm = try! Realm()

// Observe realm notifications. Keep a strong reference to the notification token
// or the observation will stop.
let token = realm.observe { notification, realm in
    // `notification` is an enum specifying what kind of notification was emitted
    viewController.updateUI()
}

// ...

// Later, explicitly stop observing.
token.invalidate()

Register a Collection Change Listener

You can register a notification handler on a collection within a
realm.

Realm notifies your handler:

  • After first retrieving the collection.
  • Whenever a write transaction adds, changes, or removes objects in the collection.

Notifications describe the changes since the prior notification with
three lists of indices: the indices of the objects that were deleted,
inserted, and modified.

Important:
In collection notification handlers, always apply changes
in the following order: deletions, insertions, then
modifications. Handling insertions before deletions may
result in unexpected behavior.

Collection notifications provide a change parameter that reports which
objects are deleted, added, or modified during the write transaction. This
RealmCollectionChange
resolves to an array of index paths that you can pass to a UITableView's
batch update methods.

Important:
This example of a collection change listener does not support
high-frequency updates. Under an intense workload, this collection
change listener may cause the app to throw an exception.

Objective-C

text
/* Detailed source-code truncated for AI context efficiency. */

Swift

text
/* Detailed source-code truncated for AI context efficiency. */

Register an Object Change Listener

You can register a notification handler on a specific object
within a realm. Realm notifies your handler:

  • When the object is deleted.
  • When any of the object's properties change.

The handler receives information about what fields changed
and whether the object was deleted.

Objective-C

objectivec
@interface Dog : RLMObject
@property NSString *name;
@property int age;
@end

@implementation Dog
@end

RLMNotificationToken *objectNotificationToken = nil;

void objectNotificationExample() {
    Dog *dog = [[Dog alloc] init];
    dog.name = @"Max";
    dog.age = 3;
    
    // Open the default realm
    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        [realm addObject:dog];
    }];

    // Observe object notifications. Keep a strong reference to the notification token
    // or the observation will stop. Invalidate the token when done observing.
    objectNotificationToken = [dog addNotificationBlock:^(BOOL deleted, NSArray<RLMPropertyChange *> * _Nullable changes, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"An error occurred: %@", [error localizedDescription]);
            return;
        }
        if (deleted) {
            NSLog(@"The object was deleted.");
            return;
        }
        NSLog(@"Property %@ changed to '%@'",
              changes[0].name,
              changes[0].value);
    }];

    // Now update to trigger the notification
    [realm transactionWithBlock:^{
        dog.name = @"Wolfie";
    }];

}

Swift

swift
// Define the dog class.
class Dog: Object {
    @Persisted var name = ""
}

var objectNotificationToken: NotificationToken?

func objectNotificationExample() {
    let dog = Dog()
    dog.name = "Max"

    // Open the default realm.
    let realm = try! Realm()
    try! realm.write {
        realm.add(dog)
    }
    // Observe object notifications. Keep a strong reference to the notification token
    // or the observation will stop. Invalidate the token when done observing.
    objectNotificationToken = dog.observe { change in
        switch change {
        case .change(let object, let properties):
            for property in properties {
                print("Property '\(property.name)' of object \(object) changed to '\(property.newValue!)'")
            }
        case .error(let error):
            print("An error occurred: \(error)")
        case .deleted:
            print("The object was deleted.")
        }
    }

    // Now update to trigger the notification
    try! realm.write {
        dog.name = "Wolfie"
    }
}

Register a Key Path Change Listener

Version added: 10.12.0

In addition to registering a notification handler on an object
or collection, you can pass an optional string keyPaths parameter to specify the key path or
key paths to watch.

Example:
```
/* Detailed source-code truncated for AI context efficiency. */

text
>

> Version added: 10.14.0

You can `observe`
a partially type-erased [PartialKeyPath](https://developer.apple.com/documentation/swift/partialkeypath)
on `Objects` or `RealmCollections`.

```swift
objectNotificationToken = dog.observe(keyPaths: [\Dog.favoriteToy, \Dog.age], { change in

When you specify keyPaths, only changes to those
keyPaths trigger notification blocks. Any other changes do not trigger
notification blocks.

Example:
Consider a Dog object where one of its properties is a list of
siblings:

swift
class Dog: Object {
    @Persisted var name = ""
    @Persisted var siblings: List<Dog>
    @Persisted var age: Int?
}

If you pass siblings as a keyPath to observe, any insertion,
deletion, or modification to the siblings list would trigger a
notification. However, a change to someSibling.name would not trigger
a notification, unless you explicitly observed ["siblings.name"].

Note:
Multiple notification tokens on the same object which filter for
separate key paths do not filter exclusively. If one key path
change is satisfied for one notification token, then all notification
token blocks for that object will execute.

Realm Collections

When you observe key paths on the various collection types, expect these
behaviors:

  • LinkingObjects:
    Observing a property of the LinkingObject triggers a notification for a
    change to that property, but does not trigger notifications for changes to
    its other properties. Insertions or deletions to the list or the object
    that the list is on trigger a notification.
  • Lists:
    Observing a property of the list's object will triggers a notification for
    a change to that property, but does not trigger notifications for changes
    to its other properties. Insertions or deletions to the list or the object
    that the list is on trigger a notification.
  • Map:
    Observing a property of the map's object triggers a notification for a change
    to that property, but does not trigger notifications for changes to its other
    properties. Insertions or deletions to the Map or the object that the map is
    on trigger a notification. The change parameter reports, in the form of
    keys within the map, which key-value pairs are added, removed, or modified
    during each write transaction.
  • MutableSet:
    Observing a property of a MutableSet's object triggers a notification
    for a change to that property, but does not trigger notifications for changes
    to its other properties. Insertions or deletions to the MutableSet or the
    object that the MutableSet is on trigger a notification.
  • Results:
    Observing a property of the Result triggers a notification for a change to
    that property, but does not trigger notifications for changes to its other
    properties. Insertions or deletions to the Result trigger a notification.

Write Silently

You can write to a realm without sending a notification to a
specific observer by passing the observer's notification token in an
array to realm.write(withoutNotifying:_):

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

// Observe realm notifications
RLMNotificationToken *token = [realm addNotificationBlock:^(RLMNotification  _Nonnull notification, RLMRealm * _Nonnull realm) {
    // ... handle update
}];

// Later, pass the token in an array to the realm's `-transactionWithoutNotifying:block:` method.
// Realm will _not_ notify the handler after this write.
[realm transactionWithoutNotifying:@[token] block:^{
   // ... write to realm
}];

// Finally
[token invalidate];

Swift

swift
let realm = try! Realm()

// Observe realm notifications
let token = realm.observe { notification, realm in
    // ... handle update
}

// Later, pass the token in an array to the realm.write(withoutNotifying:)
// method to write without sending a notification to that observer.
try! realm.write(withoutNotifying: [token]) {
    // ... write to realm
}

// Finally
token.invalidate()

Stop Watching for Changes

Observation stops when the token returned by an observe call becomes
invalid. You can explicitly invalidate a token by calling its
invalidate() method.

Important:
Notifications stop if the token is in a local variable that goes out
of scope.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

// Observe and obtain token
RLMNotificationToken *token = [realm addNotificationBlock:^(RLMNotification  _Nonnull notification, RLMRealm * _Nonnull realm) {
    /* ... */
}];

// Stop observing
[token invalidate];

Swift

swift
let realm = try! Realm()

// Observe and obtain token
let token = realm.observe { notification, realm in /* ... */ }

// Stop observing
token.invalidate()

Key-value Observation

Key-value Observation Compliance

Realm objects are key-value observing (KVO)
compliant

for most properties:

  • Almost all managed (non-ignored) properties on Object subclasses
  • The invalidated property on Object and List

You cannot observe LinkingObjects properties via Key-value observation.

Important:
You cannot add an object to a realm (with realm.add(obj) or similar
methods) while it has any registered observers.

Managed vs. Unmanaged KVO Considerations

Observing the properties of unmanaged instances of Object subclasses
works like any other dynamic property.

Observing the properties of managed objects works differently. With
realm-managed objects, the value of a property may change when:

  • You assign to it
  • The realm is refreshed, either manually with realm.refresh() or
    automatically on a runloop thread
  • You begin a write transaction after changes on another thread

Realm applies changes made in the write transaction(s) on other threads
at once. Observers see Key-value observation notifications at once.
Intermediate steps do not trigger KVO notifications.

Example:
Say your app performs a write transaction that increments a property
from 1 to 10. On the main thread, you get a single notification of a
change directly from 1 to 10. You won't get notifications for every
incremental change between 1 and 10.

Avoid modifying managed Realm objects from within
observeValueForKeyPath(_:ofObject:change:context:). Property values
can change when not in a write transaction, or as part of beginning a
write transaction.

Observing Realm Lists

Observing changes made to Realm List properties is simpler than
NSMutableArray properties:

  • You don't have to mark List properties as dynamic to observe them.
  • You can call modification methods on List directly. Anyone observing
    the property that stores it gets a notification.

You don't need to use mutableArrayValueForKey(_:), although realm
does support this for code compatibility.

Seealso:
Examples of using Realm with ReactiveCocoa from Objective-C,
and ReactKit from Swift.

React to Changes on a Different Actor

You can observe notifications on a different actor. Calling
await object.observe(on: Actor) or
await collection.observe(on: Actor) registers a block to be called each
time the object or collection changes.

text
/* Detailed source-code truncated for AI context efficiency. */

For more information about change notifications on another actor,
refer to Observe Notifications on a Different Actor.

React to Changes to a Class Projection

Like other realm objects, you can react to changes
to a class projection. When you register a class projection change listener,
you see notifications for changes made through the class projection object
directly. You also see notifications for changes to the underlying object's
properties that project through the class projection object.

Properties on the underlying object that are not @Projected in the
class projection do not generate notifications.

This notification block fires for changes in:

  • Person.firstName property of the class projection's underlying
    Person object, but not changes to Person.lastName or
    Person.friends.
  • PersonProjection.firstName property, but not another class projection
    that uses the same underlying object's property.
swift
let realm = try! Realm()
let projectedPerson = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })!
let token = projectedPerson.observe(keyPaths: ["firstName"], { change in
    switch change {
    case .change(let object, let properties):
        for property in properties {
            print("Property '\(property.name)' of object \(object) changed to '\(property.newValue!)'")
        }
    case .error(let error):
        print("An error occurred: \(error)")
    case .deleted:
        print("The object was deleted.")
    }
})

// Now update to trigger the notification
try! realm.write {
    projectedPerson.firstName = "David"
}

Notification Delivery

Notification delivery can vary depending on:

  • Whether or not the notification occurs within a write transaction
  • The relative threads of the write and the observation

When your application relies on the timing of notification delivery, such
as when you use notifications to update a UITableView, it's important
to understand the specific behaviors for your application code's context.

Perform Writes Only on a Different Thread than the Observing Thread

Reading an observed collection or object from inside a change notification
always accurately tells you what has changed in the collection passed to
the callback since the last time the callback was invoked.

Reading collections or objects outside of change notifications always gives
you the exact same values you saw in the most recent change notification
for that object.

Reading objects other than the observed one inside a change notification
may see a different value prior to the notification for that change being
delivered. Realm refresh brings the entire realm from 'old version' to
'latest version' in one operation. However, there might have been multiple
change notifications fired between 'old version' and 'latest version'. Inside
a callback, you may see changes that have pending notifications.

Writes on different threads eventually become visible on the observing
thread. Explicitly calling refresh() blocks until the writes made on
other threads are visible and the appropriate notifications have been sent.
If you call refresh() within a notification callback, it's a no-op.

Perform Writes on the Observing Thread, Outside of Notifications

At the start of the write transaction all behaviors above apply to this
context. Additionally, you can expect to always see the latest version of
the data.

Inside a write transaction, the only changes you see are those you've made
so far within the write transaction.

Between committing a write transaction and the next set of change
notifications being sent, you can see the changes you made in the write
transaction, but no other changes. Writes made on different threads do
not become visible until you receive the next set of notifications.
Performing another write on the same thread sends notifications for the
previous write first.

Perform Writes Inside of Notifications

When you perform writes within notifications, you see many of the same
behaviors above, with a few exceptions.

Callbacks invoked before the one that performed a write behave normally.
While Realm invokes change callbacks in a stable order, this is not strictly
the order in which you added the observations.

If beginning the write refreshes the realm, which can happen if another
thread is making writes, this triggers recursive notifications. These
nested notifications report the changes made since the last call to the
callback. For callbacks before the one making the write, this means the
inner notification reports only the changes made after the ones already
reported in the outer notification. If the callback making the write tries
to write again in the inner notification, Realm throws an exception.
The callbacks after the one making the write get a single notification for
both sets of changes.

After the callback completes the write and returns, Realm does not invoke
any of the subsequent callbacks as they no longer have any changes to report.
Realm provides a notification later for the write as if the write had happened
outside of a notification.

If beginning the write doesn't refresh the realm, the write happens as
usual. However, Realm invokes the subsequent callbacks in an inconsistent
state. They continue to report the original change information, but the
observed object/collection now includes the changes from the write made
in the previous callback.

If you try to perform manual checks and write handling to get more fine-grained
notifications from within a write transaction, you can get notifications
nested more than two levels deep. An example of a manual write handling is
checking realm.isInWriteTransaction, and if so making changes, calling
realm.commitWrite() and then realm.beginWrite(). The nested
notifications and potential for error make this manual manipulation
error-prone and difficult to debug.

You can use the writeAsync API to sidestep complexity
if you don't need fine-grained change information from inside your write block.
Observing an async write similar to this provides notifications even if the
notification happens to be delivered inside a write transaction:

swift
let token = dog.observe(keyPaths: [\Dog.age]) { change in
   guard case let .change(dog, _) = change else { return }
   dog.realm!.writeAsync {
      dog.isPuppy = dog.age < 2
   }
}

However, because the write is async the realm may have changed between the
notification and when the write happens. In this case, the change information
passed to the notification may no longer be applicable.

Updating a UITableView Based on Notifications

If you only update a UITableView via notifications, in the time between
a write transaction and the next notification arriving, the TableView's
state is out of sync with the data. The TableView could have a pending update
scheduled, which can appear to cause delayed or inconsistent updates.

You can address these behaviors in a few ways.

The following examples use this very basic UITableViewController.

text
/* Detailed source-code truncated for AI context efficiency. */

Update the UITableView Directly Without a Notification

Updating the UITableView directly without waiting for a notification
provides the most responsive UI. This code updates the TableView immediately
instead of requiring hops between threads, which add a small amount of
lag to each update. The downside is that it requires frequent manual
updates to the view.

swift
func delete(at index: Int) throws {
    try realm.write(withoutNotifying: [notificationToken]) {
        realm.delete(results[index])
    }
    tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}

Force a Refresh After a Write

Forcing a refresh() after a write provides the notifications from the
write immediately rather than on a future run of the run loop. There's no
window for the TableView to read out-of-sync values.

The downside is that this means things we recommend doing in the background,
such as writing, rerunning the query and re-sorting the results, happen
on the main thread. When these operations are computationally expensive,
this can cause delays on the main thread.

swift
func delete(at index: Int) throws {
    try realm.write {
        realm.delete(results[index])
    }
    realm.refresh()
}

Perform the Write on a Background Thread

Performing a write on a background thread blocks the main thread for the
least amount of time. However, the code to perform a write on the background
requires more familiarity with Realm's threading model and Swift DispatchQueue
usage. Since the write doesn't happen on the main thread, the main thread
never sees the write before the notifications arrive.

swift
func delete(at index: Int) throws {
    func delete(at index: Int) throws {
        @ThreadSafe var object = results[index]
        DispatchQueue.global().async {
            guard let object = object else { return }
            let realm = object.realm!
            try! realm.write {
                if !object.isInvalidated {
                    realm.delete(object)
                }
            }
        }
    }
}

Change Notification Limits

Changes in nested documents deeper than four levels down do not trigger
change notifications.

If you have a data structure where you need to listen for changes five
levels down or deeper, workarounds include:

  • Refactor the schema to reduce nesting.
  • Add something like "push-to-refresh" to enable users to manually refresh data.

In the Swift SDK, you can also use
key path filtering to work
around this limitation. This feature is not available in the other SDKs.


Guides Crud Read (`docs/guides/crud/read.md`)

CRUD - Read - Swift SDK

Read from a Realm

A read from a realm generally consists of the following
steps:

  • Get all objects of a certain type from the realm.
  • Optionally, filter the results.
  • Optionally, sort the results.
  • Alternately, get all objects of a certain type, divided into
    sections. As with regular results, you can
    filter and sort sectioned results.

Query, filter, and sort operations return either a results or
SectionedResults collection. These
collections are live, meaning they always contain the latest
results of the associated query.

Read Characteristics

When you design your app's data access patterns around the
following three key characteristics of reads in Realm,
you can be confident you are reading data as
efficiently as possible.

Results Are Not Copies

Results to a query are not copies of your data: modifying
the results of a query will modify the data on disk
directly. This memory mapping also means that results are
live: that is, they always reflect the current state on
disk.

See also: Collections are Live.

Results Are Lazy

Realm only runs a query when you actually request the
results of that query. This lazy evaluation enables you to write
elegant, highly performant code for handling large data sets and complex
queries. You can chain several filter and sort operations without requiring extra work to process the
intermediate state.

References Are Retained

One benefit of Realm's object model is that
Realm automatically retains all of an object's
relationships as direct
references, so you can traverse your graph of relationships
directly through the results of a query.

A direct reference, or pointer, allows you to access a
related object's properties directly through the reference.

Other databases typically copy objects from database storage
into application memory when you need to work with them
directly. Because application objects contain direct
references, you are left with a choice: copy the object
referred to by each direct reference out of the database in
case it's needed, or just copy the foreign key for each
object and query for the object with that key if it's
accessed. If you choose to copy referenced objects into
application memory, you can use up a lot of resources for
objects that are never accessed, but if you choose to only
copy the foreign key, referenced object lookups can cause
your application to slow down.

Realm bypasses all of this using zero-copy
live objects. Realm object accessors point
directly into database storage using memory mapping, so there is no
distinction between the objects in Realm and the results
of your query in application memory. Because of this, you can traverse
direct references across an entire realm from any query result.

Limiting Query Results

As a result of lazy evaluation, you do not need any special mechanism to
limit query results with Realm. For example, if your query
matches thousands of objects, but you only want to load the first ten,
simply access only the first ten elements of the results collection.

Pagination

Thanks to lazy evaluation, the common task of pagination becomes quite
simple. For example, suppose you have a results collection associated
with a query that matches thousands of objects in your realm. You
display one hundred objects per page. To advance to any page, simply
access the elements of the results collection starting at the index that
corresponds to the target page.

Read Realm Objects

About The Examples On This Page

The examples on this page use the following models:

Objective-C

objectivec
// DogToy.h
@interface DogToy : RLMObject
@property NSString *name;
@end

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@property int age;
@property NSString *color;

// To-one relationship
@property DogToy *favoriteToy;

@end

// Enable Dog for use in RLMArray
RLM_COLLECTION_TYPE(Dog)

// Person.h
// A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs.
@interface Person : RLMObject
@property int _id;
@property NSString *name;

// To-many relationship - a person can have many dogs
@property RLMArray<Dog *><Dog> *dogs;

// Inverse relationship - a person can be a member of many clubs
@property (readonly) RLMLinkingObjects *clubs;
@end

RLM_COLLECTION_TYPE(Person)

// DogClub.h
@interface DogClub : RLMObject
@property NSString *name;
@property RLMArray<Person *><Person> *members;
@end

// Dog.m
@implementation Dog
@end

// DogToy.m
@implementation DogToy
@end

// Person.m
@implementation Person
// Define the primary key for the class
+ (NSString *)primaryKey {
    return @"_id";
}

// Define the inverse relationship to dog clubs
+ (NSDictionary *)linkingObjectsProperties {
    return @{
        @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"],
    };
}
@end

// DogClub.m
@implementation DogClub
@end

Swift

text
/* Detailed source-code truncated for AI context efficiency. */

Find a Specific Object by Primary Key

Objective-C

If you know the primary key for
a given object, you can look it up directly with
+[RLMObject objectForPrimaryKey:].

objectivec
// Get a specific person from the default realm
Person *specificPerson = [Person objectForPrimaryKey:@12345];

Swift

If you know the primary key for a given
object, you can look it up directly with
Realm.object(ofType:forPrimaryKey:).

swift
let realm = try! Realm()

let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345)

Query All Objects of a Given Type

Objective-C

To query for objects of a given type in a realm, pass the realm
instance to +[YourRealmObjectClass allObjectsInRealm:].
Replace YourRealmObjectClass with your Realm object class
name. This returns an RLMResults object representing all objects of the
given type in the realm.

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

RLMResults *dogs = [Dog allObjectsInRealm:realm];
RLMResults *people = [Person allObjectsInRealm:realm];

Swift

To query for objects of a given type in a realm, pass the metatype
instance YourClassName.self to Realm.objects(_).
This returns a Results object
representing all objects of the given type in the realm.

swift
let realm = try! Realm()
// Access all dogs in the realm
let dogs = realm.objects(Dog.self)

Filter Queries Based on Object Properties

A filter selects a subset of results based on the value(s) of one or
more object properties. Realm provides a full-featured
query engine that you can use to define filters.

Swift

Version added: 10.19.0

To use the Realm Swift Query API,
call .where with a closure that
contains a query expression as an argument.

swift
let realm = try! Realm()
// Access all dogs in the realm
let dogs = realm.objects(Dog.self)

// Query by age
let puppies = dogs.where {
    $0.age < 2
}

// Query by person
let dogsWithoutFavoriteToy = dogs.where {
    $0.favoriteToy == nil
}

// Query by person's name
let dogsWhoLikeTennisBalls = dogs.where {
    $0.favoriteToy.name == "Tennis ball"
}

Swift Nspredicate

To filter, call Results.filter(_:)
with a query predicate.

swift
let realm = try! Realm()
// Access all dogs in the realm
let dogs = realm.objects(Dog.self)

// Filter by age
let puppies = dogs.filter("age < 2")

// Filter by person
let dogsWithoutFavoriteToy = dogs.filter("favoriteToy == nil")

// Filter by person's name
let dogsWhoLikeTennisBalls = dogs.filter("favoriteToy.name == 'Tennis ball'")

Objective-C

To filter, call -[RLMResults objectsWhere:]
with a query predicate.

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

// Access all dogs in the realm
RLMResults *dogs = [Dog allObjectsInRealm:realm];

// Filter by age
RLMResults *puppies = [dogs objectsWhere:@"age < 2"];

// Filter by favorite toy
RLMResults *dogsWithoutFavoriteToy = [dogs objectsWhere:@"favoriteToy == nil"];

// Filter by favorite toy's name
RLMResults *dogsWhoLikeTennisBalls = [dogs objectsWhere:@"favoriteToy.name == %@", @"Tennis ball"];

Tip:
To filter a query based on a property of an embedded object or a related object, use dot-notation as if it were
in a regular, nested object.

Filter on Object ID Properties

The types in your predicate must match the types of the
properties. Avoid comparing
ObjectId properties to strings, as
Realm does not automatically convert strings to ObjectIds.

Swift

Version added: 10.19.0

The Realm Swift Query API's built-in type safety simplifies writing a
query with an ObjectId:

swift
let realm = try! Realm()

let dogToys = realm.objects(DogToy.self)

// Get specific user by ObjectId id
let specificToy = dogToys.where {
    $0.id == ObjectId("11223344556677889900aabb")
}

Swift Nspredicate

The following example shows the correct and incorrect way to write a
query with an ObjectId given the following Realm object:

swift
let realm = try! Realm()

let dogToys = realm.objects(DogToy.self)

// Get specific toy by ObjectId id
let specificToy = dogToys.filter("id = %@", ObjectId("11223344556677889900aabb")).first

// WRONG: Realm will not convert the string to an object id
// users.filter("id = '11223344556677889900aabb'") // not ok
// users.filter("id = %@", "11223344556677889900aabb") // not ok

Query a Relationship

You can query through a relationship the same way you would access a
member of a regular Swift or Objective-C object.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

// Establish a relationship
Dog *dog = [[Dog alloc] init];
dog.name = @"Rex";
dog.age = 10;

Person *person = [[Person alloc] init];
person._id = 12345;
[person.dogs addObject:dog];

[realm transactionWithBlock:^() {
    [realm addObject:person];
}];

// Later, query the specific person
Person *specificPerson = [Person objectForPrimaryKey:@12345];

// Access directly through a relationship
NSLog(@"# dogs: %lu", [specificPerson.dogs count]);
NSLog(@"First dog's name: %@", specificPerson.dogs[0].name);

Swift

swift
let realm = try! Realm()

// Establish a relationship
let dog = Dog()
dog.name = "Rex"
dog.age = 10

let person = Person()
person.id = 12345
person.dogs.append(dog)

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

// Later, query the specific person
let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345)

// Access directly through a relationship
let specificPersonDogs = specificPerson!.dogs
let firstDog = specificPersonDogs[0]
print("# dogs: \(specificPersonDogs.count)")
print("First dog's name: \(firstDog.name)")

Query an Inverse Relationship

You can query through an inverse relationship the same way you would
access a member of a regular Swift or Objective-C object.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

// Establish a relationship
Person *person = [[Person alloc] init];
person._id = 12345;

DogClub *club = [[DogClub alloc] init];
club.name = @"Pooch Pals";
[club.members addObject:person];

[realm transactionWithBlock:^() {
    [realm addObject:club];
}];

// Later, query the specific person
Person *specificPerson = [Person objectForPrimaryKey:@12345];
    
// Access directly through an inverse relationship
NSLog(@"# memberships: %lu", [specificPerson.clubs count]);
NSLog(@"First club's name: %@", [specificPerson.clubs[0] name]);

Swift

swift
let realm = try! Realm()

// Establish an inverse relationship
let person = Person()
person.id = 12345

let club = DogClub()
club.name = "Pooch Pals"
club.members.append(person)

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

// Later, query the specific person
let specificPerson = realm.object(ofType: Person.self, forPrimaryKey: 12345)

// Access directly through an inverse relationship
let clubs = specificPerson!.clubs
let firstClub = clubs[0]
print("# memberships: \(clubs.count)")
print("First club's name: \(firstClub.name)")

Query a Collection on Embedded Object Properties

Use dot notation to filter or sort a collection of objects based on an embedded object
property value:

Note:
It is not possible to query embedded objects directly. Instead,
access embedded objects through a query for the parent object type.

Swift

Version added: 10.19.0

swift
// Open the default realm
let realm = try! Realm()

// Get all contacts in Los Angeles, sorted by street address
let losAngelesPeople = realm.objects(Person.self)
    .where {
        $0.address.city == "Los Angeles"
    }
    .sorted(byKeyPath: "address.street")
print("Los Angeles Person: \(losAngelesPeople)")

Swift Nspredicate

swift
// Open the default realm
let realm = try! Realm()

// Get all people in Los Angeles, sorted by street address
let losAngelesPeople = realm.objects(Person.self)
    .filter("address.city = %@", "Los Angeles")
    .sorted(byKeyPath: "address.street")
print("Los Angeles Person: \(losAngelesPeople)")

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
RLMResults<Contact *> *losAngelesContacts = [Contact objectsInRealm:realm where:@"address.city = %@", @"Los Angeles"];

losAngelesContacts = [losAngelesContacts sortedResultsUsingKeyPath:@"address.street" ascending:YES]; 
NSLog(@"Los Angeles Contacts: %@", losAngelesContacts);

Query a Map Property

You can iterate and check the values of a realm map
as you would a standard Dictionary:

swift
let realm = try! Realm()

let dogs = realm.objects(Dog.self)

// Find dogs who have favorite parks
let dogsWithFavoriteParks = dogs.where {
    $0.favoriteParksByCity.count >= 1
}

for dog in dogsWithFavoriteParks {
    // Check if an entry exists
    if dog.favoriteParksByCity.keys.contains("Chicago") {
        print("\(dog.name) has a favorite park in Chicago")
    }

    // Iterate over entries
    for element in dog.favoriteParksByCity {
        print("\(dog.name)'s favorite park in \(element.key) is \(element.value)")
    }
}

Query a MutableSet Property

You can query a MutableSet to check if
it contains an element. If you are working with multiple sets, you can
check for the intersection of two sets, or check whether one set is a subset
of the other set.

swift
let realm = try! Realm()

// Find dogs who have visited New York
let newYorkDogs = realm.objects(Dog.self).where {
    $0.citiesVisited.contains("New York")
}

// Get some information about the cities they have visited
for dog in newYorkDogs {
    print("Cities \(dog.name) has visited: \(dog.citiesVisited)")
}

// Check whether two dogs have visited some of the same cities.
// Use "intersects" to find out whether the values of the two sets share common elements.
let isInBothCitiesVisited = (dog.citiesVisited.intersects(dog2.citiesVisited))
print("The two dogs have visited some of the same cities: \(isInBothCitiesVisited)")
// Prints "The two dogs have visited some of the same cities: true"

// Or you can check whether a set is a subset of another set. In this example,
// the first dog has visited "New York" and "Toronto", while dog2 has visited both of
// those but also "Toronto" and "Boston".
let isSubset = (dog.citiesVisited.isSubset(of: dog2.citiesVisited))
print("\(dog.name)'s set of cities visited is a subset of \(dog2.name)'s: \(isSubset)")
// Prints "Maui's set of cities visited is a subset of Lita's: true"

Read and Query AnyRealmValue Property

When you read an AnyRealmValue property, check the value's type before doing
anything with it. The Realm Swift SDK provides an AnyRealmValue enum that iterates through all of the types the
AnyRealmValue can store.

swift
let realm = try! Realm()

let dogs = realm.objects(Dog.self)

for dog in dogs {
    // Verify the type of the ``AnyRealmProperty`` when attempting to get it. This
    // returns an object whose property contains the matched type.

    // If you only care about one type, check for that type.
    if case let .string(companion) = dog.companion {
        print("\(dog.name)'s companion is: \(companion)")
        // Prints "Wolfie's companion is: Fluffy the Cat"
    }

    // Or if you want to do something with multiple types of data
    // that could be in the value, switch on the type.
    switch dog.companion {
    case .string:
        print("\(dog.name)'s companion is: \(dog.companion)")
        // Prints "Wolfie's companion is: string("Fluffy the Cat")
    case .object:
        print("\(dog.name)'s companion is: \(dog.companion)")
        // Prints "Fido's companion is: object(Dog { name = Spot })"
    case .none:
        print("\(dog.name) has no companion")
        // Prints "Rex has no companion" and "Spot has no companion"
    default:
        print("\(dog.name)'s companion is another type.")
    }
}

You can compare these mixed value types:

  • Numeric: int, bool, float, double, decimal
  • Byte-based: string, binary
  • Time-based: timestamp, objectId

When using the AnyRealmValue mixed data type, keep these things in mind:

  • equals queries match on value and type
  • not equals queries match objects with either different values or
    different types
  • realm converts comparable numeric properties where possible. For example,
    in a mixed type field, 1 matches all of 1.0, 1, and true.
  • String properties do not match numeric queries. For example, in a mixed
    type field, 1 does not match "1". "1" does not match 1, 1.0, or true.

Query Geospatial Data

Version added: 10.47.0

The Swift SDK provides several shapes to simplify querying
geospatial data. You can use the
GeoCircle, GeoBox, and GeoPolygon shapes to set the boundaries for
your geospatial data queries.

The SDK provides two specialized non-persistable data types to define shapes:

  • GeoPoint: A struct that represents the coordinates of a point formed by
    a pair of doubles consisting of these values: Latitude: ranges between -90 and 90 degrees, inclusive.Longitude: ranges between -180 and 180 degrees, inclusive.
  • RLMDistance: A helper struct to represent and convert a distance.

Define Geospatial Shapes

Geocircle

A GeoCircle is a circular shape whose bounds originate from a central
GeoPoint, and has a size corresponding to a radius measured in
radians. You can use the SDK's convenience RLMDistance data type to
easily work with radii in different units.

RLMDistance enables you to specify the radius distance for your geo shapes
in one of four units:

  • .degrees
  • .kilometers
  • .miles
  • .radians

You can optionally use the supplied convenience methods to convert a
measurement to a different distance units.

swift
// You can create a GeoCircle radius measured in radians.
// This radian distance corresponds with 0.25 degrees.
let smallCircle = GeoCircle(center: (47.3, -121.9), radiusInRadians: 0.004363323)

// You can also create a GeoCircle radius measured with a Distance.
// You can specify a Distance in .degrees, .kilometers, .miles, or .radians.
let largeCircle = GeoCircle(center: GeoPoint(latitude: 47.8, longitude: -122.6)!, radius: Distance.kilometers(44.4)!)

Geobox

A GeoBox is a rectangular shape whose bounds are determined by
coordinates for a bottom-left and a top-right corner.

swift
let largeBox = GeoBox(bottomLeft: (47.3, -122.7), topRight: (48.1, -122.1))

let smallBoxBottomLeft = GeoPoint(latitude: 47.5, longitude: -122.4)!
let smallBoxTopRight = GeoPoint(latitude: 47.9, longitude: -121.8)
let smallBox = GeoBox(bottomLeft: smallBoxBottomLeft, topRight: smallBoxTopRight!)

Geopolygon

A GeoPolygon is a polygon shape whose bounds consist of an outer
ring, and 0 or more inner holes to exclude from the geospatial query.

A polygon's outer ring must contain at least three segments. The last
and the first GeoPoint must be the same, which indicates a closed
polygon. This means that it takes at least four GeoPoint values to
construct a polygon.

Inner holes in a GeoPolygon must be entirely contained within the
outer ring.

Holes have the following restrictions:

  • Holes may not cross. The boundary of a hole may not intersect both the
    interior and the exterior of any other hole.
  • Holes may not share edges. If a hole contains an edge AB, then no other
    hole may contain it.
  • Holes may share vertices. However, no vertex may appear twice in a
    single hole.
  • No hole may be empty.
  • Only one nesting is allowed.
swift
// Create a basic polygon
let basicPolygon = GeoPolygon(outerRing: [
    (48.0, -122.8),
    (48.2, -121.8),
    (47.6, -121.6),
    (47.0, -122.0),
    (47.2, -122.6),
    (48.0, -122.8)
])

// Create a polygon with one hole
let outerRing: [GeoPoint] = [
    GeoPoint(latitude: 48.0, longitude: -122.8)!,
    GeoPoint(latitude: 48.2, longitude: -121.8)!,
    GeoPoint(latitude: 47.6, longitude: -121.6)!,
    GeoPoint(latitude: 47.0, longitude: -122.0)!,
    GeoPoint(latitude: 47.2, longitude: -122.6)!,
    GeoPoint(latitude: 48.0, longitude: -122.8)!
]

let hole: [GeoPoint] = [
    GeoPoint(latitude: 47.8, longitude: -122.6)!,
    GeoPoint(latitude: 47.7, longitude: -122.2)!,
    GeoPoint(latitude: 47.4, longitude: -122.6)!,
    GeoPoint(latitude: 47.6, longitude: -122.5)!,
    GeoPoint(latitude: 47.8, longitude: -122.6)!
]

let polygonWithOneHole = GeoPolygon(outerRing: outerRing, holes: [hole])

// Add a second hole to the polygon
let hole2: [GeoPoint] = [
    GeoPoint(latitude: 47.55, longitude: -122.05)!,
    GeoPoint(latitude: 47.55, longitude: -121.9)!,
    GeoPoint(latitude: 47.3, longitude: -122.1)!,
    GeoPoint(latitude: 47.55, longitude: -122.05)!
]

let polygonWithTwoHoles = GeoPolygon(outerRing: outerRing, holes: [hole, hole2])

Query with Geospatial Shapes

You can then use these shapes in a geospatial query. You can query geospatial
data in three ways:

  • Using the .geoWithin() operator with the type-safe Realm Swift Query API
  • Using a .filter() with RQL
  • Using a .filter() with an NSPredicate query

The examples below show the results of queries using these two Company
objects:

swift
let company1 = Geospatial_Company()
company1.location = CustomGeoPoint(47.68, -122.35)

let company2 = Geospatial_Company(CustomGeoPoint(47.9, -121.85))

Geocircle

swift
let companiesInSmallCircle = realm.objects(Geospatial_Company.self).where {
    $0.location.geoWithin(smallCircle!)
}
print("Number of companies in small circle: \(companiesInSmallCircle.count)")

let companiesInLargeCircle = realm.objects(Geospatial_Company.self)
    .filter("location IN %@", largeCircle)
print("Number of companies in large circle: \(companiesInLargeCircle.count)")

Geobox

swift
let companiesInSmallBox = realm.objects(Geospatial_Company.self).where {
    $0.location.geoWithin(smallBox)
}
print("Number of companies in small box: \(companiesInSmallBox.count)")

let filterArguments = NSMutableArray()
filterArguments.add(largeBox)
let companiesInLargeBox = realm.objects(Geospatial_Company.self)
    .filter(NSPredicate(format: "location IN %@", argumentArray: filterArguments as? [Any]))
print("Number of companies in large box: \(companiesInLargeBox.count)")

Geopolygon

swift
let companiesInBasicPolygon = realm.objects(Geospatial_Company.self).where {
    $0.location.geoWithin(basicPolygon!)
}
print("Number of companies in basic polygon: \(companiesInBasicPolygon.count)")

let companiesInPolygonWithTwoHoles = realm.objects(Geospatial_Company.self).where {
    $0.location.geoWithin(polygonWithTwoHoles!)
}
print("Number of companies in polygon with two holes: \(companiesInPolygonWithTwoHoles.count)")

Query a Custom Persistable Property

When you use type projection to map unsupported
types to supported types, accessing those properties is often based on the
persisted type.

Queries on Realm Objects

When working with projected types, queries operate on the persisted type.
However, you can use the mapped types interchangeably with the persisted
types in arguments in most cases. The exception is queries on embedded
objects.

Tip:
Projected types support sorting and aggregates where
the persisted type supports them.

swift
let akcClub = realm.objects(Club.self).where {
    $0.name == "American Kennel Club"
}.first!
// You can use type-safe expressions to check for equality
XCTAssert(akcClub.url == URL(string: "https://akc.org")!)

let clubs = realm.objects(Club.self)
// You can use the persisted property type in NSPredicate query expressions
let akcByUrl = clubs.filter("url == 'https://akc.org'").first!
XCTAssert(akcByUrl.name == "American Kennel Club")

Queries on Embedded Objects

You can query embedded types on the supported property types within the
object using memberwise equality.

Object link properties support equality comparisons, but do not support
memberwise comparisons. You can query embedded objects for memberwise
equality on all primitive types. You cannot perform memberwise comparison
on objects and collections.

Dynamic APIs

Because the schema has no concept of custom type mappings, reading data via
any of the dynamic APIs gives the underlying persisted type. Realm does
support writing mapped types via a dynamic API, and converts the projected
type to the persisted type.

The most common use of the dynamic APIs is migration. You can write projected
types during migration, and Realm converts the projected type to the persisted
type. However, reading data during a migration gives the underlying persisted
type.

Read an Object Asynchronously

When you use an actor-isolated realm, you can use Swift concurrency features
to asynchronously query objects.

swift
let actor = try await RealmActor()

// Read objects in functions isolated to the actor and pass primitive values to the caller
func getObjectId(in actor: isolated RealmActor, forTodoNamed name: String) async -> ObjectId {
    let todo = actor.realm.objects(Todo.self).where {
        $0.name == name
    }.first!
    return todo._id
}
let objectId = await getObjectId(in: actor, forTodoNamed: "Keep it safe")

If you need to manually advance the state of an observed realm on the main
thread or an actor-isolated realm, call await realm.asyncRefresh().
This updates the realm and outstanding objects managed by the Realm to point to
the most recent data and deliver any applicable notifications.

For more information about working with realm using Swift concurrency features,
refer to Use Realm with Actors - Swift SDK.

Sort Query Results

A sort operation allows you to configure the order in which Realm
Database returns queried objects. You can sort based on one or more
properties of the objects in the results collection. Realm only
guarantees a consistent order of results if you explicitly sort them.

Objective-C

To sort, call -[RLMResults
sortedResultsUsingKeyPath:ascending:]

with the desired key path to sort by.

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

RLMResults *dogs = [Dog allObjectsInRealm:realm];

// Sort dogs by name
RLMResults *dogsSorted = [dogs sortedResultsUsingKeyPath:@"name" ascending:NO];

// You can also sort on the members of linked objects. In this example,
// we sort the dogs by their favorite toys' names.
RLMResults *dogsSortedByFavoriteToyName = [dogs sortedResultsUsingKeyPath:@"favoriteToy.name" ascending:YES];

Swift

Version added: 10.11.0

You can sort using the type-safe keyPath by calling
Results.sorted(by: ) with the
keyPath name and optional sort order:

swift
let realm = try! Realm()
// Access all dogs in the realm
let dogs = realm.objects(Dog.self)

// Sort by type-safe keyPath
let dogsSorted = dogs.sorted(by: \.name)

To sort using the older API, call Results.sorted(byKeyPath:ascending:)
with the desired key path to sort by.

swift
let realm = try! Realm()
// Access all dogs in the realm
let dogs = realm.objects(Dog.self)

let dogsSorted = dogs.sorted(byKeyPath: "name", ascending: false)

// You can also sort on the members of linked objects. In this example,
// we sort the dogs by their favorite toys' names.
let dogsSortedByFavoriteToyName = dogs.sorted(byKeyPath: "favoriteToy.name")

Tip:
To sort a query based on a property of an embedded object or a related object, use dot-notation as if it
were in a regular, nested object.

Note:
String sorting and case-insensitive queries are only supported for
character sets in 'Latin Basic', 'Latin Supplement', 'Latin Extended
A', and 'Latin Extended B' (UTF-8 range 0-591).

Section Query Results

You can split results into individual sections. Each section corresponds
to a key generated from a property on the object it represents.

For example, you might add a computed variable to your object to get the
first letter of the name property:

swift
// Computed variable that is not persisted, but only
// used to section query results.
var firstLetter: String {
    return name.first.map(String.init(_:)) ?? ""
}

Then, you can create a SectionedResults
type-safe collection for that object, and use it to retrieve objects sectioned
by that computed variable:

swift
var dogsByFirstLetter: SectionedResults<String, Dog>

dogsByFirstLetter = realm.objects(Dog.self).sectioned(by: \.firstLetter, ascending: true)

You can get a count of the sections, get a list of keys, or access an individual
ResultSection by index:

swift
let realm = try! Realm()

var dogsByFirstLetter: SectionedResults<String, Dog>

dogsByFirstLetter = realm.objects(Dog.self).sectioned(by: \.firstLetter, ascending: true)

// You can get a count of the sections in the SectionedResults
let sectionCount = dogsByFirstLetter.count

// Get an array containing all section keys for objects that match the query.
let sectionKeys = dogsByFirstLetter.allKeys
// This example realm contains 4 dogs, "Rex", "Wolfie", "Fido", "Spot".
// Prints ["F", "R", "S", "W"]
print(sectionKeys)

// Get a specific key by index position
let sectionKey = dogsByFirstLetter[0].key
// Prints "Key for index 0: F"
print("Key for index 0: \(sectionKey)")

// You can access Results Sections by the index of the key you want in SectionedResults.
// "F" is the key at index position 0. When we access this Results Section, we get dogs whose name begins with "F".
let dogsByF = dogsByFirstLetter[0]
// Prints "Fido"
print(dogsByF.first?.name)

You can also section using a callback. This enables you to section a
collection of primitives, or have more control over how the section key is
generated.

swift
let realm = try! Realm()
let results = realm.objects(Dog.self)
let sectionedResults = results.sectioned(by: { String($0.name.first!) },
                                         sortDescriptors: [SortDescriptor.init(keyPath: "name", ascending: true)])
let sectionKeys = sectionedResults.allKeys

You can observe
SectionedResults and ResultsSection instances, and both conform to
ThreadConfined.

Aggregate Data

You can use Realm's aggregation operators for sophisticated queries
against list properties.

Swift

Version added: 10.19.0

swift
let realm = try! Realm()

let people = realm.objects(Person.self)

// People whose dogs' average age is 5
people.where {
    $0.dogs.age.avg == 5
}

// People with older dogs
people.where {
    $0.dogs.age.min > 5
}

// People with younger dogs
people.where {
    $0.dogs.age.max < 2
}

// People with many dogs
people.where {
    $0.dogs.count > 2
}

// People whose dogs' ages combined > 10 years
people.where {
    $0.dogs.age.sum > 10
}

Swift Nspredicate

swift
let realm = try! Realm()

let people = realm.objects(Person.self)

// People whose dogs' average age is 5
people.filter("[email protected] == 5")

// People with older dogs
people.filter("[email protected] > 5")

// People with younger dogs
people.filter("[email protected] < 2")

// People with many dogs
people.filter("dogs.@count > 2")

// People whose dogs' ages combined > 10 years
people.filter("[email protected] > 10")

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

RLMResults *people = [Person allObjectsInRealm:realm];

// People whose dogs' average age is 5
[people objectsWhere:@"[email protected] == 5"];

// People with older dogs
[people objectsWhere:@"[email protected] > 5"];

// People with younger dogs
[people objectsWhere:@"[email protected] < 2"];

// People with many dogs
[people objectsWhere:@"dogs.@count > 2"];

// People whose dogs' ages combined > 10 years
[people objectsWhere:@"[email protected] > 10"];

Chain Queries

Because results are lazily evaluated, you
can chain several queries together. Unlike traditional databases, this
does not require a separate trip to the database for each successive
query.

Example:
To get a result set for tan dogs, and tan dogs whose names start with
'B', chain two queries like this:

Swift

Version added: 10.19.0

swift
let realm = try! Realm()
let tanDogs = realm.objects(Dog.self).where {
    $0.color == "tan"
}
let tanDogsWithBNames = tanDogs.where {
    $0.name.starts(with: "B")
}

Swift Nspredicate

swift
let realm = try! Realm()
let tanDogs = realm.objects(Dog.self).filter("color = 'tan'")
let tanDogsWithBNames = tanDogs.filter("name BEGINSWITH 'B'")

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
RLMResults<Dog *> *tanDogs = [Dog objectsInRealm:realm where:@"color = 'tan'"];
RLMResults<Dog *> *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];

Query Class Projections

To query for class projections in a
realm, pass the metatype instance YourProjectionName.self to
Realm.objects(_).
This returns a Results object
representing all of the class projection objects in the realm.

swift
// Retrieve all class projections of the given type `PersonProjection`
let people = realm.objects(PersonProjection.self)
// Use projection data in your view
print(people.first?.firstName)
print(people.first?.homeCity)
print(people.first?.firstFriendsName)

Tip:
Don't do derived queries on top of class projection results. Instead, run a
query against the Realm object directly and then project the result. If you
try to do a derived query on top of class projection results, querying a
field with the same name and type as the original object works, but querying
a field with a name or type that isn't in the original object fails.


Guides Crud Threading (`docs/guides/crud/threading.md`)

Threading - Swift SDK

To make your iOS and tvOS apps fast and responsive, you must
balance the computing time needed to lay out the visuals and
handle user interactions with the time needed to process
your data and run your business logic. Typically, app
developers spread this work across multiple threads: the
main or UI thread for all of the user interface-related
work, and one or more background threads to compute heavier
workloads before sending it to the UI thread for
presentation. By offloading heavy work to background
threads, the UI thread can remain highly responsive
regardless of the size of the workload. But it can be
notoriously difficult to write thread-safe, performant, and
maintainable multithreaded code that avoids issues like
deadlocking and race conditions. Realm aims to
simplify this for you.

Seealso:
As of 10.26.0, Realm provides async write methods to perform background
writes. See: Perform a Background Write. With
async write, you don't need to pass a thread-safe reference or frozen objects
across threads.

This page describes how to manually manage realm files and objects across threads.
Realm also supports using a Swift actor
to manage realm access using Swift concurrency features. For an overview
of Realm's actor support, refer to Use Realm with Actors - Swift SDK.

Three Rules to Follow

Before exploring Realm's tools for multithreaded apps, you need to
understand and follow these three rules:

Realm's Multiversion Concurrency Control (MVCC)
architecture eliminates the need to lock for read operations. The values you
read will never be corrupted or in a partially-modified state. You can freely
read from the same Realm file on any thread without the need for locks or
mutexes. Unnecessarily locking would be a performance bottleneck since each
thread might need to wait its turn before reading.

You can write to a Realm file from any thread, but there can be only one
writer at a time. Consequently, synchronous write transactions block each
other. A synchronous write on the UI thread may result in your app appearing
unresponsive while it waits for a write on a background thread to complete.

Live objects, collections, and realm instances are
thread-confined: that is, they are only valid on the
thread on which they were created. Practically speaking,
this means you cannot pass live instances to other
threads. However, Realm offers several mechanisms for
sharing objects across threads.

Perform a Background Write

Version added: 10.26.0

You can add, modify, or delete objects in the background using
writeAsync.

With writeAsync, you don't need to pass a thread-safe reference or frozen objects
across threads. Instead, call realm.writeAsync. You can provide
a completion block for the method to execute on the source thread after
the write completes or fails.

Things to consider when performing background writes:

  • Async writes block closing or invalidating the realm
  • You can explicitly commit or cancel transactions
swift
let realm = try! Realm()

// Query for a specific person object on the main thread
let people = realm.objects(Person.self)
let thisPerson = people.where {
    $0.name == "Dachary"
}.first

// Perform an async write to add dogs to that person's dog list.
// No need to pass a thread-safe reference or frozen object.
realm.writeAsync {
    thisPerson?.dogs.append(objectsIn: [
        Dog(value: ["name": "Ben", "age": 13]),
        Dog(value: ["name": "Lita", "age": 9]),
        Dog(value: ["name": "Maui", "age": 1])
    ])
} onComplete: { _ in
    // Confirm the three dogs were successfully added to the person's dogs list
    XCTAssertEqual(thisPerson!.dogs.count, 3)
    // Query for one of the dogs we added and see that it is present
    let dogs = realm.objects(Dog.self)
    let benDogs = dogs.where {
        $0.name == "Ben"
    }
    XCTAssertEqual(benDogs.count, 1)
}

Wait for Async Writes to Complete

The SDK provides a Bool to signal whether the realm is currently
performing an async write. The
isPerformingAsynchronousWriteOperations
variable becomes true after a call to one of:

  • writeAsync
  • beginAsyncWrite
  • commitAsyncWrite

It remains true until all scheduled async write operations have completed.
While this is true, this blocks closing or invalidating the realm.

Commit or Cancel an Async Write

To complete an async write, you or the SDK must call either:

  • commitAsyncWrite
  • cancelAsyncWrite

When you use the writeAsync method, the SDK handles committing or
canceling the transaction. This provides the convenience of the async write
without the need to manually keep state tied to the scope of the object.
However, while in the writeAsync block, you can explicitly call
commitAsyncWrite or cancelAsyncWrite. If you return without
calling one of these methods, writeAsync either:

  • Commits the write after executing the instructions in the write block
  • Returns an error

In either case, this completes the writeAsync operation.

For more control over when to commit or cancel the async write transaction,
use the beginAsyncWrite method. When you use this method, you must
explicitly commit the transactions. Returning without committing an async
write cancels the transaction. beginAsyncWrite returns an ID that you
can pass to cancelAsyncWrite.

commitAsyncWrite asynchronously commits a write transaction. This is
the step that persists the data to the realm. commitAsyncWrite can
take an onComplete block. . This block executes on the source thread
once the commit completes or fails with an error.

Calling commitAsyncWrite immediately returns. This allows the caller
to proceed while the SDK performs the I/O on a background thread. This method
returns an ID that you can pass to cancelAsyncWrite. This cancels the
pending invocation of the completion block. It does not cancel the commit
itself.

You can group sequential calls to commitAsyncWrite. Batching these calls
improves write performance; particularly when the batched transactions are
small. To permit grouping transactions, set the isGroupingAllowed
parameter to true.

You can call cancelAsyncWrite on either beginAsyncWrite or
commitAsyncWrite. When you call it on beginAsyncWrite, this cancels
the entire write transaction. When you call it on commitAsyncWrite, this
cancels only an onComplete block you may have passed to
commitAsyncWrite. It does not cancel the commit itself. You need the ID
of the beginAsyncWrite or the commitAsyncWrite you want to cancel.

Communication Across Threads

To access the same Realm file from different threads, you must instantiate a
realm instance on every thread that needs access. As long as you specify the same
configuration, all realm instances will map to the same file on disk.

One of the key rules when working with Realm in a multithreaded
environment is that objects are thread-confined: you cannot access the
instances of a realm, collection, or object that originated on other threads.

Realm's Multiversion Concurrency Control (MVCC)
architecture means that there could be many active versions of an object at any
time. Thread-confinement ensures that all instances in that thread are of the
same internal version.

When you need to communicate across threads, you have several options depending
on your use case:

  • To modify an object on two threads, query
    for the object on both threads.
  • To react to changes made on any thread, use Realm's
    notifications.
  • To see changes that happened on another thread in the current thread's realm
    instance, refresh your realm instance.
  • To send a fast, read-only view of the object to other threads,
    "freeze" the object.
  • To keep and share many read-only views of the object in your app, copy
    the object from the realm.
  • To share an instance of a realm or specific object with another thread or
    across actor boundaries, share a thread-safe reference to the realm instance or object. For more
    information, refer to Pass a ThreadSafeReference.

Create a Serial Queue to use Realm on a Background Thread

When using Realm on a background thread, create a serial queue. Realm
does not support using realms in concurrent queues, such as the global()
queue.

swift
// Initialize a serial queue, and
// perform realm operations on it
let serialQueue = DispatchQueue(label: "serial-queue")
serialQueue.async {
    let realm = try! Realm(configuration: .defaultConfiguration, queue: serialQueue)
    // Do something with Realm on the non-main thread
}

Pass Instances Across Threads

Instances of Realm, Results, List, and managed Objects
are thread-confined. That means you may only use them on the thread
where you created them. However, Realm provides a mechanism called
thread-safe references that allows you to copy an instance
created on one thread to another thread.

Sendable Conformance

Version added: 10.20.0
@ThreadSafe wrapper and ThreadSafeReference conform to Sendable

If you are using Swift 5.6 or higher, both the @ThreadSafe property wrapper and
ThreadSafeReference
conform to Sendable.

Use the @ThreadSafe Wrapper

Version added: 10.17.0

You can pass thread-confined instances to another thread as follows:

  1. Use the @ThreadSafe property wrapper to declare a variable that references the original object. By definition, @ThreadSafe-wrapped variables are always optional.
  2. Pass the @ThreadSafe-wrapped variable to the other thread.
  3. Use the @ThreadSafe-wrapped variable as you would any optional. If the referenced object is removed from the realm, the referencing variable becomes nil.
swift
let realm = try! Realm()

let person = Person(name: "Jane")
try! realm.write {
    realm.add(person)
}

// Create thread-safe reference to person
@ThreadSafe var personRef = person

// @ThreadSafe vars are always optional. If the referenced object is deleted,
// the @ThreadSafe var will be nullified.
print("Person's name: \(personRef?.name ?? "unknown")")

// Pass the reference to a background thread
DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async {
    let realm = try! Realm()
    try! realm.write {
        // Resolve within the transaction to ensure you get the
        // latest changes from other threads. If the person
        // object was deleted, personRef will be nil.
        guard let person = personRef else {
            return // person was deleted
        }
        person.name = "Jane Doe"
    }
}

Another way to work with an object on another thread is to query for it
again on that thread. But if the object does not have a primary
key, it is not trivial to
query for it. You can use the @ThreadSafe wrapper on any object,
regardless of whether it has a primary key.

Example:
The following example shows how to use @ThreadSafe on a function
parameter. This is useful for functions that may run asynchronously
or on another thread.

Tip:
If your app accesses Realm in an async/await context, mark the code
with @MainActor to avoid threading-related crashes.

Use ThreadSafeReference (Legacy Swift / Objective-C)

Before Realm Swift SDK version 10.17.0 or in Objective-C, you can pass
thread-confined instances to another thread as follows:

  1. Initialize a ThreadSafeReference with the thread-confined object.
  2. Pass the reference to the other thread or queue.
  3. Resolve the reference on the other thread's realm by calling Realm.resolve(_:). Use the returned object as normal.

Important:
You must resolve a ThreadSafeReference exactly once. Otherwise,
the source realm remains pinned until the reference gets
deallocated. For this reason, ThreadSafeReference should be
short-lived.

swift
let person = Person(name: "Jane")
let realm = try! Realm()

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

// Create thread-safe reference to person
let personRef = ThreadSafeReference(to: person)

// Pass the reference to a background thread
DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async {
    let realm = try! Realm()
    try! realm.write {
        // Resolve within the transaction to ensure you get the latest changes from other threads
        guard let person = realm.resolve(personRef) else {
            return // person was deleted
        }
        person.name = "Jane Doe"
    }
}

Another way to work with an object on another thread is to query for it
again on that thread. But if the object does not have a primary
key, it is not trivial to
query for it. You can use ThreadSafeReference on any object,
regardless of whether it has a primary key. You can also use it with
lists and results.

The downside is that ThreadSafeReference requires some boilerplate.
You must remember to wrap everything in a DispatchQueue with a
properly-scoped autoreleaseFrequency so the objects do not linger on
the background thread. So, it can be helpful to make a convenience extension
to handle the boilerplate as follows:

swift
extension Realm {
    func writeAsync<T: ThreadConfined>(_ passedObject: T, errorHandler: @escaping ((_ error: Swift.Error) -> Void) = { _ in return }, block: @escaping ((Realm, T?) -> Void)) {
        let objectReference = ThreadSafeReference(to: passedObject)
        let configuration = self.configuration
        DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async {
            do {
                let realm = try Realm(configuration: configuration)
                try realm.write {
                    // Resolve within the transaction to ensure you get the latest changes from other threads
                    let object = realm.resolve(objectReference)
                    block(realm, object)
                }
            } catch {
                errorHandler(error)
            }
        }
    }
}

This extension adds a writeAsync() method to the Realm class. This
method passes an instance to a background thread for you.

Example:
Suppose you made an email app and want to delete all read emails in
the background. You can now do it with two lines of code. Note that
the closure runs on the background thread and receives its own
version of both the realm and passed object:

swift
let realm = try! Realm()
let readEmails = realm.objects(Email.self).where {
    $0.read == true
}
realm.writeAsync(readEmails) { (realm, readEmails) in
    guard let readEmails = readEmails else {
        // Already deleted
        return
    }
    realm.delete(readEmails)
}

Use the Same Realm Across Threads

You cannot share realm instances across threads.

To use the same Realm file across threads, open a different realm
instance on each thread. As long as you use the same
configuration, all Realm
instances will map to the same file on disk.

Refreshing Realms

When you open a realm, it reflects the most recent successful write
commit and remains on that version until it is refreshed. This means
that the realm will not see changes that happened on another thread
until the next refresh. A realm on the UI thread -- more precisely,
on any event loop thread -- automatically refreshes itself at
the beginning of that thread's loop. However, you must manually refresh
realm instances that do not exist on loop threads or that have
auto-refresh disabled.

Objective-C

objective-c
if (![realm autorefresh]) {
    [realm refresh]
}

Swift

swift
if (!realm.autorefresh) {
   // Manually refresh
   realm.refresh()
}

Frozen Objects

Live, thread-confined objects work fine in most cases.
However, some apps -- those based on reactive, event
stream-based architectures, for example -- need to send
immutable copies around to many threads for processing
before ultimately ending up on the UI thread. Making a deep
copy every time would be expensive, and Realm does not allow
live instances to be shared across threads. In this case,
you can freeze and thaw objects, collections, and realms.

Freezing creates an immutable view of a specific object,
collection, or realm. The frozen object, collection, or realm still
exists on disk, and does not need to be deeply copied when passed around
to other threads. You can freely share the frozen object across threads
without concern for thread issues. When you freeze a realm, its child
objects also become frozen.

Tip:
Realm does not currently support using thaw() with Swift Actors.
To work with Realm data across actor boundaries, use
ThreadSafeReference instead of frozen objects. For more information,
refer to Pass a ThreadSafeReference.

Frozen objects are not live and do not automatically update.
They are effectively snapshots of the object state at the
time of freezing. Thawing an object returns a live version of the frozen
object.

Objective-C

objectivec
// Get an immutable copy of the realm that can be passed across threads
RLMRealm *frozenRealm = [realm freeze];

RLMResults *dogs = [Dog allObjectsInRealm:realm];

// You can freeze collections
RLMResults *frozenDogs = [dogs freeze];

// You can still read from frozen realms
RLMResults *frozenDogs2 = [Dog allObjectsInRealm:frozenRealm];

Dog *dog = [dogs firstObject];

// You can freeze objects
Dog *frozenDog = [dog freeze];

// To modify frozen objects, you can thaw them
// You can thaw collections
RLMResults *thawedDogs = [dogs thaw];

// You can thaw objects
Dog *thawedDog = [dog thaw];

// You can thaw frozen realms
RLMRealm *thawedRealm = [realm thaw];

Swift

swift
let realm = try! Realm()

// Get an immutable copy of the realm that can be passed across threads
let frozenRealm = realm.freeze()

assert(frozenRealm.isFrozen)

let people = realm.objects(Person.self)

// You can freeze collections
let frozenPeople = people.freeze()

assert(frozenPeople.isFrozen)

// You can still read from frozen realms
let frozenPeople2 = frozenRealm.objects(Person.self)

assert(frozenPeople2.isFrozen)

let person = people.first!

assert(!person.realm!.isFrozen)

// You can freeze objects
let frozenPerson = person.freeze()

assert(frozenPerson.isFrozen)
// Frozen objects have a reference to a frozen realm
assert(frozenPerson.realm!.isFrozen)

When working with frozen objects, an attempt to do any of
the following throws an exception:

  • Opening a write transaction on a frozen realm.
  • Modifying a frozen object.
  • Adding a change listener to a frozen realm, collection, or object.

You can use isFrozen to check if the object is frozen. This is always
thread-safe.

Objective-C

objective-c
if ([realm isFrozen]) {
    // ...
}

Swift

swift
if (realm.isFrozen) {
    // ...
}

Frozen objects remain valid as long as the live realm that
spawned them stays open. Therefore, avoid closing the live
realm until all threads are done with the frozen objects.
You can close a frozen realm before the live realm is closed.

Important:
Caching too many frozen objects can have a negative
impact on the realm file size. "Too many" depends on your
specific target device and the size of your Realm
objects. If you need to cache a large number of versions,
consider copying what you need out of the realm instead.

Modify a Frozen Object

To modify a frozen object, you must thaw the object. Alternately, you can
query for it on an unfrozen realm, then modify it. Calling thaw
on a live object, collection, or realm returns itself.

Thawing an object or collection also thaws the realm it references.

swift
// Read from a frozen realm
let frozenPeople = frozenRealm.objects(Person.self)

// The collection that we pull from the frozen realm is also frozen
assert(frozenPeople.isFrozen)

// Get an individual person from the collection
let frozenPerson = frozenPeople.first!

// To modify the person, you must first thaw it
// You can also thaw collections and realms
let thawedPerson = frozenPerson.thaw()

// Check to make sure this person is valid. An object is
// invalidated when it is deleted from its managing realm,
// or when its managing realm has invalidate() called on it.
assert(thawedPerson?.isInvalidated == false)

// Thawing the person also thaws the frozen realm it references
assert(thawedPerson!.realm!.isFrozen == false)

// Let's make the code easier to follow by naming the thawed realm
let thawedRealm = thawedPerson!.realm!

// Now, you can modify the todo
try! thawedRealm.write {
   thawedPerson!.name = "John Michael Kane"
}

Append to a Frozen Collection

When you append to a frozen collection,
you must thaw both the collection and the object that you want to append.
In this example, we query for two objects in a frozen Realm:

  • A Person object that has a List property
    of Dog objects
  • A Dog object

We must thaw both objects before we can append the Dog to
the Dog List collection on the Person. If we thaw only the Person object
but not the Dog, Realm throws an error.

The same rule applies when passing frozen objects across threads. A common
case might be calling a function on a background thread to do some work
instead of blocking the UI.

swift
// Get a copy of frozen objects.
// Here, we're getting them from a frozen realm,
// but you might also be passing them across threads.
let frozenTimmy = frozenRealm.objects(Person.self).where {
    $0.name == "Timmy"
}.first!
let frozenLassie = frozenRealm.objects(Dog.self).where {
    $0.name == "Lassie"
}.first!
// Confirm the objects are frozen.
assert(frozenTimmy.isFrozen == true)
assert(frozenLassie.isFrozen == true)
// Thaw the frozen objects. You must thaw both the object
// you want to append and the collection you want to append it to.
let thawedTimmy = frozenTimmy.thaw()
let thawedLassie = frozenLassie.thaw()
let realm = try! Realm()
try! realm.write {
    thawedTimmy?.dogs.append(thawedLassie!)
}
XCTAssertEqual(thawedTimmy?.dogs.first?.name, "Lassie")

Realm's Threading Model in Depth

Realm provides safe, fast, lock-free, and concurrent access
across threads with its Multiversion Concurrency
Control (MVCC)

architecture.

Compared and Contrasted with Git

If you are familiar with a distributed version control
system like Git, you may already
have an intuitive understanding of MVCC. Two fundamental
elements of Git are:

  • Commits, which are atomic writes.
  • Branches, which are different versions of the commit history.

Similarly, Realm has atomically-committed writes in the form
of transactions. Realm also has many
different versions of the history at any given time, like
branches.

Unlike Git, which actively supports distribution and
divergence through forking, a realm only has one true latest
version at any given time and always writes to the head of
that latest version. Realm cannot write to a previous
version. This means your data converges on one
latest version of the truth.

Internal Structure

A realm is implemented using a B+ tree data structure. The top-level node represents a
version of the realm; child nodes are objects in that
version of the realm. The realm has a pointer to its latest
version, much like how Git has a pointer to its HEAD commit.

Realm uses a copy-on-write technique to ensure
isolation and
durability.
When you make changes, Realm copies the relevant part of the
tree for writing. Realm then commits the changes in two
phases:

  • Realm writes changes to disk and verifies success.
  • Realm then sets its latest version pointer to point to the newly-written version.

This two-step commit process guarantees that even if the
write failed partway, the original version is not corrupted
in any way because the changes were made to a copy of the
relevant part of the tree. Likewise, the realm's root
pointer will point to the original version until the new
version is guaranteed to be valid.

Example:
The following diagram illustrates the commit process:

  1. The realm is structured as a tree. The realm has a pointer
    to its latest version, V1.
  1. When writing, Realm creates a new version V2 based on V1.
    Realm makes copies of objects for modification (A 1,
    C 1), while links to unmodified objects continue to
    point to the original versions (B, D).
  1. After validating the commit, Realm updates the
    pointer to the new latest version, V2. Realm then discards
    old nodes no longer connected to the tree.

ealm uses zero-copy techniques
like memory mapping to handle data. When you read a value
from the realm, you are virtually looking at the value on
the actual disk, not a copy of it. This is the basis for
live objects. This is also why a realm
head pointer can be set to point to the new version after
the write to disk has been validated.

Summary

  • Realm enables simple and safe multithreaded code when you follow
    these rules: don't lock to readavoid writes on the UI thread if you
    write on background threads, and don't pass live objects to other threads.
  • There is a proper way to share objects across threads for each use case.
  • In order to see changes made on other threads in your realm
    instance, you must manually refresh realm instances that do
    not exist on "loop" threads or that have auto-refresh disabled.
  • For apps based on reactive, event-stream-based architectures, you can
    freeze objects, collections, and realms in order to pass
    shallow copies around efficiently to different threads for processing.
  • Realm's multiversion concurrency control (MVCC)
    architecture is similar to Git's. Unlike Git, Realm has
    only one true latest version for each realm.
  • Realm commits in two stages to guarantee isolation and durability.

Sendable, Non-Sendable and Thread-Confined Types

The Realm Swift SDK public API contains types that fall into three broad
categories:

  • Sendable
  • Not Sendable and not thread confined
  • Thread-confined

You can share types that are not Sendable and not thread confined between
threads, but you must synchronize them.

Thread-confined types, unless frozen, are confined to an isolation context.
You cannot pass them between these contexts even with synchronization.


Guides Crud Update (`docs/guides/crud/update.md`)

CRUD - Update - Swift SDK

Update Realm Objects

Updates to Realm Objects must occur within write transactions. For
more information about write transactions, see: Transactions.

About The Examples On This Page

The examples on this page use the following models:

Objective-C

objectivec
// DogToy.h
@interface DogToy : RLMObject
@property NSString *name;
@end

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@property int age;
@property NSString *color;

// To-one relationship
@property DogToy *favoriteToy;

@end

// Enable Dog for use in RLMArray
RLM_COLLECTION_TYPE(Dog)

// Person.h
// A person has a primary key ID, a collection of dogs, and can be a member of multiple clubs.
@interface Person : RLMObject
@property int _id;
@property NSString *name;

// To-many relationship - a person can have many dogs
@property RLMArray<Dog *><Dog> *dogs;

// Inverse relationship - a person can be a member of many clubs
@property (readonly) RLMLinkingObjects *clubs;
@end

RLM_COLLECTION_TYPE(Person)

// DogClub.h
@interface DogClub : RLMObject
@property NSString *name;
@property RLMArray<Person *><Person> *members;
@end

// Dog.m
@implementation Dog
@end

// DogToy.m
@implementation DogToy
@end

// Person.m
@implementation Person
// Define the primary key for the class
+ (NSString *)primaryKey {
    return @"_id";
}

// Define the inverse relationship to dog clubs
+ (NSDictionary *)linkingObjectsProperties {
    return @{
        @"clubs": [RLMPropertyDescriptor descriptorWithClass:DogClub.class propertyName:@"members"],
    };
}
@end

// DogClub.m
@implementation DogClub
@end

Swift

swift
class Dog: Object {
    @Persisted var name = ""
    @Persisted var age = 0
    @Persisted var color = ""
    @Persisted var currentCity = ""
    @Persisted var citiesVisited: MutableSet<String>
    @Persisted var companion: AnyRealmValue

    // Map of city name -> favorite park in that city
    @Persisted var favoriteParksByCity: Map<String, String>
}

class Person: Object {
    @Persisted(primaryKey: true) var id = 0
    @Persisted var name = ""

    // To-many relationship - a person can have many dogs
    @Persisted var dogs: List<Dog>

    // Embed a single object.
    // Embedded object properties must be marked optional.
    @Persisted var address: Address?
}

class Address: EmbeddedObject {
    @Persisted var street: String?
    @Persisted var city: String?
    @Persisted var country: String?
    @Persisted var postalCode: String?
}

Update an Object

You can modify properties of a Realm object inside of a write
transaction in the same way that you would update any other Swift or
Objective-C object.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
// Open a thread-safe transaction.
[realm transactionWithBlock:^{
    // Get a dog to update.
    Dog *dog = [[Dog allObjectsInRealm: realm] firstObject];

    // Update some properties on the instance.
    // These changes are saved to the realm.
    dog.name = @"Wolfie";
    dog.age += 1;
}];

Swift

swift
let realm = try! Realm()

// Get a dog to update
let dog = realm.objects(Dog.self).first!

// Open a thread-safe transaction
try! realm.write {
    // Update some properties on the instance.
    // These changes are saved to the realm
    dog.name = "Wolfie"
    dog.age += 1
}

Tip:
To update a property of an embedded object or a related object, modify the property with
dot-notation or bracket-notation as if it were in a regular, nested
object.

Update Properties with Key-value Coding

Object, Result, and List all conform to
key-value coding.
This can be useful when you need to determine which property to update
at runtime.

Applying KVC to a collection is a great way to update objects in bulk.
Avoid the overhead of iterating over a collection while creating
accessors for every item.

swift
let realm = try! Realm()

let allDogs = realm.objects(Dog.self)

try! realm.write {
    allDogs.first?.setValue("Sparky", forKey: "name")
    // Move the dogs to Toronto for vacation
    allDogs.setValue("Toronto", forKey: "currentCity")
}

You can also add values for embedded objects or relationships this
way. In this example, we add a collection to an object's list property:

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];

[realm transactionWithBlock:^() {
    // Create a person to take care of some dogs.
    Person *ali = [[Person alloc] initWithValue:@{@"_id": @1, @"name": @"Ali"}];
    [realm addObject:ali];

    // Find dogs younger than 2.
    RLMResults<Dog *> *puppies = [Dog objectsInRealm:realm where:@"age < 2"];

    // Batch update: give all puppies to Ali.
    [ali setValue:puppies forKey:@"dogs"];
}];

Swift

swift
let realm = try! Realm()
try! realm.write {
    // Create a person to take care of some dogs.
    let person = Person(value: ["id": 1, "name": "Ali"])
    realm.add(person)

    let dog = Dog(value: ["name": "Rex", "age": 1])
    realm.add(dog)

    // Find dogs younger than 2.
    let puppies = realm.objects(Dog.self).filter("age < 2")

    // Give all puppies to Ali.
    person.setValue(puppies, forKey: "dogs")

}

Upsert an Object

An upsert either inserts or updates an object depending on whether
the object already exists. Upserts require the data model to have a
primary key.

Objective-C

To upsert an object, call -[RLMRealm addOrUpdateObject:].

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    Person *jones = [[Person alloc] initWithValue:@{@"_id": @1234, @"name": @"Jones"}];
    // Add a new person to the realm. Since nobody with ID 1234
    // has been added yet, this adds the instance to the realm.
    [realm addOrUpdateObject:jones];
    
    Person *bowie = [[Person alloc] initWithValue:@{@"_id": @1234, @"name": @"Bowie"}];
    // Judging by the ID, it's the same person, just with a different name.
    // This overwrites the original entry (i.e. Jones -> Bowie).
    [realm addOrUpdateObject:bowie];
}];

Swift

To upsert an object, call Realm.add(_:update:)
with the second parameter, update policy, set to .modified.

swift
let realm = try! Realm()
try! realm.write {
    let person1 = Person(value: ["id": 1234, "name": "Jones"])
    // Add a new person to the realm. Since nobody with ID 1234
    // has been added yet, this adds the instance to the realm.
    realm.add(person1, update: .modified)

    let person2 = Person(value: ["id": 1234, "name": "Bowie"])
    // Judging by the ID, it's the same person, just with a
    // different name. When `update` is:
    // - .modified: update the fields that have changed.
    // - .all: replace all of the fields regardless of
    //   whether they've changed.
    // - .error: throw an exception if a key with the same
    //   primary key already exists.
    realm.add(person2, update: .modified)
}

You can also partially update an object by passing the primary key and a
subset of the values to update:

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    // Only update the provided values.
    // Note that the "name" property will remain the same
    // for the person with primary key "_id" 123.
    [Person createOrUpdateModifiedInRealm:realm
        withValue:@{@"_id": @123, @"dogs": @[@[@"Buster", @5]]}];
}];

Swift

swift
let realm = try! Realm()
try! realm.write {
    // Use .modified to only update the provided values.
    // Note that the "name" property will remain the same
    // for the person with primary key "id" 123.
    realm.create(Person.self,
                 value: ["id": 123, "dogs": [["Buster", 5]]],
                 update: .modified)
}

Update a Map/Dictionary

You can update a realm map as you would a
standard Dictionary:

swift
let realm = try! Realm()

// Find the dog we want to update
let wolfie = realm.objects(Dog.self).where {
    $0.name == "Wolfie"
}.first!

print("Wolfie's favorite park in New York is: \(wolfie.favoriteParksByCity["New York"])")
XCTAssertTrue(wolfie.favoriteParksByCity["New York"] == "Domino Park")

// Update values for keys, or add values if the keys do not currently exist
try! realm.write {
    wolfie.favoriteParksByCity["New York"] = "Washington Square Park"
    wolfie.favoriteParksByCity.updateValue("A Street Park", forKey: "Boston")
    wolfie.favoriteParksByCity.setValue("Little Long Pond", forKey: "Seal Harbor")
}

XCTAssertTrue(wolfie.favoriteParksByCity["New York"] == "Washington Square Park")

Update a MutableSet Property

You can insert elements into a MutableSet during write transactions to add them to the
property. If you are working with multiple sets, you can also insert or
remove set elements contained in one set from the other set. Alternately,
you can mutate a set to contain only the common elements from both.

swift
let realm = try! Realm()

// Record a dog's name, current city, and store it to the cities visited.
let dog = Dog()
dog.name = "Maui"
dog.currentCity = "New York"
try! realm.write {
    realm.add(dog)
    dog.citiesVisited.insert(dog.currentCity)
}

// Update the dog's current city, and add it to the set of cities visited.
try! realm.write {
    dog.currentCity = "Toronto"
    dog.citiesVisited.insert(dog.currentCity)
}
XCTAssertEqual(dog.citiesVisited.count, 2)

// If you're operating with two sets, you can insert the elements from one set into another set.
// The dog2 set contains one element that isn't present in the dog set.
try! realm.write {
    dog.citiesVisited.formUnion(dog2.citiesVisited)
}
XCTAssertEqual(dog.citiesVisited.count, 3)

// Or you can remove elements that are present in the second set. This removes the one element
// that we added above from the dog2 set.
try! realm.write {
    dog.citiesVisited.subtract(dog2.citiesVisited)
}
XCTAssertEqual(dog.citiesVisited.count, 2)

// If the sets contain common elements, you can mutate the set to only contain those common elements.
// In this case, the two sets contain no common elements, so this set should now contain 0 items.
try! realm.write {
    dog.citiesVisited.formIntersection(dog2.citiesVisited)
}
XCTAssertEqual(dog.citiesVisited.count, 0)

Update an AnyRealmValue Property

You can update an AnyRealmValue property through assignment, but you must
specify the type of the value when you assign it. The Realm Swift SDK
provides an AnyRealmValue enum that
iterates through all of the types the AnyRealmValue can store.

swift
let realm = try! Realm()

// Get a dog to update
let rex = realm.objects(Dog.self).where {
    $0.name == "Rex"
}.first!

try! realm.write {
    // As with creating an object with an AnyRealmValue, you must specify the
    // type of the value when you update the property.
    rex.companion = .object(Dog(value: ["name": "Regina"]))
}

Update an Embedded Object Property

To update a property in an embedded object, modify the property in a
write transaction. If the embedded object is null, updating an embedded
object property has no effect.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock: ^{
    Contact *contact = [Contact objectInRealm:realm
                                forPrimaryKey:[[RLMObjectId alloc] initWithString:@"5f481c21f634a1f4eeaa7268" error:nil]];
    contact.address.street = @"Hollywood Upstairs Medical College";
    contact.address.city = @"Los Angeles";
    contact.address.postalCode = @"90210";
    NSLog(@"Updated contact: %@", contact);
}];

Swift

swift
// Open the default realm
let realm = try! Realm()

let idOfPersonToUpdate = 123

// Find the person to update by ID
guard let person = realm.object(ofType: Person.self, forPrimaryKey: idOfPersonToUpdate) else {
    print("Person \(idOfPersonToUpdate) not found")
    return
}

try! realm.write {
    // Update the embedded object directly through the person
    // If the embedded object is null, updating these properties has no effect
    person.address?.street = "789 Any Street"
    person.address?.city = "Anytown"
    person.address?.postalCode = "12345"
    print("Updated person: \(person)")
}

Overwrite an Embedded Object

To overwrite an embedded object, reassign the embedded object property
of a party to a new instance in a write transaction.

Objective-C

objectivec
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock: ^{
    Contact *contact = [Contact objectInRealm:realm
                                forPrimaryKey:[[RLMObjectId alloc] initWithString:@"5f481c21f634a1f4eeaa7268" error:nil]];
    Address *newAddress = [[Address alloc] init];
    newAddress.street = @"Hollywood Upstairs Medical College";
    newAddress.city = @"Los Angeles";
    newAddress.country = @"USA";
    newAddress.postalCode = @"90210";
    contact.address = newAddress;
    NSLog(@"Updated contact: %@", contact);
}];

Swift

swift
// Open the default realm
let realm = try! Realm()

let idOfPersonToUpdate = 123

// Find the person to update by ID
guard let person = realm.object(ofType: Person.self, forPrimaryKey: idOfPersonToUpdate) else {
    print("Person \(idOfPersonToUpdate) not found")
    return
}

try! realm.write {
    let newAddress = Address()
    newAddress.street = "789 Any Street"
    newAddress.city = "Anytown"
    newAddress.country = "USA"
    newAddress.postalCode = "12345"

    // Overwrite the embedded object
    person.address = newAddress
    print("Updated person: \(person)")
}

Update an Object Asynchronously

You can use Swift concurrency features to asynchronously update objects
using an actor-isolated realm.

This function from the example RealmActor defined on the
Use Realm with Actors page shows how you might
update an object in an actor-isolated realm:

swift
func updateTodo(_id: ObjectId, name: String, owner: String, status: String) async throws {
    try await realm.asyncWrite {
        realm.create(Todo.self, value: [
            "_id": _id,
            "name": name,
            "owner": owner,
            "status": status
        ], update: .modified)
    }
}

And you might perform this update using Swift's async syntax:

swift
let actor = try await RealmActor()

// Read objects in functions isolated to the actor and pass primitive values to the caller
func getObjectId(in actor: isolated RealmActor, forTodoNamed name: String) async -> ObjectId {
    let todo = actor.realm.objects(Todo.self).where {
        $0.name == name
    }.first!
    return todo._id
}
let objectId = await getObjectId(in: actor, forTodoNamed: "Keep it safe")

try await actor.updateTodo(_id: objectId, name: "Keep it safe", owner: "Frodo", status: "Completed")

This operation does not block or perform I/O on the calling thread. For
more information about writing to realm using Swift concurrency features,
refer to Use Realm with Actors - Swift SDK.

Update Properties through Class Projections

Change Class Projection Properties

You can make changes to a class projection's properties in a write transaction.

swift
// Retrieve all class projections of the given type `PersonProjection`
// and filter for the first class projection where the `firstName` property
// value is "Jason"
let person = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })!
// Update class projection property in a write transaction
try! realm.write {
    person.firstName = "David"
}

Guides Model Data Change An Object Model (`docs/guides/model-data/change-an-object-model.md`)

Change an Object Model - Swift SDK

Overview

When you update your object schema, you must increment the schema version
and perform a migration.

Seealso:
This page provides general Swift and Objective-C migration examples.
If you are using Realm with SwiftUI, see the SwiftUI-specific
migration examples.

If your schema update adds optional properties or removes properties,
Realm can perform the migration automatically. You only need to
increment the schemaVersion.

For more complex schema updates, you must also manually specify the migration logic
in a migrationBlock. This might include changes such as:

  • Adding required properties that must be populated with default values
  • Combining fields
  • Renaming a field
  • Changing a field's type
  • Converting from an object to an embedded object

Tip:
When developing or debugging your application, you may prefer to delete
the realm instead of migrating it. Use the
deleteRealmIfMigrationNeeded
flag to delete the database automatically when a schema mismatch would
require a migration.

Never release an app to production with this flag set to true.

Schema Version

A schema version identifies the state of a Realm Schema at some point in time. Realm tracks the schema
version of each realm and uses it to map the objects in each realm
to the correct schema.

Schema versions are integers that you may include
in the realm configuration when you open a realm. If a client
application does not specify a version number when it opens a realm then
the realm defaults to version 0.

Important:
Migrations must update a realm to a higher schema version.
Realm will throw an error if a client application opens
a realm with a schema version that is lower than the realm's
current version or if the specified schema version is the same as the
realm's current version but includes different object
schemas.

Migrations

Local migrations have access to the existing
Realm Schema, version, and objects and define logic that
incrementally updates the realm to its new schema version.
To perform a local migration you must specify a new schema
version that is higher than the current version and provide
a migration function when you open the out-of-date realm.

In iOS, you can update underlying data to reflect schema changes using
manual migrations. During such a
manual migration, you can define new and deleted properties when they
are added or removed from your schema.

Automatically Update Schema

Add a Property

Realm can automatically migrate added
properties, but you must specify an updated schema version when you make
these changes.

Note:
Realm does not automatically set values for new required
properties. You must use a migration block to set default values for
new required properties. For new optional properties, existing records
can have null values. This means you don't need a migration block when
adding optional properties.

Example:
A realm using schema version 1 has a Person object type
that has first name, last name, and age properties:

Objective-C

objectivec
// In the first version of the app, the Person model
// has separate fields for first and last names,
// and an age property.
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"firstName", @"lastName", @"age"];
}
@end

Swift

swift
// In the first version of the app, the Person model
// has separate fields for first and last names,
// and an age property.
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    @Persisted var age = 0
}

The developer decides that the Person class needs an email field and updates
the schema.

Objective-C

objectivec
// In a new version, you add a property
// on the Person model.
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
// Add a new "email" property.
@property NSString *email;
// New properties can be migrated
// automatically, but must update the schema version.
@property int age;
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"firstName", @"lastName", @"email", @"age"];
}
@end

Swift

swift
// In a new version, you add a property
// on the Person model.
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    // Add a new "email" property.
    @Persisted var email: String?
    // New properties can be migrated
    // automatically, but must update the schema version.
    @Persisted var age = 0

}

Realm automatically migrates the realm to conform to
the updated Person schema. But the developer must set the realm's
schema version to 2.

Objective-C

objectivec
// When you open the realm, specify that the schema
// is now using a newer version.
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
// Set the new schema version
config.schemaVersion = 2;
// Use this configuration when opening realms
[RLMRealmConfiguration setDefaultConfiguration:config];
RLMRealm *realm = [RLMRealm defaultRealm];

Swift

swift
// When you open the realm, specify that the schema
// is now using a newer version.
let config = Realm.Configuration(
    schemaVersion: 2)
// Use this configuration when opening realms
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()

Delete a Property

To delete a property from a schema, remove the property from the object's class
and set a schemaVersion of the realm's configuration object. Deleting a property
will not impact existing objects.

Example:
A realm using schema version 1 has a Person object type
that has first name, last name, and age properties:

Objective-C

objectivec
// In the first version of the app, the Person model
// has separate fields for first and last names,
// and an age property.
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"firstName", @"lastName", @"age"];
}
@end

Swift

swift
// In the first version of the app, the Person model
// has separate fields for first and last names,
// and an age property.
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    @Persisted var age = 0
}

The developer decides that the Person does not need the age field and updates the schema.

Objective-C

objectivec
// In a new version, you remove a property
// on the Person model.
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
// Remove the "age" property.
// @property int age;
// Removed properties can be migrated
// automatically, but must update the schema version.
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"firstName", @"lastName"];
}
@end

Swift

swift
// In a new version, you remove a property
// on the Person model.
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    // Remove the "age" property.
    // @Persisted var age = 0
    // Removed properties can be migrated
    // automatically, but must update the schema version.

}

Realm automatically migrates the realm to conform to
the updated Person schema. But the developer must set the realm's
schema version to 2.

Objective-C

objectivec
// When you open the realm, specify that the schema
// is now using a newer version.
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
// Set the new schema version
config.schemaVersion = 2;
// Use this configuration when opening realms
[RLMRealmConfiguration setDefaultConfiguration:config];
RLMRealm *realm = [RLMRealm defaultRealm];

Swift

swift
// When you open the realm, specify that the schema
// is now using a newer version.
let config = Realm.Configuration(
    schemaVersion: 2)
// Use this configuration when opening realms
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()

Tip:
SwiftUI developers may see an error that a migration is required when they
add or delete properties. This is related to the lifecycle in SwiftUI.
The Views are laid out, and then the .environment modifier sets the
config.

To resolve a migration error in these circumstances, pass
Realm.Configuration(schemaVersion: <Your Incremented Version>)
into the ObservedResults constructor.

Manually Migrate Schema

For more complex schema updates, Realm requires a manual
migration for old instances of a given object to the new schema.

Rename a Property

To rename a property during a migration, use the
Migration.renameProperty(onType:from:to:)
method.

Realm applies any new nullability or indexing settings
during the rename operation.

Example:
Rename age to yearsSinceBirth within a migrationBlock.

Objective-C

objectivec
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {
    if (oldSchemaVersion < 2) {
        // Rename the "age" property to "yearsSinceBirth".
        // The renaming operation should be done outside of calls to `enumerateObjects(ofType: _:)`.
        [migration renamePropertyForClass:[Person className] oldName:@"age" newName:@"yearsSinceBirth"];
    }
};

Swift

swift
let config = Realm.Configuration(
    schemaVersion: 2,
    migrationBlock: { migration, oldSchemaVersion in
        if oldSchemaVersion < 2 {
            // Rename the "age" property to "yearsSinceBirth".
            // The renaming operation should be done outside of calls to `enumerateObjects(ofType: _:)`.
            migration.renameProperty(onType: Person.className(), from: "age", to: "yearsSinceBirth")
        }
    })

Modify Properties

Tip:
You can use the deleteRealmIfMigrationNeeded
method to delete the realm if it would require a migration. This can
be useful during development when you need to iterate quickly and don't
want to perform the migration.

To define custom migration logic, set the migrationBlock
property of the Configuration when opening a realm.

The migration block receives a Migration object that you can use to perform the migration. You
can use the Migration object's enumerateObjects(ofType:_:)
method to iterate over and update all instances of a given
Realm type in the realm.

Example:
A realm using schema version 1 has a Person object type
that has separate fields for first and last names:

Objective-C

objectivec
// In the first version of the app, the Person model
// has separate fields for first and last names,
// and an age property.
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"firstName", @"lastName", @"age"];
}
@end

Swift

swift
// In the first version of the app, the Person model
// has separate fields for first and last names,
// and an age property.
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    @Persisted var age = 0
}

The developer decides that the Person class should use a combined
fullName field instead of the separate firstName and
lastName fields and updates the schema.

To migrate the realm to conform to the updated Person schema,
the developer sets the realm's schema version to 2 and
defines a migration function to set the value of fullName based
on the existing firstName and lastName properties.

Objective-C

objectivec
// In version 2, the Person model has one
// combined field for the full name and age as a Int.
// A manual migration will be required to convert from
// version 1 to this version.
@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"fullName", @"age"];
}
@end
objectivec
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
// Set the new schema version
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {
    if (oldSchemaVersion < 2) {
        // Iterate over every 'Person' object stored in the Realm file to
        // apply the migration
        [migration enumerateObjects:[Person className]
                            block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
            // Combine name fields into a single field
            newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
                                        oldObject[@"firstName"],
                                        oldObject[@"lastName"]];
        }];
    }
};

// Tell Realm to use this new configuration object for the default Realm
[RLMRealmConfiguration setDefaultConfiguration:config];

// Now that we've told Realm how to handle the schema change, opening the realm
// will automatically perform the migration
RLMRealm *realm = [RLMRealm defaultRealm];

Swift

swift
// In version 2, the Person model has one
// combined field for the full name and age as a Int.
// A manual migration will be required to convert from
// version 1 to this version.
class Person: Object {
    @Persisted var fullName = ""
    @Persisted var age = 0
}
swift
// In application(_:didFinishLaunchingWithOptions:)
let config = Realm.Configuration(
    schemaVersion: 2, // Set the new schema version.
    migrationBlock: { migration, oldSchemaVersion in
        if oldSchemaVersion < 2 {
            // The enumerateObjects(ofType:_:) method iterates over
            // every Person object stored in the Realm file to apply the migration
            migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in
                // combine name fields into a single field
                let firstName = oldObject!["firstName"] as? String
                let lastName = oldObject!["lastName"] as? String
                newObject!["fullName"] = "\(firstName!) \(lastName!)"
            }
        }
    }
)

// Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config

// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
let realm = try! Realm()

Later, the developer decides that the age field should be of type String
rather than Int and updates the schema.

To migrate the realm to conform to the updated Person schema,
the developer sets the realm's schema version to 3 and
adds a conditional to the migration function so that the function defines
how to migrate from any previous version to the new one.

Objective-C

objectivec
// In version 3, the Person model has one
// combined field for the full name and age as a String.
// A manual migration will be required to convert from
// version 2 to this version.
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *age;
@end

@implementation Person
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"fullName", @"age"];
}
@end
objectivec
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
// Set the new schema version
config.schemaVersion = 3;
config.migrationBlock = ^(RLMMigration * _Nonnull migration, uint64_t oldSchemaVersion) {
    if (oldSchemaVersion < 2) {
        // Previous Migration.
        [migration enumerateObjects:[Person className]
                            block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
            newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
                                        oldObject[@"firstName"],
                                        oldObject[@"lastName"]];
        }];
    }
    if (oldSchemaVersion < 3) {
        // New Migration
        [migration enumerateObjects:[Person className]
                            block:^(RLMObject * _Nullable oldObject, RLMObject * _Nullable newObject) {
            // Make age a String instead of an Int
            newObject[@"age"] = [oldObject[@"age"] stringValue];
        }];
    }
};

// Tell Realm to use this new configuration object for the default Realm
[RLMRealmConfiguration setDefaultConfiguration:config];

// Now that we've told Realm how to handle the schema change, opening the realm
// will automatically perform the migration
RLMRealm *realm = [RLMRealm defaultRealm];

Swift

swift
// In version 3, the Person model has one
// combined field for the full name and age as a String.
// A manual migration will be required to convert from
// version 2 to this version.
 class Person: Object {
    @Persisted var fullName = ""
    @Persisted var age = "0"
 }
swift
// In application(_:didFinishLaunchingWithOptions:)
let config = Realm.Configuration(
    schemaVersion: 3, // Set the new schema version.
    migrationBlock: { migration, oldSchemaVersion in
        if oldSchemaVersion < 2 {
            // Previous Migration.
            migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in
                let firstName = oldObject!["firstName"] as? String
                let lastName = oldObject!["lastName"] as? String
                newObject!["fullName"] = "\(firstName!) \(lastName!)"
            }
        }
        if oldSchemaVersion < 3 {
            // New Migration.
            migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in
                // Make age a String instead of an Int
                newObject!["age"] = "\(oldObject!["age"] ?? 0)"
            }
        }
    }
)

// Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config

// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
let realm = try! Realm()

Tip:
Avoid nesting or otherwise skipping if (oldSchemaVersion < X) statements
in migration blocks. This ensures that all updates can be applied in the correct order,
no matter which schema version a client starts from. The goal is to define
migration logic which can transform data from any outdated schema version to
match the current schema.

Convert from Object to EmbeddedObject

Embedded objects cannot exist
independently of a parent object. When changing an Object to an
EmbeddedObject, the migration block must ensure that every embedded
object has exactly one backlink to a parent object. Having no backlinks
or multiple backlinks raises the following exceptions:

text
At least one object does not have a backlink (data would get lost).
text
At least one object does have multiple backlinks.

Seealso:
Define an Embedded Object Property

Additional Migration Examples

Please check out the additional migration examples on the
realm-swift repo.


Guides Model Data Model Data (`docs/guides/model-data/model-data.md`)

Model Data - Swift SDK

Object Types & Schemas

Realm applications model data as objects composed of
field-value pairs that each contain one or more supported data types.

Realm objects are regular Swift or Objective-C classes, but
they also bring a few additional features like live queries. The Swift SDK memory maps Realm objects directly to
native Swift or Objective-C objects, which means there's no need to use
a special data access library, such as an ORM. Instead, you can work with Realm objects
as you would any other class instance.

Every Realm object conforms to a specific object type, which is
essentially a class that defines the properties
and relationships for objects of that type.
Realm guarantees that all objects in a realm conform to
the schema for their object type and validates objects whenever they're
created, modified, or deleted.

Example:
The following schema defines a Dog object type with a string name,
optional string breed, date of birth, and primary key ID.

Objective-C

objectivec
// A dog has an _id primary key, a string name, an optional
// string breed, and a date of birth.
@interface Dog : RLMObject
@property RLMObjectId *_id;
@property NSString *name;
@property NSString *breed;
@property NSDate *dateOfBirth;
@end

@implementation Dog
+ (NSString *)primaryKey {
    return @"_id";
}

+ (NSArray<NSString *> *)requiredProperties {
    return @[
        @"_id", @"name", @"dateOfBirth"
    ];
}
@end

Swift

swift
// A dog has an _id primary key, a string name, an optional
// string breed, and a date of birth.
class Dog: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var name = ""
    @Persisted var breed: String?
    @Persisted var dateOfBirth = Date()
}

Realm Schema

A realm schema is a list of valid object schemas that a realm may contain. Every Realm object must
conform to an object type that's included in its realm's schema.

By default, the Swift SDK automatically adds all classes in your project
that derive from RLMObject or
RLMEmbeddedObject to the
realm schema.

Tip:
To control which classes Realm adds to a realm schema, see
Provide a Subset of Classes to a Realm.

If a realm already contains data when you open it,
Realm validates each object to ensure that an object
schema was provided for its type and that it meets all of the
constraints specified in the schema.

Tip:
For code examples that show how to configure and open a realm in the
Swift SDK, see Configure & Open a Realm - Swift SDK.

Model Inheritance

You can subclass Realm models to share behavior between
classes, but there are limitations. In particular, Realm
does not allow you to:

  • Cast between polymorphic classes: subclass to subclass, subclass to parent, parent to subclass
  • Query on multiple classes simultaneously: for example, "get all instances of parent class and subclass"
  • Multi-class containers: List and Results with a mixture of parent and subclass

Tip:
Check out the code samples for working
around these limitations.

Version added: 10.10.0While you can't mix and property declarations
within a class, you can mix the notation styles across base and subclasses.
For example, a base class could have a property,
and a subclass could have an property, with
both persisted. However, the property would be ignored if
the property were within the same base or subclass.

Swift Structs

Realm does not support Swift structs as models for a variety of
reasons. Realm's design focuses on "live" objects.
This concept is not compatible with value type structs. By design,
Realm provides features that are incompatible with these
semantics, such as:

  • Live data
  • Reactive APIs
  • Low memory footprint of data
  • Good operation performance
  • Lazy and cheap access to partial data
  • Lack of data serialization/deserialization
  • Keeping potentially complex object graphs synchronized

That said, it is sometimes useful to detach objects from their backing
realm. This typically isn't an ideal design decision. Instead,
developers use this as a workaround for temporary limitations in our
library.

You can use key-value coding to initialize an unmanaged object as a copy of
a managed object. Then, you can work with that unmanaged object
like any other NSObject.

swift
let standaloneModelObject = MyModel(value: persistedModelObject)

Properties

Your Realm object model is a collection of properties. On the most basic level,
when you create your model, your declarations give Realm information about
each property:

  • The data type and whether the property is optional or required
  • Whether Realm should store or ignore the property
  • Whether the property is a primary key or should be indexed

Properties are also the mechanism for establishing relationships between Realm object types.

The Realm Swift SDK uses reflection to determine the properties
in your models at runtime. Your project must not set
SWIFT_REFLECTION_METADATA_LEVEL = none, or Realm cannot discover
children of types, such as properties and enum cases. Reflection is enabled
by default if your project does not specifically set a level for this setting.

View Models with Realm

Version added: 10.21.0

You can work with a subset of your Realm object's properties
by creating a class projection. A class projection is a class that passes
through or transforms some or all of your Realm object's
properties. Class projection enables you to build view models that use an
abstraction of your object model. This simplifies using and testing Realm objects
in your application.

With class projection, you can use a subset of your object's properties
directly in the UI or transform them. When you use a class projection for
this, you get all the benefits of Realm's live objects:

  • The class-projected object live updates
  • You can observe it for changes
  • You can apply changes directly to the properties in write transactions

Seealso:
Define a Class Projection

Relationships

Realm doesn't use bridge tables or explicit joins to define
relationships as you would in a relational database. Realm
handles relationships through embedded objects or reference properties to
other Realm objects. You read from and write to these
properties directly. This makes querying relationships as performant as
querying against any other property.

Realm supports to-one, to-many, and inverse
relationships.

To-One Relationship

A to-one relationship means that an object relates to one other object.
You define a to-one relationship for an object type in its object
schema. Specify a property where the type is the related Realm
object type. For example, a dog might have a to-one relationship with
a favorite toy.

Tip:
To learn how to define a to-one relationship, see
Define a To-One Relationship Property.

To-Many Relationship

A to-many relationship means that an object relates to more than one
other object. In Realm, a to-many relationship is a list of
references to other objects. For example, a person might have many dogs.

A List represents the to-many
relationship between two Realm
types. Lists are mutable: within a write transaction, you can add and
remove elements to and from a list. Lists are not associated with a
query and are usually declared as a property of an object model.

Tip:
To learn how to define a to-many relationship, see
Define a To-Many Relationship Property.

Inverse Relationship

Relationship definitions in Realm are unidirectional. An
inverse relationship links an object back to an object that refers
to it. You must explicitly define a property in the object's model as an
inverse relationship. Inverse relationships can link back to objects in
a to-one or to-many relationship.

A LinkingObjects collection
represents the inverse relationship
between two Realm types. You cannot directly add or remove
items from a LinkingObjects collection.

Inverse relationships automatically update themselves with corresponding
backlinks. You can find the same set of Realm objects with a
manual query, but the inverse relationship field reduces boilerplate query
code and capacity for error.

For example, consider a task tracker with the to-many relationship "User has
many Tasks". This does not automatically create the inverse relationship
"Task belongs to User". To create the inverse relationship, add a User
property on the Task that points back to the task's owner. When you specify
the inverse relationship from task to user, you can query on that. If you
don't specify the inverse relationship, you must run a separate query to
look up the user to whom the task is assigned.

Important:
You cannot manually set the value of an inverse relationship property.
Instead, Realm updates implicit relationships when you add
or remove an object in the relationship.

Relationships can be many-to-one or many-to-many. So following inverse
relationships can result in zero, one, or many objects.

Tip:
To learn how to define an inverse relationship, see
Define an Inverse Relationship Property.


Guides Model Data Object Models (`docs/guides/model-data/object-models.md`)

Define a Realm Object Model - Swift SDK

Define a New Object Type

Objective-C

You can define a Realm object by deriving from the
RLMObject or
RLMEmbeddedObject class. The name of the
class becomes the table name in the realm, and properties of the
class persist in the database. This makes it as easy to work with
persisted objects as it is to work with regular Objective-C
objects.

objectivec
// A dog has an _id primary key, a string name, an optional
// string breed, and a date of birth.
@interface Dog : RLMObject
@property RLMObjectId *_id;
@property NSString *name;
@property NSString *breed;
@property NSDate *dateOfBirth;
@end

@implementation Dog
+ (NSString *)primaryKey {
    return @"_id";
}

+ (NSArray<NSString *> *)requiredProperties {
    return @[
        @"_id", @"name", @"dateOfBirth"
    ];
}
@end

Swift

You can define a Realm object by deriving from the
Object or
EmbeddedObject
class. The name of the class becomes the table name in the realm,
and properties of the class persist in the database. This makes it
as easy to work with persisted objects as it is to work with
regular Swift objects.

swift
// A dog has an _id primary key, a string name, an optional
// string breed, and a date of birth.
class Dog: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var name = ""
    @Persisted var breed: String?
    @Persisted var dateOfBirth = Date()
}

Note:
Class names are limited to a maximum of 57 UTF-8 characters.

Declare Properties

When you declare the property attributes of a class, you can specify whether
or not those properties should be managed by the realm. Managed properties
are stored or updated in the database. Ignored properties are not
stored to the database. You can mix managed and ignored properties
within a class.

The syntax to mark properties as managed or ignored varies depending on which
version of the SDK you use.

Persisted Property Attributes

Version added: 10.10.0The declaration style replaces the ,
, and declaration notations from older
versions of the SDK. For an older version of the SDK, see:
.

Declare model properties that you want to store to the database as
@Persisted. This enables them to access the underlying database data.

When you declare any properties as @Persisted within a class, the other
properties within that class are automatically ignored.

If you mix @Persisted and @objc dynamic property declarations within
a class definition, any property attributes marked as @objc dynamic will
be ignored.

Seealso:
Our Supported Property Types
page contains a property declaration cheatsheet.

Objective-C Dynamic Property Attributes

Version changed: 10.10.0This property declaration information is for versions of the SDK before
10.10.0.

Declare dynamic Realm model properties in the Objective-C runtime. This
enables them to access the underlying database data.

You can either:

  • Use @objc dynamic var to declare individual properties
  • Use @objcMembers to declare a class. Then, declare individual
    properties with dynamic var.

Use let to declare LinkingObjects, List, RealmOptional and
RealmProperty. The Objective-C runtime cannot represent these
generic properties.

Version changed: 10.8.0
RealmProperty replaces RealmOptional

Seealso:
Our Supported Property Types
page contains a property declaration cheatsheet.

Tip:
For reference on which types Realm supports for use as
properties, see Supported Property Types.

Swift

When declaring non-generic properties, use the @Persisted annotation.
The @Persisted attribute turns Realm model properties into accessors
for the underlying database data.

Objective-C

Declare properties on your object type as you would on a normal
Objective-C interface.

In order to use your interface in a Realm array, pass your
interface name to the RLM_COLLECTION_TYPE() macro. You can put this
at the bottom of your interface's header file. The
RLM_COLLECTION_TYPE() macro creates a protocol that allows you to
tag RLMArray with your type:

objectivec
// Task.h
@interface Task : RLMObject
@property NSString *description;
@end

// Define an RLMArray<Task> type
RLM_COLLECTION_TYPE(Task)

// User.h
// #include "Task.h"
@interface User : RLMObject
@property NSString *name;
// Use RLMArray<Task> to have a list of tasks
// Note the required double tag (<Task *><Task>)
@property RLMArray<Task *><Task> *tasks;
@end

Swift Pre 10.10.0

When declaring non-generic properties, use the @objc dynamic var annotation. The @objc dynamic var attribute turns Realm
model properties into accessors for the underlying database data.
If the class is declared as @objcMembers (Swift 4 or later),
you can declare properties as dynamic var without @objc.

To declare properties of generic types LinkingObjects,
List, and RealmProperty, use let. Generic properties
cannot be represented in the Objective‑C runtime, which
Realm uses for dynamic dispatch of dynamic
properties.

Note:
Property names are limited to a maximum of 63 UTF-8 characters.

Specify an Optional/Required Property

Swift

You can declare properties as optional or required (non-optional) using
standard Swift syntax.

swift
class Person: Object {
    // Required string property
    @Persisted var name = ""

    // Optional string property
    @Persisted var address: String?

    // Required numeric property
    @Persisted var ageYears = 0

    // Optional numeric property
    @Persisted var heightCm: Float?
}

Objective-C

To declare a given property as required, implement the
requiredProperties
method and return an array of required property names.

objectivec
@interface Person : RLMObject
// Required property - included in `requiredProperties`
// return value array
@property NSString *name;

// Optional string property - not included in `requiredProperties`
@property NSString *address;

// Required numeric property
@property int ageYears;

// Optional numeric properties use NSNumber tagged
// with RLMInt, RLMFloat, etc.
@property NSNumber<RLMFloat> *heightCm;
@end

@implementation Person
// Specify required pointer-type properties here.
// Implicitly required properties (such as properties
// of primitive types) do not need to be named here.
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"name"];
}
@end

Swift Pre 10.10.0

Version changed: 10.8.0
RealmProperty replaces RealmOptional

You can declare String, Date, Data, and
ObjectId properties as
optional or required (non-optional) using standard Swift syntax.
Declare optional numeric types using the RealmProperty
type.

swift
class Person: Object {
    // Required string property
    @objc dynamic var name = ""

    // Optional string property
    @objc dynamic var address: String?

    // Required numeric property
    @objc dynamic var ageYears = 0

    // Optional numeric property
    let heightCm = RealmProperty<Float?>()
}

RealmProperty supports Int, Float, Double, Bool,
and all of the sized versions of Int (Int8, Int16,
Int32, Int64).

Specify a Primary Key

You can designate a property as the primary key of your class.

Primary keys allow you to efficiently find, update, and upsert objects.

Primary keys are subject to the following limitations:

  • You can define only one primary key per object model.
  • Primary key values must be unique across all instances of an object
    in a realm. Realm throws an error if you try to
    insert a duplicate primary key value.
  • Primary key values are immutable. To change the primary key value of
    an object, you must delete the original object and insert a new object
    with a different primary key value.
  • Embedded objects cannot define a
    primary key.

Swift

Declare the property with primaryKey: true
on the @Persisted notation to set the model's primary key.

swift
class Project: Object {
    @Persisted(primaryKey: true) var id = 0
    @Persisted var name = ""
}

Objective-C

Override +[RLMObject primaryKey] to
set the model's primary key.

objectivec
@interface Project : RLMObject
@property NSInteger id; // Intended primary key
@property NSString *name;
@end

@implementation Project
// Return the name of the primary key property
+ (NSString *)primaryKey {
    return @"id";
}
@end

Swift Pre 10.10.0

Override Object.primaryKey()
to set the model's primary key.

swift
class Project: Object {
    @objc dynamic var id = 0
    @objc dynamic var name = ""

    // Return the name of the primary key property
    override static func primaryKey() -> String? {
        return "id"
    }
}

Index a Property

You can create an index on a given property of your model. Indexes speed up
queries using equality and IN operators. They make insert and update operation
speed slightly slower. Indexes use memory and take up more space in the realm
file. Each index entry is a minimum of 12 bytes. It's best to only add indexes
when optimizing the read performance for specific situations.

Realm supports indexing for string, integer, boolean, Date, UUID,
ObjectId, and AnyRealmValue properties.

Version added: 10.8.0
UUID and AnyRealmValue types

Swift

To index a property, declare the property with
indexed:true
on the @Persisted notation.

swift
class Book: Object {
    @Persisted var priceCents = 0
    @Persisted(indexed: true) var title = ""
}

Objective-C

To index a property, override RLMObject indexedProperties
and return a list of indexed property names.

objectivec
@interface Book : RLMObject
@property int priceCents;
@property NSString *title;
@end

@implementation Book
// Return a list of indexed property names
+ (NSArray *)indexedProperties {
    return @[@"title"];
}
@end

Swift Pre 10.10.0

To index a property, override
Object.indexedProperties()
and return a list of indexed property names.

swift
class Book: Object {
    @objc dynamic var priceCents = 0
    @objc dynamic var title = ""

    // Return a list of indexed property names
    override static func indexedProperties() -> [String] {
        return ["title"]
    }
}

Ignore a Property

Ignored properties behave exactly like normal properties. They can't be
used in queries and won't trigger Realm notifications. You can still
observe them using KVO.

Tip:
Realm automatically ignores read-only properties.

Swift

Deprecated:

If you don't want to save a field in your model to its realm,
leave the @Persisted notation off the property attribute.

Additionally, if you mix @Persisted and @objc dynamic
property declarations within a class, the @objc dynamic
properties will be ignored.

swift
class Person: Object {
    // If some properties are marked as @Persisted,
    // any properties that do not have the @Persisted
    // annotation are automatically ignored.
    var tmpId = 0

    // The @Persisted properties are managed
    @Persisted var firstName = ""
    @Persisted var lastName = ""

    // Read-only properties are automatically ignored
    var name: String {
        return "\(firstName) \(lastName)"
    }

    // If you mix the pre-10.10 property declaration
    // syntax `@objc dynamic` with the 10.10+ @Persisted
    // annotation within a class, `@objc dynamic`
    // properties are ignored.
    @objc dynamic var email = ""
}

Objective-C

If you don't want to save a field in your model to its realm,
override +[RLMObject ignoredProperties]
and return a list of ignored property names.

objectivec
@interface Person : RLMObject
@property NSInteger tmpId;
@property (readonly) NSString *name; // read-only properties are automatically ignored
@property NSString *firstName;
@property NSString *lastName;
@end

@implementation Person
+ (NSArray *)ignoredProperties {
    return @[@"tmpId"];
}
- (NSString *)name {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end

Swift Pre 10.10.0

If you don't want to save a field in your model to its realm,
override Object.ignoredProperties()
and return a list of ignored property names.

swift
class Person: Object {
    @objc dynamic var tmpId = 0
    @objc dynamic var firstName = ""
    @objc dynamic var lastName = ""

    // Read-only properties are automatically ignored
    var name: String {
        return "\(firstName) \(lastName)"
    }

    // Return a list of ignored property names
    override static func ignoredProperties() -> [String] {
        return ["tmpId"]
    }
}

Declare Enum Properties

Swift

Version changed: 10.10.0
Protocol is now PersistableEnum rather than RealmEnum.

You can use enums with @Persisted by marking them as complying with the
PersistableEnum
protocol. A PersistableEnum can be any RawRepresentable enum
whose raw type is a type that Realm supports.

swift
// Define the enum
enum TaskStatusEnum: String, PersistableEnum {
    case notStarted
    case inProgress
    case complete
}

// To use the enum:
class Task: Object {
    @Persisted var name: String = ""
    @Persisted var owner: String?

    // Required enum property
    @Persisted var status = TaskStatusEnum.notStarted

    // Optional enum property
    @Persisted var optionalTaskStatusEnumProperty: TaskStatusEnum?
}

Swift Pre 10.10.0

Realm supports only Int-backed @objc enums.

swift
// Define the enum
@objc enum TaskStatusEnum: Int, RealmEnum {
    case notStarted = 1
    case inProgress = 2
    case complete = 3
}

// To use the enum:
class Task: Object {
    @objc dynamic var name: String = ""
    @objc dynamic var owner: String?

    // Required enum property
    @objc dynamic var status = TaskStatusEnum.notStarted
    // Optional enum property
    let optionalTaskStatusEnumProperty = RealmProperty<TaskStatusEnum?>()
}

Seealso:
RealmEnum

Remap a Property Name

Version added: 10.33.0

You can map the public name of a property in your object model to a different
private name to store in the realm.

Declare the name you want to use in your project as the @Persisted
property on the object model. Then, pass a dictionary containing the
public and private values for the property names via the
propertiesMapping() function.

In this example, firstName is the public property name we use in the code
throughout the project to perform CRUD operations. Using the propertiesMapping()
function, we map that to store values using the private property name
first_name in the realm.

swift
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""

    override class public func propertiesMapping() -> [String: String] {
        ["firstName": "first_name",
         "lastName": "last_name"]
    }
}

Define a Class Projection

About These Examples

The examples in this section use a simple data set. The two Realm object
types are Person and an embedded object Address. A Person has
a first and last name, an optional Address, and a list of friends
consisting of other Person objects. An Address has a city and country.

See the schema for these two classes, Person and Address, below:

swift
class Person: Object {
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    @Persisted var address: Address?
    @Persisted var friends = List<Person>()
}

class Address: EmbeddedObject {
    @Persisted var city: String = ""
    @Persisted var country = ""
}

How to Define a Class Projection

Version added: 10.21.0

Define a class projection by creating a class of type Projection. Specify the Object
or EmbeddedObject base whose
properties you want to use in the class projection. Use the @Projected
property wrapper to declare a property that you want to project from a
@Persisted property on the base object.

Note:
When you use a List or a MutableSet in a class projection, the type in the
class projection should be ProjectedCollection.

swift
class PersonProjection: Projection<Person> {
    @Projected(\Person.firstName) var firstName // Passthrough from original object
    @Projected(\Person.address?.city) var homeCity // Rename and access embedded object property through keypath
    @Projected(\Person.friends.projectTo.firstName) var firstFriendsName: ProjectedCollection<String> // Collection mapping
}

When you define a class projection, you can transform the original @Persisted
property in several ways:

  • Passthrough: the property is the same name and type as the original object
  • Rename: the property has the same type as the original object, but a
    different name
  • Keypath resolution: use keypath resolution to access properties of the
    original object, including embedded object properties
  • Collection mapping: Project lists or
    mutable sets of Object s or
    EmbeddedObject s as a collection of primitive values
  • Exclusion: when you use a class projection, the underlying object's
    properties that are not @Projected through the class projection are
    excluded. This enables you to watch for changes to a class projection
    and not see changes for properties that are not part of the class
    projection.

Define Unstructured Data

Version added: 10.51.0

Starting in SDK version 10.51.0, you can store collections of mixed data
within a AnyRealmValue property. You can use this feature to model complex data
structures, such as JSON, without having to define a
strict data model.

Unstructured data is data that doesn't easily conform to an expected
schema, making it difficult or impractical to model to individual
data classes. For example, your app might have highly variable data or dynamic
data whose structure is unknown at runtime.

Storing collections in a mixed property offers flexibility without sacrificing
functionality. And
you can work with them the same way you would a non-mixed
collection:

  • You can nest mixed collections up to 100 levels.
  • You can query on and react to changes on mixed collections.
  • You can find and update individual mixed collection elements.

However, storing data in mixed collections is less performant than using a structured
schema or serializing JSON blobs into a single string property.

To model unstructured data in your app, define the appropriate properties in
your schema as AnyRealmValue types. You can then
set these AnyRealmValue properties as a list or a
dictionary collection of AnyRealmValue elements.
Note that AnyRealmValue cannot represent a MutableSet or an embedded
object.

Tip:

  • Use a map of mixed data types when the type is unknown but each value will have a unique identifier.
  • Use a list of mixed data types when the type is unknown but the order of
    objects is meaningful.

Guides Model Data Relationships (`docs/guides/model-data/relationships.md`)

Model Relationships - Swift SDK

Declare Relationship Properties

Define a To-One Relationship Property

A to-one relationship maps one property to a single instance of
another object type. For example, you can model a person having at most
one companion dog as a to-one relationship.

Setting a relationship field to null removes the connection between objects.
Realm does not delete the referenced object, though, unless it is
an embedded object.

Important:
When you declare a to-one relationship in your object model, it must
be an optional property. If you try to make a to-one relationship
required, Realm throws an exception at runtime.

Objective-C

objectivec
// Dog.h
@interface Dog : RLMObject
@property NSString *name;
// No backlink to person -- one-directional relationship
@end

// Define an RLMArray<Dog> type
RLM_COLLECTION_TYPE(Dog)

// Person.h
@interface Person : RLMObject
@property NSString *name;
// A person can have one dog
@property Dog *dog;
@end

// Dog.m
@implementation Dog
@end

// Person.m
@implementation Person
@end

Swift

swift
class Person: Object {
    @Persisted var name: String = ""
    @Persisted var birthdate: Date = Date(timeIntervalSince1970: 1)

    // A person can have one dog
    @Persisted var dog: Dog?
}

class Dog: Object {
    @Persisted var name: String = ""
    @Persisted var age: Int = 0
    @Persisted var breed: String?
    // No backlink to person -- one-directional relationship
}

Seealso:
For more information about to-one relationships, see:
To-One Relationship.

Define a To-Many Relationship Property

A to-many relationship maps one property to zero or more instances
of another object type. For example, you can model a person having any
number of companion dogs as a to-many relationship.

Objective-C

Use RLMArray tagged with your
target type to define your to-many relationship property.

Tip:
Remember to use the RLM_COLLECTION_TYPE() macro with your type
to declare the RLMArray protocol for your type.

objectivec
// Dog.h
@interface Dog : RLMObject
@property NSString *name;
// No backlink to person -- one-directional relationship
@end

// Define an RLMArray<Dog> type
RLM_COLLECTION_TYPE(Dog)

// Person.h
@interface Person : RLMObject
@property NSString *name;
// A person can have many dogs
@property RLMArray<Dog *><Dog> *dogs;
@end

// Dog.m
@implementation Dog
@end

// Person.m
@implementation Person
@end

Swift

Use List tagged with your target
type to define your to-many relationship property.

swift
class Person: Object {
    @Persisted var name: String = ""
    @Persisted var birthdate: Date = Date(timeIntervalSince1970: 1)

    // A person can have many dogs
    @Persisted var dogs: List<Dog>
}

class Dog: Object {
    @Persisted var name: String = ""
    @Persisted var age: Int = 0
    @Persisted var breed: String?
    // No backlink to person -- one-directional relationship
}

Seealso:
For more information about to-many relationships, see:
To-Many Relationship.

Define an Inverse Relationship Property

An inverse relationship property is an automatic backlink
relationship. Realm automatically updates implicit
relationships whenever an object is added or removed in a corresponding
to-many list or to-one relationship property. You cannot manually set
the value of an inverse relationship property.

Swift

To define an inverse relationship, use LinkingObjects in your object model. The
LinkingObjects definition specifies the object type and
property name of the relationship that it inverts.

swift
class User: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var _partition: String = ""
    @Persisted var name: String = ""

    // A user can have many tasks.
    @Persisted var tasks: List<Task>
}

class Task: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var _partition: String = ""
    @Persisted var text: String = ""

    // Backlink to the user. This is automatically updated whenever
    // this task is added to or removed from a user's task list.
    @Persisted(originProperty: "tasks") var assignee: LinkingObjects<User>
}

Objective-C

To define an inverse relationship, use
RLMLinkingObjects in your object model.
Override +[RLMObject linkingObjectProperties]
method in your class to specify the object type and property name
of the relationship that it inverts.

objectivec
// Task.h
@interface Task : RLMObject
@property NSString *description;
@property (readonly) RLMLinkingObjects *assignees;
@end

// Define an RLMArray<Task> type
RLM_COLLECTION_TYPE(Task)

// User.h
@interface User : RLMObject
@property NSString *name;
@property RLMArray<Task *><Task> *tasks;
@end

// Task.m
@implementation Task
+ (NSDictionary *)linkingObjectsProperties {
    return @{
        @"assignees": [RLMPropertyDescriptor descriptorWithClass:User.class propertyName:@"tasks"],
    };
}
@end

// User.m
@implementation User
@end

Swift Pre 10.10.0

To define an inverse relationship, use LinkingObjects
in your object model. The LinkingObjects definition specifies
the object type and property name of the relationship that it inverts.

swift
class User: Object {
    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition: String = ""
    @objc dynamic var name: String = ""

    // A user can have many tasks.
    let tasks = List<Task>()

    override static func primaryKey() -> String? {
        return "_id"
    }
}

class Task: Object {
    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition: String = ""
    @objc dynamic var text: String = ""

    // Backlink to the user. This is automatically updated whenever
    // this task is added to or removed from a user's task list.
    let assignee = LinkingObjects(fromType: User.self, property: "tasks")

    override static func primaryKey() -> String? {
        return "_id"
    }
}

Seealso:
For more information about inverse relationships, see:
Inverse Relationship.

Define an Embedded Object Property

An embedded object exists as nested data inside of a single,
specific parent object. It inherits the lifecycle of its parent object
and cannot exist as an independent Realm object. Realm automatically
deletes embedded objects if their parent object is deleted or when
overwritten by a new embedded object instance.

Note:
When you delete a Realm object, any embedded objects referenced by
that object are deleted with it. If you want the referenced objects
to persist after the deletion of the parent object, your type should
not be an embedded object at all. Use a regular Realm object with a to-one relationship instead.

Objective-C

You can define an embedded object by deriving from the
RLMEmbeddedObject class. You can use your
embedded object in another model as you would any other type.

objectivec
// Define an embedded object
@interface Address : RLMEmbeddedObject
@property NSString *street;
@property NSString *city;
@property NSString *country;
@property NSString *postalCode;
@end

// Enable Address for use in RLMArray
RLM_COLLECTION_TYPE(Address)

@implementation Address
@end

// Define an object with one embedded object
@interface Contact : RLMObject
@property NSString *name;

// Embed a single object.
@property Address *address;
@end

@implementation Contact
@end

// Define an object with an array of embedded objects
@interface Business : RLMObject
@property NSString *name;
// Embed an array of objects
@property RLMArray<Address *><Address> *addresses;
@end

Swift

You can define an embedded object by deriving from the
EmbeddedObject
class. You can use your embedded object in another model as you
would any other type.

swift
class Person: Object {
    @Persisted(primaryKey: true) var id = 0
    @Persisted var name = ""

    // To-many relationship - a person can have many dogs
    @Persisted var dogs: List<Dog>

    // Inverse relationship - a person can be a member of many clubs
    @Persisted(originProperty: "members") var clubs: LinkingObjects<DogClub>

    // Embed a single object.
    // Embedded object properties must be marked optional.
    @Persisted var address: Address?

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

class DogClub: Object {
    @Persisted var name = ""
    @Persisted var members: List<Person>

    // DogClub has an array of regional office addresses.
    // These are embedded objects.
    @Persisted var regionalOfficeAddresses: List<Address>

    convenience init(name: String, addresses: [Address]) {
        self.init()
        self.name = name
        self.regionalOfficeAddresses.append(objectsIn: addresses)
    }
}

class Address: EmbeddedObject {
    @Persisted var street: String?
    @Persisted var city: String?
    @Persisted var country: String?
    @Persisted var postalCode: String?
}

Guides Model Data Supported Types (`docs/guides/model-data/supported-types.md`)

Supported Types - Swift SDK

Collection Types

Realm has several types to represent groups of objects,
which we call collections. A collection is an object that contains
zero or more instances of one Realm type. Realm collections are homogenous:
all objects in a collection are of the same type.

You can filter and sort any collection using Realm's
query engine. Collections are
live, so they always reflect the current state
of the realm instance on the current thread. You can also
listen for changes in the collection by subscribing to collection
notifications.

All collection types conform to the RealmCollection protocol. This protocol inherits from
CollectionType, so you can use
a Realm collection as you would any other standard library
collections.

Using the RealmCollection protocol, you can write generic code that can
operate on any Realm collection:

swift
func operateOn<C: RealmCollection>(collection: C) {
    // Collection could be either Results or List
    print("operating on collection containing \(collection.count) objects")
}

Results and Sectioned Results

The Swift SDK Results collection is
a class representing objects retrieved from queries. A
[Results collection represents the
lazily-evaluated results of a query operation. Results are immutable:
you cannot add or remove elements to or from the results collection.
Results have an associated query that determines their contents.

The Swift SDK also provides SectionedResults,
a type-safe collection which holds ResultsSection as its elements.
Each ResultSection is a results
collection that contains only objects that belong to a given section key.

For example, an app that includes a contact list might use SectionedResults
to display a list of contacts divided into sections, where each section
contains all the contacts whose first name starts with the given letter.
The ResultsSection whose key is "L" would contain "Larry", "Liam",
and "Lisa".

Seealso:
Reads

Collections as Properties

The Swift SDK also offers several collection types you can use as properties
in your data model:

  1. List, a class representing
    to-many relationships in models.
  2. LinkingObjects, a class
    representing inverse relationships in models.
  3. MutableSet, a class representing
    a to-many relationship.
  4. Map, a class representing an associative array of key-value
    pairs with unique keys.
  5. AnyRealmCollection, a type-erased class that can forward calls to a concrete Realm collection like Results, List or LinkingObjects.

Collections are Live

Like live objects, Realm collections
are usually live:

  • Live results collections always reflect the current results of the associated query.
  • Live lists always reflect the current state of the relationship on the realm instance.

There are two cases when a collection is not live:

  • The collection is unmanaged. For example, a List property of
    a Realm object that has not been added to a realm yet
    or that has been copied from a realm is not live.
  • The collection is frozen.

Combined with collection notifications, live collections enable
clean, reactive code. For example, suppose your view displays the
results of a query. You can keep a reference to the results collection
in your view class, then read the results collection as needed without
having to refresh it or validate that it is up-to-date.

Important:
Since results update themselves automatically, do not
store the positional index of an object in the collection
or the count of objects in a collection. The stored index
or count value could be outdated by the time you use
it.

Supported Property Types

You can use the following types to define your object model
properties.

Property Cheat Sheet

Swift

Version changed: 10.10.0
@Persisted property declaration syntax

Type Required Optional
Bool @Persisted var boolName: Bool @Persisted var optBoolName: Bool?
Int, Int8, Int16, Int32, Int64 @Persisted var intName: Int @Persisted var optIntName: Int?
Float @Persisted var floatName: Float @Persisted var optFloatName: Float?
Double @Persisted var doubleName: Double @Persisted var optDoubleName: Double?
String @Persisted var stringName: String @Persisted var optStringName: String?
Data @Persisted var dataName: Data @Persisted var optDataName: Data?
Date @Persisted var dateName: Date @Persisted var optDateName: Date?
Decimal128 @Persisted var decimalName: Decimal128 @Persisted var optDecimalName: Decimal128?
UUID @Persisted var uuidName: UUID @Persisted var optUuidName: UUID?
ObjectId @Persisted var objectIdName: ObjectId @Persisted var optObjectIdName: ObjectId?
List @Persisted var listName: List<MyCustomObjectType> N/A
MutableSet @Persisted var mutableSetName: MutableSet<String> N/A
Map @Persisted var mapName: Map<String, String> N/A
AnyRealmValue @Persisted var anyRealmValueName: AnyRealmValue N/A
User-defined Object N/A @Persisted var optObjectPropertyName: MyCustomObjectType?
User-defined EmbeddedObject N/A @Persisted var optEmbeddedObjectPropertyName: MyEmbeddedObjectType?
User-defined Enums @Persisted var enumName: MyPersistableEnum @Persisted var optEnumName: MyPersistableEnum?

CGFloat properties are discouraged, as the type is not
platform independent.

To use Key-Value Coding with a user-defined object in the @Persisted
syntax, add the @objc attribute: @Persisted @objc var myObject: MyClass?

Setting Default Values

With the @Persisted property declaration syntax, you may see a
performance impact when setting default values for:

  • List
  • MutableSet
  • Dictionary
  • Decimal128
  • UUID
  • ObjectId

@Persisted var listProperty: List and @Persisted var listProperty = List() are both valid, and are functionally
equivalent. However, the second declaration will result in poorer
performance.

This is because the List is created when the parent object is
created, rather than lazily as needed. For most types, this is
a difference so small you can't measure it. For the types listed
here, you may see a performance impact when using the second
declaration style.

Objective-C

Type Required Optional
Boolean @property BOOL boolName; @property NSNumber<RLMBool> *optBoolName;
Integer @property int intName; @property NSNumber<RLMInt> *optIntName;
Float @property float floatName; @property NSNumber<RLMFloat> *optFloatName;
Double @property double doubleName; @property NSNumber<RLMDouble> *optDoubleName;
String @property NSString *stringName; @property NSString *optStringName;
Data @property NSData *dataName; @property NSData *optDataName;
Date @property NSDate *dateName; @property NSDate *optDateName;
Decimal128 @property RLMDecimal128 *decimalName; @property RLMDecimal128 *optDecimalName;
NSUUID @property NSUUID *uuidName; @property NSUUID *optUuidName;
RLMObjectId @property RLMObjectId *objectIdName; @property RLMObjectId *optObjectIdName;
RLMArray @property RLMArray<MyObject *><MyObject> *arrayName; N/A
RLMSet @property RLMSet<RLMString> *setName; N/A
RLMDictionary @property RLMDictionary<NSString *, NSString *><RLMString, RLMString> *dictionaryName; N/A
User-defined RLMObject N/A @property MyObject *optObjectPropertyName;
User-defined RLMEmbeddedObject N/A @property MyEmbeddedObject *optEmbeddedObjectPropertyName;

Additionally:

  • Integral types int, NSInteger, long, long long

CGFloat properties are discouraged, as the type is not
platform independent.

Swift Pre 10.10.0

Version changed: 10.8.0
RealmProperty replaces RealmOptional

Type Required Optional
Bool @objc dynamic var value = false let value = RealmProperty()
Int, Int8, Int16, Int32, Int64 @objc dynamic var value = 0 let value = RealmProperty()
Float @objc dynamic var value: Float = 0.0 let value = RealmProperty<Float?>()
Double @objc dynamic var value: Double = 0.0 let value = RealmProperty<Double?>()
String @objc dynamic var value = "" @objc dynamic var value: String? = nil
Data @objc dynamic var value = Data() @objc dynamic var value: Data? = nil
Date @objc dynamic var value = Date() @objc dynamic var value: Date? = nil
Decimal128 @objc dynamic var decimal: Decimal128 = 0 @objc dynamic var decimal: Decimal128?
UUID @objc dynamic var uuid = UUID() @objc dynamic var uuidOpt: UUID?
ObjectId @objc dynamic var objectId = ObjectId.generate() @objc dynamic var objectId: ObjectId?
List let value = List<Type>()
MutableSet let value = MutableSet<Type>()
Map let value = Map<String, String>()
AnyRealmValue let value = RealmProperty<AnyRealmValue>() N/A
User-defined Object N/A @objc dynamic var value: MyClass?

Additionally:

  • EmbeddedObject-derived types
  • Enum

You can use RealmProperty <T?> to
represent integers, doubles, and other types as optional.

CGFloat properties are discouraged, as the type is not
platform independent.

Unique Identifiers

Version added: 10.8.0
UUID type

ObjectId is a 12-byte unique value. UUID is a
16-byte globally-unique value. You can index
both types, and use either as a primary key.

Note:
When declaring default values for @Persisted UUID or ObjectId property
attributes, both of these syntax types are valid:

  • @Persisted var value: UUID
  • @Persisted var value = UUID()

However, the second will result in poorer performance. This is because the
latter creates a new identifier that is never used any time an object is
read from the realm, while the former only creates them when needed.

@Persisted var id: ObjectId has equivalent behavior to @objc dynamic var _id = ObjectId.generate(). They both make random ObjectIds.

@Persisted var _id = ObjectId() has equivalent behavior to @objc dynamic var _id = ObjectId(). They both make zero-initialized ObjectIds.

Size Limitations

Data and string properties cannot hold more than 16MB. To store
larger amounts of data, either:

  • Break the data into 16MB chunks, or
  • Store data directly on the file system and store paths to the files in the realm.

Realm throws a runtime exception if your app attempts to
store more than 16MB in a single property.

To avoid size limitations and a performance impact, it is best not to
store large blobs, such as image and video files, directly in a
realm. Instead, save the file to a file store and keep only the
location of the file and any relevant metadata in the realm.

AnyRealmCollection

To store a collection as a property or variable without needing to know
the concrete collection type, Swift's type system requires a type-erased
wrapper like AnyRealmCollection:

swift
class ViewController {
//    let collection: RealmCollection
//                    ^
//                    error: protocol 'RealmCollection' can only be used
//                    as a generic constraint because it has Self or
//                    associated type requirements
//
//    init<C: RealmCollection>(collection: C) where C.ElementType == MyModel {
//        self.collection = collection
//    }

    let collection: AnyRealmCollection<MyModel>

    init<C: RealmCollection & _ObjcBridgeable>(collection: C) where C.ElementType == MyModel {
        self.collection = AnyRealmCollection(collection)
    }
}

Mutable Set

Version added: 10.8.0

A MutableSet
collection represents a to-many relationship
containing distinct values. A MutableSet supports the following types
(and their optional versions):

  • Bool
  • Data
  • Date
  • Decimal128
  • Double
  • Float
  • Int
  • Int8
  • Int16
  • Int32
  • Int64
  • Object
  • ObjectId
  • String
  • UUID

Like Swift's Set, MutableSet is a
generic type that is parameterized on the type it stores. Unlike
native Swift collections,
Realm mutable sets are reference types, as opposed to value
types (structs).

You can only call the MutableSets mutation methods during a write
transaction. As a result, MutableSets are immutable if you open the
managing realm as a read-only realm.

You can filter and sort a MutableSet with the same predicates as Results. Like other
Realm collections, you can register a change listener on a MutableSet.

For example, a Dog class model might contain a MutableSet for
citiesVisited:

swift
class Dog: Object {
    @Persisted var name = ""
    @Persisted var currentCity = ""
    @Persisted var citiesVisited: MutableSet<String>
}

Note:
When declaring default values for @Persisted MutableSet property attributes,
both of these syntax types is valid:

  • @Persisted var value: MutableSet<String>
  • @Persisted var value = MutableSet<String>()

However, the second will result in significantly worse performance. This is
because the MutableSet is created when the parent object is created, rather than
lazily as needed.

Map/Dictionary

Version added: 10.8.0

The Map is an associative array that
contains key-value pairs with unique keys.

Like Swift's Dictionary,
Map is a generic type that is parameterized on its key and value
types. Unlike native Swift collections,
Realm Maps are reference types (classes), as opposed to
value types (structs).

You can declare a Map as a property of an object:

swift
class Dog: Object {
    @Persisted var name = ""
    @Persisted var currentCity = ""

    // Map of city name -> favorite park in that city
    @Persisted var favoriteParksByCity: Map<String, String>
}

Realm disallows the use of . or $ characters in map keys.
You can use percent encoding and decoding to store a map key that contains
one of these disallowed characters.

text
// Percent encode . or $ characters to use them in map keys
let mapKey = "New York.Brooklyn"
let encodedMapKey = "New York%2EBrooklyn"

Note:
When declaring default values for @Persisted Map property attributes, both
of these syntax types is valid:

  • @Persisted var value: Map<String, String>
  • @Persisted var value = Map<String, String>()

However, the second will result in significantly worse performance. This is
because the Map is created when the parent object is created, rather than
lazily as needed.

AnyRealmValue

Version changed: 10.51.0
AnyRealmValue properties can hold lists or maps of mixed data.

Version added: 10.8.0

AnyRealmValue is a Realm property type that can hold different
data types. Supported AnyRealmValue data types include:

  • Int
  • Float
  • Double
  • Decimal128
  • ObjectID
  • UUID
  • Bool
  • Date
  • Data
  • String
  • List
  • Map
  • Object

AnyRealmValue cannot hold a MutableSet or embedded object.

This mixed data type
is indexable, but you can't use it as a
primary key. Because null is a
permitted value, you can't declare an AnyRealmValue as optional.

swift
class Dog: Object {
    @Persisted var name = ""
    @Persisted var currentCity = ""

    @Persisted var companion: AnyRealmValue
}

Collections as Mixed

In version 10.51.0 and later, a AnyRealmValue data type can
contain collections (a list or map, but not a set) of AnyRealmValue
elements. You can use mixed collections to
model unstructured or variable data. For more information, refer to
Define Unstructured Data.

  • You can nest mixed collections up to 100 levels.
  • You can query mixed collection properties and
    register a listener for changes,
    as you would a normal collection.
  • You can find and update individual mixed collection elements
  • You cannot store sets or embedded objects in mixed collections.

To use mixed collections in your app, define the AnyRealmValue type
property in your data model.
Then, you can create the list or map collections like any other mixed data value.

Geospatial Data

Version added: 10.47.0

Geospatial data, or "geodata", specifies points and geometric objects on
the Earth's surface.

If you want to persist geospatial data, it must conform to the
GeoJSON spec.

To persist geospatial data with the Swift SDK, create a GeoJSON-compatible
embedded class that you can use in your data model.

Your custom embedded object must contain the two fields required by the
GeoJSON spec:

  • A field of type String property that maps to a type property with
    the value of "Point": @Persisted var type: String = "Point"
  • A field of type List<Double> that maps to a coordinates
    property containing a latitude/longitude pair:
    @Persisted private var coordinates: List<Double>
swift
class CustomGeoPoint: EmbeddedObject {
    @Persisted private var type: String = "Point"
    @Persisted private var coordinates: List<Double>

    public var latitude: Double { return coordinates[1] }
    public var longitude: Double { return coordinates[0] }

    convenience init(_ latitude: Double, _ longitude: Double) {
        self.init()
        // Longitude comes first in the coordinates array of a GeoJson document
        coordinates.append(objectsIn: [longitude, latitude])
    }
}

Map Unsupported Types to Supported Types

Version added: 10.20.0

You can use Type Projection to persist unsupported types as supported types
in Realm. This enables you to work with Swift types that Realm
does not support, but store them as types that Realm does support. You could
store a URL as a String, for example, but read it from
Realm and use it in your application as though it were a URL.

Declare Type Projections

To use type projection with Realm:

  1. Use one of Realm's custom type protocols to map an unsupported data type
    to a type that Realm supports
  2. Use the projected types as @Persisted properties in the Realm object
    model

Conform to the Type Projection Protocol

You can map an unsupported data type to a type that Realm supports using one of the Realm type projection protocols.

The Swift SDK provides two type projection protocols:

  • CustomPersistable
  • FailableCustomPersistable

Use CustomPersistable
when there is no chance the conversion can fail.

Use FailableCustomPersistable
when it is possible for the conversion to fail.

text
/* Detailed source-code truncated for AI context efficiency. */

Seealso:
These are protocols modeled after Swift's built-in RawRepresentable.

Supported PersistedTypes

The PersistedType can use any of the primitive types that the
Swift SDK supports. It can also be
an Embedded Object.

PersistedType cannot be an optional or a collection. However you can use the mapped type as an
optional or collection property in your object model.

swift
extension URL: FailableCustomPersistable {
   // The `PersistedType` cannot be an optional, so this is not a valid
   // conformance to the FailableCustomPersistable protocol.
   public typealias PersistedType = String?
   ...
}

class Club: Object {
   @Persisted var id: ObjectId
   @Persisted var name: String
   // Although the `PersistedType` cannot be optional, you can use the
   // custom-mapped type as an optional in your object model.
   @Persisted var url: URL?
}

Use Type Projection in the Model

A type that conforms to one of the type projection protocols can be used with
the @Persisted property declaration syntax introduced in Swift SDK
version 10.10.0. It does not work with the @objc dynamic syntax.

You can use projected types for:

  • Top-level types
  • Optional versions of the type
  • The types for a collection

When using a FailableCustomPersistable as a property, define it as an
optional property. When it is optional, the FailableCustomPersistable
protocol maps invalid values to nil. When it is a required property, it is
force-unwrapped. If you have a value that can't be converted to the projected
type, reading that property throws an unwrapped fail exception.

swift
class Club: Object {
    @Persisted var id: ObjectId
    @Persisted var name: String
    // Since we declared the URL as a FailableCustomPersistable,
    // it must be optional.
    @Persisted var url: URL?
    // Here, the `location` property maps to an embedded object.
    // We can declare the property as required.
    // If the underlying field contains nil, this becomes
    // a default-constructed instance of CLLocationCoordinate
    // with field values of `0`.
    @Persisted var location: CLLocationCoordinate2D
}

public class Location: EmbeddedObject {
    @Persisted var latitude: Double
    @Persisted var longitude: Double
}

When your model contains projected types, you can create the object with values using the persisted type, or
by assigning to the field properties of an initialized object using the
projected types.

swift
// Initialize objects and assign values
let club = Club(value: ["name": "American Kennel Club", "url": "https://akc.org"])
let club2 = Club()
club2.name = "Continental Kennel Club"
// When assigning the value to a type-projected property, type safety
// checks for the mapped type - not the persisted type.
club2.url = URL(string: "https://ckcusa.com/")!
club2.location = CLLocationCoordinate2D(latitude: 40.7509, longitude: 73.9777)

Type Projection in the Schema

When you declare your type as conforming to a type projection protocol, you
specify the type that should be persisted in realm. For example, if
you map a custom type URL to a persisted type of String, a URL
property appears as a String in the schema, and dynamic access to the
property acts on strings.

The schema does not directly represent mapped types. Changing a property
from its persisted type to its mapped type, or vice versa, does not require
a migration.


Guides Realm Files Bundle A Realm (`docs/guides/realm-files/bundle-a-realm.md`)

Bundle a Realm File - Swift SDK

Realm supports bundling realm files. When you bundle
a realm file, you include a database and all of its data in your
application download.

This allows users to start applications for the first time with a set of
initial data.

Overview

To create and bundle a realm file with your application:

  1. Create a realm file that
    contains the data you'd like to bundle.
  2. Bundle the realm file in your
    production application.
  3. In your production application,
    open the realm from the bundled asset file.

Create a Realm File for Bundling

  1. Build a temporary realm app that shares the data model of your
    application.
  2. Open a realm and add the data you wish to bundle.
  3. Use the writeCopy(configuration:)
    method to copy the realm to a new file:

Tip:
If your app accesses Realm in an async/await context, mark the code
with @MainActor to avoid threading-related crashes.

writeCopy(configuration: )
automatically compacts your realm to the smallest possible size before
copying.

Bundle a Realm File in Your Production Application

Now that you have a copy of the realm that contains the initial data,
bundle it with your production application. At a broad level, this entails:

  1. Create a new project with the exact same data models as your production
    app. Open a realm and add the data you wish to bundle. Since realm
    files are cross-platform, you can do this in a macOS app.
  2. Drag the compacted copy of your realm file to your production app's Xcode
    Project Navigator.
  3. Go to your app target's Build Phases tab in Xcode. Add the
    realm file to the Copy Bundle Resources build phase.
  4. At this point, your app can access the bundled realm file. Find its path
    with Bundle.main.path(forResource:ofType).

You can open the realm at the bundle path directly if the
readOnly property is set to true on the
Realm.Configuration. If
you want to modify the bundled realm, first copy the bundled file to
your app's Documents folder with setting seedFilePath with the URL of the bundled Realm on your Configuration.

Tip:
See the migration sample app for a
complete working app that uses a bundled local realm.

Open a Realm from a Bundled Realm File

Now that you have a copy of the realm included with your production
application, you need to add code to use it. Use the seedFilePath
method when configuring your realm to open the realm
from the bundled file:

Tip:
If your app accesses Realm in an async/await context, mark the code
with @MainActor to avoid threading-related crashes.

text
/* Detailed source-code truncated for AI context efficiency. */

Guides Realm Files Compacting (`docs/guides/realm-files/compacting.md`)

Reduce Realm File Size - Swift SDK

Overview

The size of a realm file is always larger than the total
size of the objects stored within it. This architecture enables some of
realm's great performance, concurrency, and safety benefits.

Realm writes new data within unused space tracked inside a
file. In some situations, unused space may comprise a significant
portion of a realm file. Realm's default behavior is to automatically
compact a realm file to prevent it from growing too large.
You can use manual compaction strategies when
automatic compaction is not sufficient for your use case
or you're using a version of the SDK that doesn't have automatic
compaction.

Realm File Size

Generally, a realm file takes less space on disk than a
comparable SQLite database. These factors can affect
file size:

  • Pinning transactions
  • Threading
  • Dispatch Queues

When you consider reducing the file size through compacting, there are a
couple of things to keep in mind:

  • Compacting can be a resource-intensive operation
  • Compacting can block the UI thread

Because of these factors, you probably don't want to compact a realm every
time you open it, but instead want to consider when to compact a
realm. This varies based on your application's
platform and usage patterns. When deciding when to compact, consider iOS
file size limitations.

Avoid Pinning Transactions

Realm ties read transaction lifetimes to the memory lifetime
of realm instances. Avoid "pinning" old Realm transactions.
Use auto-refreshing realms, and wrap the use of Realm APIs
from background threads in explicit autorelease pools.

Threading

Realm updates the version of your data that it accesses at
the start of a run loop iteration. While this gives you a consistent
view of your data, it has file size implications.

Imagine this scenario:

  • Thread A: Read some data from a realm, and then block the thread on a
    long-running operation.
  • Thread B: Write data on another thread.
  • Thread A: The version on the read thread isn't updated. Realm has
    to hold intermediate versions of the data, growing in file size with
    every write.

To avoid this issue, call invalidate()
on the realm. This tells the realm that you no longer need the
objects you've read so far. This frees realm from tracking
intermediate versions of those objects. The next time you access it,
realm will have the latest version of the objects.

You can also use these two methods to compact your Realm:

  • Set shouldCompactOnLaunch
    in the configuration
  • Use writeCopy(toFile:encryptionKey:)

Seealso:
Advanced Guides: Threading

Dispatch Queues

When accessing Realm using Grand Central Dispatch, you may see similar file growth. A dispatch
queue's autorelease pool may not drain immediately upon executing your
code. Realm cannot reuse intermediate versions of the data until the
dispatch pool deallocates the realm object. Use an explicit
autorelease pool when accessing realm from a dispatch queue.

Automatic Compaction

Version added: 10.35.0

The SDK automatically compacts Realm files in the background by continuously reallocating data
within the file and removing unused file space. Automatic compaction is sufficient for minimizing the Realm file size
for most applications.

Automatic compaction begins when the size of unused space in the file is more than twice the size of user
data in the file. Automatic compaction only takes place when
the file is not being accessed.

Manual Compaction

Manual compaction can be used for applications that
require stricter management of file size or that use an older version
of the SDK that does not support automatic compaction.

Realm manual compaction works by:

  1. Reading the entire contents of the realm file
  2. Writing the contents to a new file at a different location
  3. Replacing the original file

If the file contains a lot of data, this can be an expensive operation.

Use shouldCompactOnLaunch()
(Swift) or shouldCompactOnLaunch
(Objective-C) on a realm's configuration object to compact a realm.
Specify conditions to execute this method, such as:

  • The size of the file on disk
  • How much free space the file contains

For more information about the conditions to execute in the method, see:
Tips for Using Manual Compaction.

Important:
Compacting cannot occur while a realm is being accessed,
regardless of any configuration settings.

Objective-C

objectivec
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.shouldCompactOnLaunch = ^BOOL(NSUInteger totalBytes, NSUInteger usedBytes) {
    // totalBytes refers to the size of the file on disk in bytes (data + free space)
    // usedBytes refers to the number of bytes used by data in the file

    // Compact if the file is over 100MB in size and less than 50% 'used'
    NSUInteger oneHundredMB = 100 * 1024 * 1024;
    return (totalBytes > oneHundredMB) && ((double)usedBytes / totalBytes) < 0.5;
};

NSError *error = nil;
// Realm is compacted on the first open if the configuration block conditions were met.
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (error) {
    // handle error compacting or opening Realm
}

Swift

swift
let config = Realm.Configuration(shouldCompactOnLaunch: { totalBytes, usedBytes in
    // totalBytes refers to the size of the file on disk in bytes (data + free space)
    // usedBytes refers to the number of bytes used by data in the file

    // Compact if the file is over 100MB in size and less than 50% 'used'
    let oneHundredMB = 100 * 1024 * 1024
    return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
})
do {
    // Realm is compacted on the first open if the configuration block conditions were met.
    let realm = try Realm(configuration: config)
} catch {
    // handle error compacting or opening Realm
}

Compact a Realm Asynchronously

When you use the Swift async/await syntax to open a realm asynchronously,
you can compact a realm in the background.

swift
func testAsyncCompact() async {
    let config = Realm.Configuration(shouldCompactOnLaunch: { totalBytes, usedBytes in
        // totalBytes refers to the size of the file on disk in bytes (data + free space)
        // usedBytes refers to the number of bytes used by data in the file

        // Compact if the file is over 100MB in size and less than 50% 'used'
        let oneHundredMB = 100 * 1024 * 1024
        return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
    })

    do {
        // Realm is compacted asynchronously on the first open if the
        // configuration block conditions were met.
        let realm = try await Realm(configuration: config)
    } catch {
        // handle error compacting or opening Realm
    }
}

Starting with Realm Swift SDK Versions 10.15.0 and 10.16.0, many of the
Realm APIs support the Swift async/await syntax. Projects must
meet these requirements:

Swift SDK Version Swift Version Requirement Supported OS
10.25.0 Swift 5.6 iOS 13.x
10.15.0 or 10.16.0 Swift 5.5 iOS 15.x

If your app accesses Realm in an async/await context, mark the code
with @MainActor to avoid threading-related crashes.

Make a Compacted Copy

You can save a compacted (and optionally encrypted) copy of a realm to another file location
with the Realm.writeCopy(toFile:encryptionKey:)
method. The destination file cannot already exist.

Important:
Avoid calling this method within a write transaction. If called within a write transaction, this
method copies the absolute latest data. This includes any
uncommitted changes you made in the transaction before this
method call.

Tips for Using Manual Compaction

Compacting a realm can be an expensive operation that can block
the UI thread. Your application should not compact every time you open
a realm. Instead, try to optimize compacting so your application does
it just often enough to prevent the file size from growing too large.
If your application runs in a resource-constrained environment,
you may want to compact when you reach a certain file size or when the
file size negatively impacts performance.

These recommendations can help you optimize manual compaction for your
application:

  • Set the max file size to a multiple of your average realm state
    size. If your average realm state size is 10MB, you might set the max
    file size to 20MB or 40MB, depending on expected usage and device
    constraints.
  • As a starting point, compact realms when more than 50% of the realm file
    size is no longer in use. Divide the currently used bytes by the total
    file size to determine the percentage of space that is currently used.
    Then, check for that to be less than 50%. This means that greater than
    50% of your realm file size is unused space, and it is a good time to
    compact. After experimentation, you may find a different percentage
    works best for your application.

These calculations might look like this in your shouldCompactOnLaunch
callback:

swift
// Set a maxFileSize equal to 20MB in bytes
let maxFileSize = 20 * 1024 * 1024
// Check for the realm file size to be greater than the max file size,
// and the amount of bytes currently used to be less than 50% of the
// total realm file size
return (realmFileSizeInBytes > maxFileSize) && (Double(usedBytes) / Double(realmFileSizeInBytes)) < 0.5

Experiment with conditions to find the right balance of how often to
compact realm files in your application.

Consider iOS File Size Limitations

A large realm file can impact the performance and reliability of
your app. Any single realm file cannot be larger than the amount
of memory your application would be allowed to map in iOS. This limit
depends on the device and on how fragmented the memory space is at
that point in time.

If you need to store more data, map it over multiple realm files.

Summary

  • Realm's architecture enables threading-related benefits,
    but can result in file size growth.
  • Automatic compaction manages file size growth when the file is not being accessed.
  • Manual compaction strategies like shouldCompactOnLaunch() can be used when automatic compaction does not meet application needs.
  • Compacting cannot occur if another process is accessing the realm.
  • You can compact a realm in the background when you use async/await syntax.

Guides Realm Files Configure And Open A Realm (`docs/guides/realm-files/configure-and-open-a-realm.md`)

Configure & Open a Realm - Swift SDK

When you open a realm, you can pass a Realm.Configuration that specifies additional details
about how to configure the realm file. This includes things like:

  • Pass a fileURL or in-memory identifier to customize how the realm is stored on device
  • Specify the realm use only a subset of your app's classes
  • Whether and when to compact a realm to reduce its file size
  • Pass an encryption key to encrypt a realm
  • Provide a schema version or migration block when making schema changes

Open a Realm

You can open a local realm with several different configuration
options:

  • No configuration - i.e. default configuration
  • Specify a file URL for the realm
  • Open the realm only in memory, without saving a file to the file system

Open a Default Realm or Realm at a File URL

Objective-C

You can open the default realm with [+[RLMRealm
defaultRealm]].

You can also pass a RLMRealmConfiguration object to
+[RLMRealm realmWithConfiguration:error:]
to open a realm at a specific file URL or in memory.

You can set the default realm configuration by passing a
RLMRealmConfiguration instance to
+[RLMRealmConfiguration setDefaultConfiguration:].

objectivec
// Open the default realm
RLMRealm *defaultRealm = [RLMRealm defaultRealm];

// Open the realm with a specific file URL, for example a username
NSString *username = @"GordonCole";
RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
configuration.fileURL = [[[configuration.fileURL URLByDeletingLastPathComponent]
                         URLByAppendingPathComponent:username]
                         URLByAppendingPathExtension:@"realm"];
NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration
                                             error:&error];

Swift

You can open a realm with the Realm() initializer.
If you omit the Realm.Configuration parameter, you will open the
default realm.

You can set the default realm configuration by assigning a new
Realm.Configuration instance to the
Realm.Configuration.defaultConfiguration
class property.

swift
// Open the default realm
let defaultRealm = try! Realm()

// Open the realm with a specific file URL, for example a username
let username = "GordonCole"
var config = Realm.Configuration.defaultConfiguration
config.fileURL!.deleteLastPathComponent()
config.fileURL!.appendPathComponent(username)
config.fileURL!.appendPathExtension("realm")
let realm = try! Realm(configuration: config)

Open an In-Memory Realm

You can open a realm entirely in memory, which will not create a
.realm file or its associated auxiliary files. Instead the SDK stores objects in memory while the
realm is open and discards them immediately when all instances are
closed.

Objective-C

Set the inMemoryIdentifier
property of the realm configuration.

objectivec
// Open the realm with a specific in-memory identifier.
NSString *identifier = @"MyRealm";
RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
configuration.inMemoryIdentifier = identifier;
// Open the realm
RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];

Swift

Set the inMemoryIdentifier
property of the realm configuration.

swift
// Open the realm with a specific in-memory identifier.
let identifier = "MyRealm"
let config = Realm.Configuration(
    inMemoryIdentifier: identifier)
// Open the realm
let realm = try! Realm(configuration: config)

Important:
When all in-memory realm instances with a particular identifier
go out of scope, Realm deletes all data in that
realm. To avoid this, hold onto a strong reference to any
in-memory realms during your app's lifetime.

Open a Realm with Swift Concurrency Features

You can use Swift's async/await syntax to open a MainActor-isolated realm,
or specify an actor when opening a realm asynchronously:

swift
@MainActor
func mainThreadFunction() async throws {
    // These are identical: the async init produces a
    // MainActor-isolated Realm if no actor is supplied
    let realm1 = try await Realm()
    let realm2 = try await Realm(actor: MainActor.shared)

    try await useTheRealm(realm: realm1)
}

Or you can define a custom realm actor to manage all of your realm operations:

text
/* Detailed source-code truncated for AI context efficiency. */

An actor-isolated realm may be used with either local or global actors.

swift
// A simple example of a custom global actor
@globalActor actor BackgroundActor: GlobalActor {
    static var shared = BackgroundActor()
}

@BackgroundActor
func backgroundThreadFunction() async throws {
    // Explicitly specifying the actor is required for anything that is not MainActor
    let realm = try await Realm(actor: BackgroundActor.shared)
    try await realm.asyncWrite {
        _ = realm.create(Todo.self, value: [
            "name": "Pledge fealty and service to Gondor",
            "owner": "Pippin",
            "status": "In Progress"
        ])
    }
    // Thread-confined Realms would sometimes throw an exception here, as we
    // may end up on a different thread after an `await`
    let todoCount = realm.objects(Todo.self).count
    print("The number of Realm objects is: \(todoCount)")
}

@MainActor
func mainThreadFunction() async throws {
    try await backgroundThreadFunction()
}

For more information about working with actor-isolated realms, refer to
Use Realm with Actors - Swift SDK.

Close a Realm

There is no need to manually close a realm in Swift or Objective-C.
When a realm goes out of scope and is removed from memory due to
ARC,
the realm is closed.

Handle Errors When Accessing a Realm

Objective-C

To handle errors when accessing a realm, provide an
NSError pointer to the error parameter:

objectivec
NSError *error = nil;
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
    // Handle error
    return;
}
// Use realm

Swift

To handle errors when accessing a realm, use Swift's built-in
error handling mechanism:

swift
do {
    let realm = try Realm()
    // Use realm
} catch let error as NSError {
    // Handle error
}

Provide a Subset of Classes to a Realm

Tip:
Some applications, such as watchOS apps and iOS app extensions, have
tight constraints on their memory footprints. To optimize your data
model for low-memory environments, open the realm with a subset
of classes.

Objective-C

By default, the Swift SDK automatically adds all
RLMObject- and
RLMEmbeddedObject-derived
classes in your executable to the realm schema. You can control
which objects get added by setting the objectClasses
property of the RLMRealmConfiguration object.

objectivec
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];

// Given a RLMObject subclass called `Task`
// Limit the realm to only the Task object. All other
// Object- and EmbeddedObject-derived classes are not added.
config.objectClasses = @[[Task class]];

NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];

if (error != nil) {
    // Something went wrong
} else {
    // Use realm
}

Swift

By default, the Swift SDK automatically adds all
Object- and
EmbeddedObject-derived
classes in your executable to the realm schema. You can control
which objects get added by setting the objectTypes
property of the Realm.Configuration object.

swift
var config = Realm.Configuration.defaultConfiguration

// Given: `class Dog: Object`
// Limit the realm to only the Dog object. All other
// Object- and EmbeddedObject-derived classes are not added.
config.objectTypes = [Dog.self]

let realm = try! Realm(configuration: config)

Initialize Properties Using Realm APIs

You might define properties whose values are initialized using
Realm APIs. For example:

swift
class SomeSwiftType {
    let persons = try! Realm().objects(Person.self)
    // ...
}

If this initialization code runs before you set up your Realm
configurations, you might get unexpected behavior. For example, if you
set a migration block for the default realm
configuration in applicationDidFinishLaunching(), but you create an
instance of SomeSwiftType before
applicationDidFinishLaunching(), you might be accessing your
realm before it has been correctly configured.

To avoid such issues, consider doing one of the following:

  • Defer instantiation of any type that eagerly initializes properties using Realm APIs until after your app has completed setting up its realm configurations.
  • Define your properties using Swift's lazy keyword. This allows you to safely instantiate such types at any time during your application's lifecycle, as long as you do not attempt to access your lazy properties until after your app has set up its realm configurations.
  • Only initialize your properties using Realm APIs that explicitly take in user-defined configurations. You can be sure that the configuration values you are using have been set up properly before they are used to open realms.

Use Realm When the Device Is Locked

By default, iOS 8 and above encrypts app files using
NSFileProtection whenever the device is locked. If your app attempts
to access a realm while the device is locked, you might see the
following error:

text
open() failed: Operation not permitted

To handle this, downgrade the file protection of the folder containing
the Realm files.

Tip:
If you reduce iOS file encryption, consider using Realm's
built-in encryption to secure your data
instead.

This example shows how to apply a less strict protection level to the
parent directory of the default realm.

swift
let realm = try! Realm()

// Get the realm file's parent directory
let folderPath = realm.configuration.fileURL!.deletingLastPathComponent().path

// Disable file protection for this directory after the user has unlocked the device once
try! FileManager.default.setAttributes([FileAttributeKey.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication],
                                       ofItemAtPath: folderPath)

Realm may create and delete auxiliary files at any time.
Instead of downgrading file protection on the files, apply it to the
parent folder. This way, the file protection applies to all relevant
files regardless of creation time.


Guides Realm Files Delete A Realm (`docs/guides/realm-files/delete-a-realm.md`)

Delete a Realm File - Swift SDK

In some cases, you may want to completely delete a realm file from disk.

Realm avoids copying data into memory except when absolutely required.
As a result, all objects managed by a realm have references to the file
on disk. Before you can safely delete the file, you must ensure the
deallocation of these objects:

  • All objects read from or added to the realm
  • All List and Results objects
  • All ThreadSafeReference objects
  • The realm itself

Warning:
If you delete a realm file or any of its auxiliary files while one or
more instances of the realm are open, you might corrupt the realm or
disrupt sync.

Delete a Realm File to Avoid Migration

If you iterate rapidly as you develop your app, you may want to delete a
realm file instead of migrating it when you make schema changes. The Realm
configuration provides a deleteRealmIfMigrationNeeded
parameter to help with this case.

When you set this property to true, the SDK deletes the realm file when
a migration would be required. Then, you can create objects that match the
new schema instead of writing migration blocks for development or test data.

swift
do {
    // Delete the realm if a migration would be required, instead of migrating it.
    // While it's useful during development, do not leave this set to `true` in a production app!
    let configuration = Realm.Configuration(deleteRealmIfMigrationNeeded: true)
    let realm = try Realm(configuration: configuration)
} catch {
    print("Error opening realm: \(error.localizedDescription)")
}

Delete a Realm File

In practice, there are two safe times to delete the realm file:

  1. On application startup before ever opening the realm.
  2. After only having opened the realm within an explicit autorelease pool, which ensures deallocation of all of objects within it.

Guides Realm Files Encrypt A Realm (`docs/guides/realm-files/encrypt-a-realm.md`)

Encrypt a Realm - Swift SDK

Overview

You can encrypt the realm file on disk with AES-256 +
SHA-2 by supplying a 64-byte encryption key when opening a
realm.

Realm transparently encrypts and decrypts data with standard
AES-256 encryption using the
first 256 bits of the given 512-bit encryption key. Realm
uses the other 256 bits of the 512-bit encryption key to validate
integrity using a hash-based message authentication code
(HMAC)
.

Warning:
Do not use cryptographically-weak hashes for realm encryption keys.
For optimal security, we recommend generating random rather than derived
encryption keys.

Note:
You must encrypt a realm the first time you open it.
If you try to open an existing unencrypted realm using a configuration
that contains an encryption key, Realm throws an error.

Considerations

The following are key impacts to consider when encrypting a realm.

Storing & Reusing Keys

You must pass the same encryption key every time you open the encrypted realm.
If you don't provide a key or specify the wrong key for an encrypted
realm, the Realm SDK throws an error.

Apps should store the encryption key in the Keychain so that other apps
cannot read the key.

Performance Impact

Reads and writes on encrypted realms can be up to 10% slower than unencrypted realms.

Accessing an Encrypted Realm from Multiple Processes

Version changed: 10.38.0

Starting with Realm Swift SDK version 10.38.0, Realm supports opening
the same encrypted realm in multiple processes.

If your app uses Realm Swift SDK version 10.37.2 or earlier, attempting to
open an encrypted realm from multiple processes throws this error:
Encrypted interprocess sharing is currently unsupported.

Apps using earlier SDK versions have two options to work with realms in
multiple processes:

  • Use an unencrypted realm.
  • Store data that you want to encrypt as NSData properties on realm
    objects. Then, you can encrypt and decrypt individual fields.

One possible tool to encrypt and decrypt fields is Apple's
CryptoKit framework
. You can use Swift
Crypto
to simplify app
development with CryptoKit.

Example

The following code demonstrates how to generate an encryption key and
open an encrypted realm:

Objective-C

objectivec
// Generate a random encryption key
NSMutableData *key = [NSMutableData dataWithLength:64];
(void)SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);

// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.encryptionKey = key;

NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    NSLog(@"Error opening realm: %@", error);
} else {
    // Use the realm as normal...
}

Swift

swift
// Generate a random encryption key
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { (pointer: UnsafeMutableRawBufferPointer) in
    SecRandomCopyBytes(kSecRandomDefault, 64, pointer.baseAddress!) }

// Configure for an encrypted realm
var config = Realm.Configuration(encryptionKey: key)

do {
    // Open the encrypted realm
    let realm = try Realm(configuration: config)
    // ... use the realm as normal ...
} catch let error as NSError {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    fatalError("Error opening realm: \(error.localizedDescription)")
}

The following Swift example demonstrates how to store and retrieve a
generated key from the Keychain:

text
/* Detailed source-code truncated for AI context efficiency. */

Guides Realm Files Realm Files (`docs/guides/realm-files/realm-files.md`)

Work with Realm Files - Swift SDK

A realm is the core data structure used to organize data in
Realm. A realm is a collection of the objects that you use
in your application, called Realm objects, as well as additional metadata
that describe the objects. To learn how to define a Realm object, see
Define an Object Model.

Realm Files

Realm stores a binary encoded version of every object and type in a
realm in a single .realm file. The file is located at a specific
path that you can define when you open the
realm. You can open, view, and edit the contents of these files with
Realm Studio.

In-Memory Realms

You can also open a realm entirely in memory, which does not create a .realm
file or its associated auxiliary files. Instead the SDK stores objects in memory
while the realm is open and discards them immediately when all instances are
closed.

See:
To open an in-memory realm, refer to Open an In-Memory Realm.

Default Realm

Calling Realm() or
RLMRealm opens the default realm.
This method returns a realm object that maps to a file named
default.realm. You can find this file:

  • iOS: in the Documents folder of your app
  • macOS: in the Application Support folder of your app

See:
To open a default realm, refer to Open a Default Realm or Realm at a File URL.

Auxiliary Realm Files

Realm creates additional files for each realm:

  • realm files, suffixed with "realm", e.g. default.realm:
    contain object data.
  • lock files, suffixed with "lock", e.g. default.realm.lock:
    keep track of which versions of data in a realm are
    actively in use. This prevents realm from reclaiming storage space
    that is still used by a client application.
  • note files, suffixed with "note", e.g. default.realm.note:
    enable inter-thread and inter-process notifications.
  • management files, suffixed with "management", e.g. default.realm.management:
    internal state management.

Deleting these files has important implications.
For more information about deleting .realm or auxiliary files, see:
Delete a Realm

Find a Realm File Path

The realm file is located at a specific path that you can optionally define
when you open the realm.

swift
// Get on-disk location of the default Realm
let realm = try! Realm()
print("Realm is located at:", realm.configuration.fileURL!)

See:
To open a realm at a specific path, refer to Open a Default Realm or Realm at a File URL.


Guides Realm Files Tvos (`docs/guides/realm-files/tvos.md`)

Build for tvOS

Overview

This page details considerations when using Realm on tvOS.

Seealso:
Install the SDK for iOS, macOS, tvOS, and watchOS

Avoid Storing Important User Data

Avoid storing important user data in a realm on tvOS. Instead, it's
best to treat Realm as a rebuildable cache.

Note:
The reason for this has to do with where Realm writes its
Realm files. On other Apple platforms,
Realm writes its Realm files to the "Documents"
directory. Because tvOS restricts writes to that directory, the
default Realm file location on tvOS is instead NSCachesDirectory.
tvOS can purge files in that directory at any time, so reliable
long-term persistence is not possible.

You can also use Realm as an initial data source by
bundling prebuilt Realm files in your app.
Note that the App Store guidelines limit your
app size to 4GB.

Tip:
Browse our tvOS examples for sample tvOS apps
that demonstrate how to use Realm as an offline cache.

Share Realm Files with TV Services Extensions

To share a Realm file between a tvOS app and a
TV services extension such as Top Shelf, use the
Library/Caches/ directory in the shared container for the
application group:

swift
let fileUrl = FileManager.default
    .containerURL(forSecurityApplicationGroupIdentifier: "group.com.mongodb.realm.examples.extension")!
    .appendingPathComponent("Library/Caches/default.realm")

Guides Swiftui Configure And Open Realm (`docs/guides/swiftui/configure-and-open-realm.md`)

Configure and Open a Realm - SwiftUI

The Swift SDK provides property wrappers to open a realm in a
SwiftUI-friendly way.

You can:

  • Implicitly open a realm
    with a defaultConfiguration or specify a different configuration.

Open a Realm with a Configuration

When you use @ObservedRealmObject
or @ObservedResults, these
property wrappers implicitly open a realm and retrieve the specified
objects or results.

swift
// Implicitly use the default realm's objects(Dog.self)
@ObservedResults(Dog.self) var dogs

Note:
The @ObservedResults property wrapper is intended for use in a
SwiftUI View. If you want to observe results in a view model, register
a change listener.

When you do not specify a configuration, these property wrappers use the
defaultConfiguration.
You can set the defaultConfiguration
globally, and property wrappers across the app can use that configuration
when they implicitly open a realm.

You can provide alternative configurations that the property wrappers use
to implicitly open the realm.
To do this, create explicit configurations.
Then, use environment injection to pass the respective configurations
to the views that need them.
Passing a configuration to a view where property wrappers open a realm
uses the passed configuration instead of the defaultConfiguration.


Guides Swiftui Filter Data (`docs/guides/swiftui/filter-data.md`)

Filter Data - SwiftUI

Observe in SwiftUI Views

The @ObservedResults property wrapper used in the examples on this page
is intended for use in a SwiftUI View. If you want to observe results
in a view model instead, register a change listener.

Search a Realm Collection

Version added: 10.19.0

The Realm Swift SDK allows you to extend .searchable.
When you use ObservedResults
to query a realm, you can specify collection and keypath in the result set
to mark it as searchable.

The collection is the bound collection represented by your ObservedResults
query. In this example, it is the dogs variable that represents the
collection of all Dog objects in the realm.

The keypath is the object property that you want to search. In this
example, we search the dogs collection by dog name. The Realm Swift
.searchable implementation only supports keypaths with String types.

swift
struct SearchableDogsView: View {
    @ObservedResults(Dog.self) var dogs
    @State private var searchFilter = ""
    
    var body: some View {
        NavigationView {
            // The list shows the dogs in the realm.
            List {
                ForEach(dogs) { dog in
                    DogRow(dog: dog)
                }
            }
            .searchable(text: $searchFilter,
                        collection: $dogs,
                        keyPath: \.name) {
                ForEach(dogs) { dogsFiltered in
                    Text(dogsFiltered.name).searchCompletion(dogsFiltered.name)
                }
            }
        }
    }
}

Filter or Query a Realm with ObservedResults

The @ObservedResults property wrapper
opens a realm and returns all objects of the specified type. However, you
can filter or query @ObservedResults to use only a subset of the objects
in your view.

Seealso:
For more information about the query syntax and types of queries that Realm
supports, see: Read and Filter Data.

Filter with an NSPredicate

To filter @ObservedResults using the NSPredicate Query API, pass an NSPredicate as an argument to filter:

swift
struct FilterDogsViewNSPredicate: View {
    @ObservedResults(Dog.self, filter: NSPredicate(format: "weight > 40")) var dogs
    
    var body: some View {
        NavigationView {
            // The list shows the dogs in the realm.
            List {
                ForEach(dogs) { dog in
                    DogRow(dog: dog)
                }
            }
        }
    }
}

Query with the Realm Type-Safe Query API

Version added: 10.24.0
Use where to perform type-safe queries on ObservedResults.

To use @ObservedResults with the Realm Type-Safe Query API, pass a query in a closure as an argument to
where:

swift
struct FilterDogsViewTypeSafeQuery: View {
    @ObservedResults(Dog.self, where: ( { $0.weight > 40 } )) var dogs
    
    var body: some View {
        NavigationView {
            // The list shows the dogs in the realm.
            List {
                ForEach(dogs) { dog in
                    DogRow(dog: dog)
                }
            }
        }
    }
}

Section Filtered Results

Version added: 10.29.0

The @ObservedSectionedResults
property wrapper opens a realm and returns all objects of the specified type,
divided into sections by the specified key path. Similar to
@ObservedResults above, you can filter or query @ObservedSectionedResults
to use only a subset of the objects in your view:

swift
@ObservedSectionedResults(Dog.self,
                          sectionKeyPath: \.firstLetter,
                          where: ( { $0.weight > 40 } )) var dogs

Guides Swiftui Pass Realm Data Between Views (`docs/guides/swiftui/pass-realm-data-between-views.md`)

Pass Realm Data Between SwiftUI Views

The Realm Swift SDK provides several ways to pass realm data between views:

  • Pass Realm objects to a view
  • Use environment injection to: Inject a partition value into a viewInject an opened realm into a viewInject a realm configuration into a view

Pass Realm Objects to a View

When you use the @ObservedRealmObject or @ObservedResults property
wrapper, you implicitly open a realm and retrieve the specified objects
or results. You can then pass those objects to a view further down the
hierarchy.

swift
struct DogsView: View {
    @ObservedResults(Dog.self) var dogs

    /// The button to be displayed on the top left.
    var leadingBarButton: AnyView?

    var body: some View {
        NavigationView {
            VStack {
                // The list shows the dogs in the realm.
                // The ``@ObservedResults`` above implicitly opens a realm and retrieves
                // all the Dog objects. We can then pass those objects to views further down the
                // hierarchy.
                List {
                    ForEach(dogs) { dog in
                        DogRow(dog: dog)
                    }.onDelete(perform: $dogs.remove)
                }.listStyle(GroupedListStyle())
                    .navigationBarTitle("Dogs", displayMode: .large)
                    .navigationBarBackButtonHidden(true)
                    .navigationBarItems(
                        leading: self.leadingBarButton,
                        // Edit button on the right to enable rearranging items
                        trailing: EditButton())
            }.padding()
        }
    }
}

Pass Environment Values

Environment injection is a
useful tool in SwiftUI development with Realm.
Realm property wrappers provide different ways for you to
work with environment values when developing your SwiftUI application.

Inject an Opened Realm

You can inject a realm that you opened in another SwiftUI view into
a view as an environment value. The property wrapper uses this passed-in
realm to populate the view:

swift
ListView()
   .environment(\.realm, realm)

Inject a Realm Configuration

You can use a realm other than the default realm by passing a different
configuration in an environment object.

swift
LocalOnlyContentView()
.environment(\.realmConfiguration, Realm.Configuration( /* ... */ ))

Guides Swiftui React To Changes (`docs/guides/swiftui/react-to-changes.md`)

React to Changes - SwiftUI

Observe an Object

The Swift SDK provides the @ObservedRealmObject property wrapper that invalidates a view
when an observed object changes. You can use this property wrapper to
create a view that automatically updates itself when the observed object
changes.

swift
struct DogDetailView: View {
    @ObservedRealmObject var dog: Dog

    var body: some View {
        VStack {
            Text(dog.name)
                .font(.title2)
            Text("\(dog.name) is a \(dog.breed)")
            AsyncImage(url: dog.profileImageUrl) { image in
                            image.resizable()
                        } placeholder: {
                            ProgressView()
                        }
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 150, height: 150)
            Text("Favorite toy: \(dog.favoriteToy)")
        }
    }
}

Observe Query Results

The Swift SDK provides the @ObservedResults
property wrapper that lets you observe a collection of query results. You
can perform a quick write to an ObservedResults collection, and the view
automatically updates itself when the observed query changes. For example,
you can remove a dog from an observed list of dogs using onDelete.

Note:
The @ObservedResults property wrapper is intended for use in a
SwiftUI View. If you want to observe results in a view model, register
a change listener.

swift
struct DogsView: View {
    @ObservedResults(Dog.self) var dogs

    /// The button to be displayed on the top left.
    var leadingBarButton: AnyView?

    var body: some View {
        NavigationView {
            VStack {
                // The list shows the dogs in the realm.
                // The ``@ObservedResults`` above implicitly opens a realm and retrieves
                // all the Dog objects. We can then pass those objects to views further down the
                // hierarchy.
                List {
                    ForEach(dogs) { dog in
                        DogRow(dog: dog)
                    }.onDelete(perform: $dogs.remove)
                }.listStyle(GroupedListStyle())
                    .navigationBarTitle("Dogs", displayMode: .large)
                    .navigationBarBackButtonHidden(true)
                    .navigationBarItems(
                        leading: self.leadingBarButton,
                        // Edit button on the right to enable rearranging items
                        trailing: EditButton())
            }.padding()
        }
    }
}

Seealso:
For more information about the query syntax and types of queries that Realm
supports, see: Read and Filter
Data.

Sort Observed Results

The @ObservedResults
property wrapper can take a SortDescriptor parameter to sort the query results.

swift
struct SortedDogsView: View {
    @ObservedResults(Dog.self,
                     sortDescriptor: SortDescriptor(keyPath: "name",
                        ascending: true)) var dogs

    var body: some View {
        NavigationView {
            // The list shows the dogs in the realm, sorted by name
            List(dogs) { dog in
                DogRow(dog: dog)
            }
        }
    }
}

Tip:
You cannot use a computed property as a SortDescriptor for @ObservedResults.

Observe Sectioned Results

Version added: 10.29.0

You can observe a results set that is divided into sections by a key
generated from a property on the object. We've added a computed variable
to the model that we don't persist; we just use this to section the results
set.

swift
var firstLetter: String {
    guard let char = name.first else {
        return ""
    }
    return String(char)
}

Then, we can use the @ObservedSectionedResults property wrapper to
observe the results set divided into sections based on the computed variable
key.

swift
@ObservedSectionedResults(Dog.self,
                          sectionKeyPath: \.firstLetter) var dogs

You might use these observed sectioned results to populate a List view
divided by sections:

swift
struct SectionedDogsView: View {
    @ObservedSectionedResults(Dog.self,
                              sectionKeyPath: \.firstLetter) var dogs

    /// The button to be displayed on the top left.
    var leadingBarButton: AnyView?

    var body: some View {
        NavigationView {
            VStack {
                // The list shows the dogs in the realm, split into sections according to the keypath.
                List {
                    ForEach(dogs) { section in
                        Section(header: Text(section.key)) {
                            ForEach(section) { dog in
                                DogRow(dog: dog)
                            }
                        }
                    }
                }
                .listStyle(GroupedListStyle())
                    .navigationBarTitle("Dogs", displayMode: .large)
                    .navigationBarBackButtonHidden(true)
                    .navigationBarItems(
                        leading: self.leadingBarButton,
                        // Edit button on the right to enable rearranging items
                        trailing: EditButton())
            }.padding()
        }
    }
}

Guides Swiftui Swiftui Previews (`docs/guides/swiftui/swiftui-previews.md`)

Use Realm with SwiftUI Previews

Overview

SwiftUI Previews are a useful tool during development. You can work with Realm
data in SwiftUI Previews in a few ways:

  • Initialize individual objects to use in detail views
  • Conditionally use an array of objects in place of @ObservedResults
  • Create a realm that contains data for the previews

SwiftUI Preview debugging can be opaque, so we also have a few tips to debug
issue with persisting Realms within SwiftUI Previews.

Initialize an Object for a Detail View

In the simplest case, you can use SwiftUI Previews with one or more objects
that use Realm properties you can set directly at initialization.
You might want to do this when previewing a Detail view. Consider DoggoDB's
DogDetailView:

swift
struct DogDetailView: View {
    @ObservedRealmObject var dog: Dog

    var body: some View {
        VStack {
            Text(dog.name)
                .font(.title2)
            Text("\(dog.name) is a \(dog.breed)")
            AsyncImage(url: dog.profileImageUrl) { image in
                            image.resizable()
                        } placeholder: {
                            ProgressView()
                        }
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 150, height: 150)
            Text("Favorite toy: \(dog.favoriteToy)")
        }
    }
}

Create an extension for your model object. Where you put this extension depends
on convention in your codebase. You may put it directly in the model file,
have a dedicated directory for sample data, or use some other convention in
your codebase.

In this extension, initialize one or more Realm objects with static let:

swift
extension Dog {
    static let dog1 = Dog(value: ["name": "Lita", "breed": "Lab mix", "weight": 27, "favoriteToy": "Squeaky duck", "profileImageUrl": "https://www.corporaterunaways.com/images/2021/04/lita-768x768.jpeg"])
    static let dog2 = Dog(value: ["name": "Maui", "breed": "English Springer Spaniel", "weight": 42, "favoriteToy": "Wubba", "profileImageUrl": "https://www.corporaterunaways.com/images/2021/04/maui_with_leaf-768x576.jpeg"])
    static let dog3 = Dog(value: ["name": "Ben", "breed": "Border Collie mix", "weight": 48, "favoriteToy": "Frisbee", "profileImageUrl": "https://www.corporaterunaways.com/images/2012/03/ben-630x420.jpg"])

}

In this example, we initialize objects with a value. You can only initialize objects with
a value when your model contains properties that you can directly initialize.
If your model object contains properties that are only mutable within a
write transaction, such as a List property,
you must instead create a realm to use with your SwiftUI Previews.

After you have initialized an object as an extension of your model class,
you can use it in your SwiftUI Preview. You can pass the object directly
to the View in the Preview:

swift
struct DogDetailView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            DogDetailView(dog: Dog.dog1)
        }
    }
}

Conditionally Use ObservedResults in a List View

When you use @ObservedResults
in a List view, this implicitly opens a realm and queries it. For this to
work in a Preview, you need a realm populated with data. As an alternative, you can conditionally
use a static array in Previews and only use the @ObservedResults variable
when running the app.

You could do this in multiple ways, but for the sake of making our
code easier to read and understand, we'll create an EnvironmentValue
that can detect whether the app is running in a Preview:

swift
import Foundation
import SwiftUI

public extension EnvironmentValues {
   var isPreview: Bool {
      #if DEBUG
      return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
      #else
      return false
      #endif
   }
}

Then, we can use this as an environment value in our view, and conditionally
change which variable we use based on whether or not we are in a Preview.

This example builds on the Dog extension we defined above. We'll create an dogArray as
a static let in our Dog extension, and include the item objects we
already created:

swift
static let dogArray = [dog1, dog2, dog3]

Then, when we iterate through our List, use the static dogArray if
running in a Preview, or use the @ObservedResults query if not in a Preview.

swift
struct DogsView: View {
   @Environment(\.isPreview) var isPreview
   @ObservedResults(Dog.self) var dogs
   var previewDogs = Dog.dogArray

   var body: some View {
      NavigationView {
         VStack {
            List {
               if isPreview {
                  ForEach(previewDogs) { dog in
                     DogRow(dog: dog)
                  }
               } else {
                  ForEach(dogs) { dog in
                     DogRow(dog: dog)
                  }.onDelete(perform: $dogs.remove)
               }
            }
            ... More View code

This has the benefit of being lightweight and not persisting any data, but
the downside of making the View code more verbose. If you prefer cleaner
View code, you can create a realm with data that you use in the Previews.

Create a Realm with Data for Previews

In some cases, your only option to see realm data in a SwiftUI Preview
is to create a realm that contains the data. You might do this when populating
a property that can only be populated during a write transaction, rather
than initialized directly with a value, such as a List or MutableSet.
You might also want to do this if your view relies on more complex object
hierarchies being passed in from other views.

However, using a realm directly does inject state into your SwiftUI Previews,
which can come with drawbacks. Whether you're using Realm or Core Data,
stateful SwiftUI Previews can cause issues like:

  • Seeing unexpected or duplicated data due to re-running the realm file
    creation steps repeatedly
  • Needing to perform a migration within the SwiftUI Preview when you make model changes
  • Potential issues related to changing state within views
  • Unexplained crashes or performance issues related to issues that are not
    surfaced in a visible way in SwiftUI Previews

You can avoid or fix some of these issues with these tips:

  • Use an in-memory realm, when possible (demonstrated in the example above)
  • Manually delete all preview data from the command line to reset state
  • Check out diagnostic logs to try to troubleshoot SwiftUI Preview issues

You can create a static variable for your realm in your model extension.
This is where you do the work to populate your realm. In our case, we
create a Person and append some Dog objects to the dogs
List property. This example builds on the example above where we initialized
a few Dog objects in an Dog extension.

We'll create a Person extension, and create a single Person object
in that extension. Then, we'll create a previewRealm by adding the
Person we just created, and appending the example Dog objects from
the Dog extension.

To avoid adding these objects more than once, we add a check to see if the
Person already exists by querying for Person objects and checking that
the count is 1. If the realm contains a Person, we can use it in our
SwiftUI Preview. If not, we add the data.

swift
static var previewRealm: Realm {
    var realm: Realm
    let identifier = "previewRealm"
    let config = Realm.Configuration(inMemoryIdentifier: identifier)
    do {
        realm = try Realm(configuration: config)
        // Check to see whether the in-memory realm already contains a Person.
        // If it does, we'll just return the existing realm.
        // If it doesn't, we'll add a Person append the Dogs.
        let realmObjects = realm.objects(Person.self)
        if realmObjects.count == 1 {
            return realm
        } else {
            try realm.write {
                realm.add(person)
                person.dogs.append(objectsIn: [Dog.dog1, Dog.dog2, Dog.dog3])
            }
            return realm
        }
    } catch let error {
        fatalError("Can't bootstrap item data: \(error.localizedDescription)")
    }
}

To use it in the SwiftUI Preview, our ProfileView code expects a Profile.
This is a projection of the Person object. In our
Preview, we can get the realm, query it for the Profile, and pass it to the
view:

swift
struct ProfileView_Previews: PreviewProvider {
    static var previews: some View {
        let realm = Person.previewRealm
        let profile = realm.objects(Profile.self)
        ProfileView(profile: profile.first!)
    }
}

If you don't have a View that is expecting a realm object to be passed in,
but instead uses @ObservedResults to query a realm or otherwise work
with an existing realm, you can inject the realm into the view as an
environment value:

swift
struct SomeListView_Previews: PreviewProvider {
   static var previews: some View {
      SomeListView()
         .environment(\.realm, Person.previewRealm)
   }
}

Use an In-Memory Realm

When possible, use an in-memory realm
to get around some of the state-related issues that can come from using
a database within a SwiftUI Preview.

Use the inMemoryIdentifier
configuration property when you initialize the realm.

swift
static var previewRealm: Realm {
   var realm: Realm
   let identifier = "previewRealm"
   let config = Realm.Configuration(inMemoryIdentifier: identifier)
   do {
      realm = try Realm(configuration: config)
      // ... Add data to realm

Note:
Do not use the the deleteRealmIfMigrationNeeded
configuration property when you initialize a realm for SwiftUI Previews.
Due to the way Apple has implemented SwiftUI Previews, using this property
to bypass migration issues causes SwiftUI Previews to crash.

Delete SwiftUI Previews

If you run into other SwiftUI Preview issues related to state,
such as a failure to load a realm in a Preview due to migration being
required, there are a few things you can do to remove cached Preview data.

The Apple-recommended fix is to close Xcode and use the command line to
delete all your existing SwiftUI Preview data.

  1. Close Xcode.
  2. From your command line, run: xcrun simctl --set previews delete all

It's possible that data may persist after running this command. This is
likely due to Xcode retaining a reference due to something in the Preview
and being unable to delete it. You can also try these steps to resolve issues:

  • Build for a different simulator
  • Restart the computer and re-run xcrun simctl --set previews delete all
  • Delete stored Preview data directly. This data is stored in
    ~/Library/Developer/Xcode/UserData/Previews.

Get Detailed Information about SwiftUI Preview Crashes

If you have an unexplained SwiftUI Preview crash when using realm, first try
running the application on the simulator. The error messaging and logs available
for the simulator make it easier to find and diagnose issues. If you can
debug the issue in the simulator, this is the easiest route.

If you cannot replicate a SwiftUI Preview crash in the simulator, you can
view crash logs for the SwiftUI Preview app. These logs are available in
~/Library/Logs/DiagnosticReports/. These logs sometimes appear after
a delay, so wait a few minutes after a crash if you don't see the relevant
log immediately.


Guides Swiftui Swiftui Tutorial (`docs/guides/swiftui/swiftui-tutorial.md`)

Realm with SwiftUI QuickStart

Prerequisites

  • Have Xcode 12.4 or later (minimum Swift version 5.3.1).
  • Create a new Xcode project using the SwiftUI "App" template with a minimum iOS target of 15.0.
  • Install the Swift SDK. This SwiftUI app requires a minimum
    SDK version of 10.19.0.

Overview

Seealso:
This page provides a small working app to get you up and running with
Realm and SwiftUI quickly. If you'd like to see additional examples,
including more explanation about Realm's SwiftUI features, see:
SwiftUI.

This page contains all of the code for a working Realm and SwiftUI app.
The app starts on the ItemsView, where you can edit a list of items:

  • Press the Add button on the bottom right of the screen to add
    randomly-generated items.
  • Press the Edit button on the top right to modify the list order,
    which the app persists in the realm.
  • You can also swipe to delete items.

When you have items in the list, you can press one of the items to
navigate to the ItemDetailsView. This is where you can modify the
item name or mark it as a favorite:

  • Press the text field in the center of the screen and type a new name.
    When you press Return, the item name should update across the app.
  • You can also toggle its favorite status by pressing the heart toggle in the
    top right.

Get Started

We assume you have created an Xcode project with the SwiftUI "App"
template. Open the main Swift file and delete all of the code inside,
including any @main App classes that Xcode generated for you. At
the top of the file, import the Realm and SwiftUI frameworks:

swift
import RealmSwift
import SwiftUI

Tip:
Just want to dive right in with the complete code? Jump to
Complete Code below.

Define Models

A common Realm data modeling use case is to have "things" and
"containers of things". This app defines two related Realm object models: item
and itemGroup.

An item has two user-facing properties:

  • A randomly generated-name, which the user can edit.
  • An isFavorite boolean property, which shows whether the user "favorited"
    the item.

An itemGroup contains items. You can extend the itemGroup to have a name and an
association with a specific user, but that's out of scope of this guide.

Paste the following code into your main Swift file to define the models:

text
/* Detailed source-code truncated for AI context efficiency. */

Views and Observed Objects

The entrypoint of the app is the ContentView class that derives from
SwiftUI.App. For now, this always displays the
LocalOnlyContentView.

swift
@main
struct ContentView: SwiftUI.App {
    var body: some Scene {
        WindowGroup {
            LocalOnlyContentView()
        }
    }
}

Tip:
You can use a realm other than the default realm by passing
an environment object from higher in the View hierarchy:

swift
LocalOnlyContentView()
  .environment(\.realmConfiguration, Realm.Configuration( /* ... */ ))

The LocalOnlyContentView has an @ObservedResults itemGroups. This implicitly uses the default
realm to load all itemGroups when the view appears.

This app only expects there to ever be one itemGroup. If there is an itemGroup
in the realm, the LocalOnlyContentView renders an ItemsView for
that itemGroup.

If there is no itemGroup already in the realm, then the
LocalOnlyContentView displays a ProgressView while it adds one. Because
the view observes the itemGroups thanks to the @ObservedResults property
wrapper, the view immediately refreshes upon adding that first itemGroup and
displays the ItemsView.

swift
/// The main content view
struct LocalOnlyContentView: View {
    @State var searchFilter: String = ""
    // Implicitly use the default realm's objects(ItemGroup.self)
    @ObservedResults(ItemGroup.self) var itemGroups

    var body: some View {
        if let itemGroup = itemGroups.first {
            // Pass the ItemGroup objects to a view further
            // down the hierarchy
            ItemsView(itemGroup: itemGroup)
        } else {
            // For this small app, we only want one itemGroup in the realm.
            // You can expand this app to support multiple itemGroups.
            // For now, if there is no itemGroup, add one here.
            ProgressView().onAppear {
                $itemGroups.append(ItemGroup())
            }
        }
    }
}

Tip:
Starting in SDK version 10.12.0, you can use an optional key path parameter
with @ObservedResults to filter change notifications to only those
occurring on the provided key path or key paths. For example:

text
@ObservedResults(MyObject.self, keyPaths: ["myList.property"])

The ItemsView receives the itemGroup from the parent view and stores it in
an @ObservedRealmObject
property. This allows the ItemsView to "know" when the object has
changed regardless of where that change happened.

The ItemsView iterates over the itemGroup's items and passes each item to an
ItemRow for rendering as a list.

To define what happens when a user deletes or moves a row, we pass the
remove and move methods of the Realm
List as the handlers of the respective
remove and move events of the SwiftUI List. Thanks to the
@ObservedRealmObject property wrapper, we can use these methods
without explicitly opening a write transaction. The property wrapper
automatically opens a write transaction as needed.

text
/* Detailed source-code truncated for AI context efficiency. */

Finally, the ItemRow and ItemDetailsView classes use the
@ObservedRealmObject property wrapper with the item passed in from
above. These classes demonstrate a few more examples of how to use the
property wrapper to display and update properties.

swift
/// Represents an Item in a list.
struct ItemRow: View {
    @ObservedRealmObject var item: Item

    var body: some View {
        // You can click an item in the list to navigate to an edit details screen.
        NavigationLink(destination: ItemDetailsView(item: item)) {
            Text(item.name)
            if item.isFavorite {
                // If the user "favorited" the item, display a heart icon
                Image(systemName: "heart.fill")
            }
        }
    }
}

/// Represents a screen where you can edit the item's name.
struct ItemDetailsView: View {
    @ObservedRealmObject var item: Item

    var body: some View {
        VStack(alignment: .leading) {
            Text("Enter a new name:")
            // Accept a new name
            TextField("New name", text: $item.name)
                .navigationBarTitle(item.name)
                .navigationBarItems(trailing: Toggle(isOn: $item.isFavorite) {
                    Image(systemName: item.isFavorite ? "heart.fill" : "heart")
                })
        }.padding()
    }
}

Tip:
@ObservedRealmObject is a frozen object. If you want to modify
the properties of an @ObservedRealmObject
directly in a write transaction, you must .thaw() it first.

At this point, you have everything you need to work with
Realm and SwiftUI. Test it out and see if everything is
working as expected.

Complete Code

If you would like to copy and paste or examine the complete code, see below.

text
/* Detailed source-code truncated for AI context efficiency. */

Guides Swiftui Write (`docs/guides/swiftui/write.md`)

Write Data - SwiftUI

Perform a Quick Write

In addition to performing writes inside a transaction block, the Realm Swift
SDK offers a convenience feature to enable quick writes without explicitly
performing a write transaction.

When you use the @ObservedRealmObject or @ObservedResults property
wrappers, you can implicitly open a write transaction. Use the $ operator
to create a two-way binding to the state object. Then, when you make changes
to the bound object or collection, you initiate an implicit write.

The Realm SwiftUI property wrappers work with frozen data to provide thread safety. When you use $ to
create a two-way binding, the Realm Swift SDK manages thawing the
frozen objects so you can write to them.

Update an Object's Properties

In this example, we create a two-way binding with one of the state object's
properties. $dog.favoriteToy creates a binding to the model Dog
object's favoriteToy property

When the app user updates that field in this example, Realm
opens an implicit write transaction and saves the new value to the database.

swift
struct EditDogDetails: View {
    @ObservedRealmObject var dog: Dog
    
    var body: some View {
        VStack {
            Text(dog.name)
                .font(.title2)
            TextField("Favorite toy", text: $dog.favoriteToy)
        }
    }
}

Add or Remove Objects in an ObservedResults Collection

While a regular Realm Results collection
is immutable, ObservedResults
is a mutable collection that allows you to perform writes using a two-way
binding. When you update the bound collection, Realm opens an implicit write
transaction and saves the changes to the collection.

In this example, we remove an element from the results set using
$dogs.remove in the onDelete. Using the $dogs here creates a
two-way binding to a BoundCollection that lets us mutate the
@ObservedResults dogs collection.

We add an item to the results using $dogs.append in the
addDogButton.

These actions write directly to the @ObservedResults collection.

text
/* Detailed source-code truncated for AI context efficiency. */

Note:
The @ObservedResults property wrapper is intended for use in a
SwiftUI View. If you want to observe results in a view model, register
a change listener.

Append an Object to a List

When you have a two-way binding with an @ObservedRealmObject that has
a list property, you can add new objects to the list.

In this example, the Person object has a list property that forms a
to-many relationship with one or more dogs.

swift
class Person: Object, ObjectKeyIdentifiable {
   @Persisted(primaryKey: true) var _id: ObjectId
   @Persisted var firstName = ""
   @Persisted var lastName = ""
   ...
   @Persisted var dogs: List<Dog>
}

When the user presses the Save button, this:

  • Creates a Dog object with the details that the user has entered
  • Appends the Dog object to the Person object's dogs list
swift
struct AddDogToPersonView: View {
    @ObservedRealmObject var person: Person
    @Binding var isInAddDogView: Bool
    @State var name = ""
    @State var breed = ""
    @State var weight = 0
    @State var favoriteToy = ""
    @State var profileImageUrl: URL?

    var body: some View {
        Form {
            TextField("Dog's name", text: $name)
            TextField("Dog's breed", text: $breed)
            TextField("Dog's weight", value: $weight, format: .number)
            TextField("Dog's favorite toy", text: $favoriteToy)
            TextField("Image link", value: $profileImageUrl, format: .url)
                            .keyboardType(.URL)
                            .textInputAutocapitalization(.never)
                            .disableAutocorrection(true)
            Section {
                Button(action: {
                    let dog = createDog(name: name, breed: breed, weight: weight, favoriteToy: favoriteToy, profileImageUrl: profileImageUrl)
                    $person.dogs.append(dog)
                    isInAddDogView.toggle()
                }) {
                    Text("Save")
                }
                Button(action: {
                    isInAddDogView.toggle()
                }) {
                    Text("Cancel")
                }
            }
        }
    }
}

Use Create to Copy an Object Into the Realm

There may be times when you create a new object, and set one of its properties
to an object that already exists in the realm. Then, when you go to add the
new object to the realm, you see an error similar to:

shell
Object is already managed by another Realm. Use create instead to copy it into this Realm.

When this occurs, you can use the .create
method to initialize the object, and use modified: .update to set its
property to the existing object.

Example:
Consider a version of the DoggoDB Dog model where the favoriteToy
property isn't just a String, but is an optional DogToy object:

swift
class Dog: Object, ObjectKeyIdentifiable {
   @Persisted(primaryKey: true) var _id: UUID
   @Persisted var name = ""
   ...
   @Persisted var favoriteToy: DogToy?
   ...
}

When your app goes to create a new Dog object, perhaps it checks to see
if the DogToy already exists in the realm, and then set the favoriteToy
property to the existing dog toy.

When you go to append the new Dog to the Person object, you may
see an error similar to:

shell
Object is already managed by another Realm. Use create instead to copy it into this Realm.

The Dog object remains unmanaged until you append it to the Person
object's dogs property. When the Realm Swift SDK checks the Dog
object to find the realm that is currently managing it, it finds nothing.

When you use the $ notation to perform a quick write that appends the
Dog object to the Person object, this write uses the realm it has
access to in the view. This is a realm instance implicitly opened by
the @ObservedRealmObject or @ObservedResults property wrapper.
The existing DogToy object, however, may be managed by a different
realm instance.

To solve this error, use the .create
method when you initialize the Dog object, and use
modified: .update to set its favoriteToy value to the existing
object:

swift
// When working with an `@ObservedRealmObject` `Person`, this is a frozen object.
// Thaw the object and get its realm to perform the write to append the new dog.
let thawedPersonRealm = frozenPerson.thaw()!.realm!
try! thawedPersonRealm.write {
    // Use the .create method with `update: .modified` to copy the
    // existing object into the realm
    let dog = thawedPersonRealm.create(Dog.self, value:
                                        ["name": "Maui",
                                         "favoriteToy": wubba],
                                       update: .modified)
    person.dogs.append(dog)
}

Perform an Explicit Write

In some cases, you may want or need to explicitly perform a write transaction
instead of using the implicit $ to perform a quick write. You may want
to do this when:

  • You need to look up additional objects to perform a write
  • You need to perform a write to objects you don't have access to in the view

If you pass an object you are observing with @ObservedRealmObject or
@ObservedResults into a function where you perform an explicit write
transaction that modifies the object, you must thaw it first.

swift
let thawedCompany = company.thaw()!

You can access the realm that is managing the object or objects by calling
.realm on the object or collection:

swift
let realm = company.realm!.thaw()

Because the SwiftUI property wrappers use frozen objects, you must thaw
the realm before you can write to it.

Example:
Consider a version of the DoggoDB app where a Company object
has a list of Employee objects. Each Employee has a list of
Dog objects. But for business reasons, you also wanted to have a
list of Dog objects available directly on the Company object,
without being associated with an Employee. The model might look
something like:

swift
class Company: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var companyName = ""
    @Persisted var employees: List<Employee>
    @Persisted var dogs: List<Dog>
}

Consider a view where you have access to the Company object, but
want to perform an explicit write to add an existing dog to an existing
employee. Your function might look something like:

swift
// The `frozenCompany` here represents an `@ObservedRealmObject var company: Company`
performAnExplicitWrite(company: frozenCompany, employeeName: "Dachary", dogName: "Maui")

func performAnExplicitWrite(company: Company, employeeName: String, dogName: String) {
    // Get the realm that is managing the `Company` object you passed in.
    // Thaw the realm so you can write to it.
    let realm = company.realm!.thaw()
    // Thawing the `Company` object that you passed in also thaws the objects in its List properties.
    // This lets you append the `Dog` to the `Employee` without individually thawing both of them.
    let thawedCompany = company.thaw()!
    let thisEmployee = thawedCompany.employees.where { $0.name == employeeName }.first!
    let thisDog = thawedCompany.dogs.where { $0.name == dogName }.first!
    try! realm.write {
        thisEmployee.dogs.append(thisDog)
    }
}

Guides Swiftui Model Data Change An Object Model (`docs/guides/swiftui/model-data/change-an-object-model.md`)

Change an Object Model - SwiftUI

Overview

When you update your object schema, you must increment the schema version
and perform a migration. You might update your object schema between major
version releases of your app.

For information on how to actually perform the migration, see:
Change an Object Model.

This page focuses on how to use migrated data in SwiftUI Views.

Use Migrated Data with SwiftUI

To perform a migration:

  • Update your schema and write a migration block, if required
  • Specify a Realm.Configuration
    that uses this migration logic and/or updated schema version when you
    initialize your realm.

From here, you have a few options to pass the configuration object. You can:

  • Set the configuration as the default configuration. If you do not explicitly pass the
    configuration via environment injection or as a parameter, property
    wrappers use the default configuration.
  • Use environment injection to provide this configuration to the first view
    in your hierarchy that uses Realm
  • Explicitly provide the configuration to a Realm property wrapper that takes
    a configuration object, such as @ObservedResults or @AsyncOpen.

Example:
For example, you might want to add a property to an existing object. We
could add a favoriteTreat property to the Dog object in DoggoDB:

swift
@Persisted var favoriteTreat = ""

After you add your new property to the schema, you must increment the
schema version. Your Realm.Configuration might look like this:

swift
let config = Realm.Configuration(schemaVersion: 2)

Declare this configuration somewhere that is accessible to the first view
in the hierarchy that needs it. Declaring this above your @main app
entrypoint makes it available everywhere, but you could also put it in
the file where you first open a realm.

Set a Default Configuration

You can set a default configuration in a SwiftUI app the same as any other
Realm Swift app. Set the default realm configuration by assigning a new
Realm.Configuration instance to the Realm.Configuration.defaultConfiguration
class property.

swift
// Open the default realm
let defaultRealm = try! Realm()

// Open the realm with a specific file URL, for example a username
let username = "GordonCole"
var config = Realm.Configuration.defaultConfiguration
config.fileURL!.deleteLastPathComponent()
config.fileURL!.appendPathComponent(username)
config.fileURL!.appendPathExtension("realm")
let realm = try! Realm(configuration: config)

Pass the Configuration Object as an Environment Object

Once you have declared the configuration, you can inject it as an environment
object to the first view in your hierarchy that opens a realm. If you are
using the @ObservedResults or @ObservedRealmObject property wrappers,
these views implicitly open a realm, so they also need access to this
configuration.

swift
.environment(\.realmConfiguration, config)

You can pass the realm configuration environment object directly
to the LocalOnlyContentView:

swift
.environment(\.realmConfiguration, config)

Which opens a realm implicitly with:

swift
struct LocalOnlyContentView: View {
    // Implicitly use the default realm's objects(Dog.self)
    @ObservedResults(Dog.self) var dogs

    var body: some View {
        if dogs.first != nil {
            // If dogs exist, go to the DogsView
            DogsView()
        } else {
            // If there is no Dog object, add one here.
            AddDogView()
        }
    }
}

Explicitly Pass the Updated Configuration to a Realm SwiftUI Property Wrapper

You can explicitly pass the configuration object to a Realm SwiftUI
property wrapper that takes a configuration object, such as @ObservedResults
or @AutoOpen. In this case, you might pass it directly to @ObservedResults
in our DogsView.

swift
// Use a `config` that you've passed in from above.
@ObservedResults(Dog.self, configuration: config) var dogs

Guides Swiftui Model Data Define A Realm Object Model (`docs/guides/swiftui/model-data/define-a-realm-object-model.md`)

Realm Object Models - SwiftUI

Concepts: Object Models and Relationships

Modeling data for SwiftUI builds on the same object model and relationship
concepts in the Swift SDK. If you are unfamiliar with Realm Swift SDK
data modeling concepts, see: Define a Realm Object Model - Swift SDK.

Binding the Object Model to the UI

The Model-View-ViewModel (MVVM) design pattern advocates creating a view
model that abstracts the model from the View code. While you can certainly
do that with Realm, the Swift SDK provides tools that make it easy to
work directly with your data in SwiftUI Views. These tools include things
like:

  • Property wrappers that create bindings to underlying observable objects
  • A class to project and transform underlying model objects for use in
    specific views

Transforming Data for SwiftUI Views

The Realm Swift SDK provides a special type of object, called a Projection, to transform
and work with subsets of your data. Consider a projection similar to
a view model. It lets you pass through or transform the original
object's properties in different ways:

  • Passthrough: The projection's property has the same name and type as
    the original object.
  • Rename: The projection's property has the same type as the original object,
    but a different name.
  • Keypath resolution: Use this to access specific properties of the
    projected Object.
  • Collection mapping: You can map some collection types to a collection of primitive values.
  • Exclusion: All properties of the original Realm object not defined in
    the projection model. Any changes to those properties do not trigger a
    change notification when observing the projection.

When you use a Projection, you get all the benefits of Realm's
live objects:

  • The class-projected object live updates
  • You can observe it for changes
  • You can apply changes directly to the properties in write transactions

Define a New Object

You can define a Realm object by deriving from the
Object or
EmbeddedObject
class. The name of the class becomes the table name in the realm,
and properties of the class persist in the database. This makes it
as easy to work with persisted objects as it is to work with
regular Swift objects.

The Realm SwiftUI documentation uses a model for a fictional app,
DoggoDB. This app is a company directory of employees who have dogs. It
lets people share a few details about their dogs with other employees.

The data model includes a Person object, with a to-many
relationship to that person's Dog objects.
It also uses a special Realm Swift SDK data type, PersistableEnum, to store information
about the person's business unit.

swift
class Person: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var firstName = ""
    @Persisted var lastName = ""
    @Persisted var personId = ""
    @Persisted var company = "my business"
    @Persisted var businessUnit = BusinessUnitEnum.engineering
    @Persisted var profileImageUrl: URL?
    @Persisted var dogs: List<Dog>
}

enum BusinessUnitEnum: String, PersistableEnum, CaseIterable {
    case customerEngineering = "Customer Engineering"
    case educationCommunityAndDocs = "Education, Community and Docs"
    case engineering = "Engineering"
    case financeAndOperations = "Finance and Operations"
    case humanResourcesAndRescruiting = "Human Resources and Recruiting"
    case management = "Management"
    case marketing = "Marketing"
    case product = "Product"
    case sales = "Sales"
}

class Dog: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: UUID
    @Persisted var name = ""
    @Persisted var breed = ""
    @Persisted var weight = 0
    @Persisted var favoriteToy = ""
    @Persisted var profileImageUrl: URL?
    @Persisted var dateLastUpdated = Date()
    @Persisted(originProperty: "dogs") var person: LinkingObjects<Person>
    var firstLetter: String {
        guard let char = name.first else {
            return ""
        }
        return String(char)
    }
}

Seealso:
For complete details about defining a Realm object model, see:

  • Object Models
  • Relationships
  • Supported Data Types

Define a Projection

Our fictional DoggoDB app has a user Profile view. This view displays
some details about the person, but we don't need all of the properties
of the Person model. We can create a Projection with only the details we want. We can also modify
the lastName property to use just the first initial of the last name.

swift
class Profile: Projection<Person> {
    @Projected(\Person.firstName) var firstName // Passthrough from original object
    @Projected(\Person.lastName.localizedCapitalized.first) var lastNameInitial // Access and transform the original property
    @Projected(\Person.personId) var personId
    @Projected(\Person.businessUnit) var businessUnit
    @Projected(\Person.profileImageUrl) var profileImageUrl
    @Projected(\Person.dogs) var dogs
}

We can use this projection in the Profile view instead of the original
Person object.

Class projection works with SwiftUI property wrappers:

  • ObservedRealmObject
  • ObservedResults

Seealso:
For a complete example of using a class projection in a SwiftUI
application, see the Projections example app.


--- METRICS ---