Enhancements
-
Add support for actor-isolated Realms, opened with
try await Realm(actor: actor)
.Rather than being confined to the current thread or a dispatch queue, actor-isolated Realms are isolated to an actor. This means that they can be used from any thread as long as it's within a function isolated to that actor, and they remain valid over suspension points where a task may hop between threads. Actor-isolated Realms can be used with either global or local actors:
@MainActor function mainThreadFunction() async throws { // These are identical: the async init continues to produce a // MainActor-confined Realm if no actor is supplied let realm1 = try await Realm() let realm2 = try await Realm(MainActor.shared) } // A simple example of a custom global actor @globalActor actor BackgroundActor: GlobalActor { static var shared = BackgroundActor() } @BackgroundActor backgroundThreadFunction() async throws { // Explicitly specifying the actor is required for everything but MainActor let realm = try await Realm(actor: BackgroundActor.shared) try await realm.write { _ = realm.create(MyObject.self) } // Thread-confined Realms would sometimes throw an exception here, as we // may end up on a different thread after an `await` print("\(realm.objects(MyObject.self).count)") } actor MyActor { // An implicitly-unwrapped optional is used here to let us pass `self` to // `Realm(actor:)` within `init` var realm: Realm! init() async throws { realm = try await Realm(actor: self) } var count: Int { realm.objects(MyObject.self).count } func create() async throws { try await realm.asyncWrite { realm.create(MyObject.self) } } } // This function isn't isolated to the actor, so each operation has to be async func createObjects() async throws { let actor = try await MyActor() for _ in 0..<5 { await actor.create() } print("\(await actor.count)") } // In an isolated function, an actor-isolated Realm can be used synchronously func createObjects(in actor: isolated MyActor) async throws { await actor.realm.write { actor.realm.create(MyObject.self) } print("\(actor.realm.objects(MyObject.self).count)") }
Actor-isolated Realms come with a more convenient syntax for asynchronous writes.
try await realm.write { ... }
will suspend the current task, acquire the write lock without blocking the current thread, and then invoke the block. The actual data is then written to disk on a background thread, and the task is resumed once that completes. As this does not block the calling thread while waiting to write and does not perform i/o on the calling thread, this will often be safe to use from@MainActor
functions without blocking the UI. Sufficiently large writes may still benefit from being done on a background thread.Asynchronous writes are only supported for actor-isolated Realms or in
@MainActor
functions.Actor-isolated Realms require Swift 5.8 (Xcode 14.3). Enabling both strict concurrency checking (
SWIFT_STRICT_CONCURRENCY=complete
in Xcode) and runtime actor data race detection (OTHER_SWIFT_FLAGS=-Xfrontend -enable-actor-data-race-checks
) is strongly recommended when using actor-isolated Realms. -
Add support for automatic partition-based to flexible sync migration. Connecting to a server-side app configured to use flexible sync with a client-side partition-based sync configuration is now supported, and will automatically create the appropriate flexible sync subscriptions to subscribe to the requested partition. This allows changing the configuration on the server from partition-based to flexible without breaking existing clients. (Core #6554)
-
Now you can use an array
[["_id": 1], ["breed": 0]]
as sorting option for a MongoCollection. This new API fixes the issue where the resulting documents when using more than one sort parameter were not consistent between calls. (#7188, since v10.0.0). -
Add support for adding a user created default logger, which allows implementing your own logging logic and the log threshold level. You can define your own logger creating an instance of
Logger
and define the log function which will be invoked whenever there is a log message.let logger = Logger(level: .all) { level, message in print("Realm Log - \(level): \(message)") }
Set this custom logger as Realm default logger using
Logger.shared
.Logger.shared = logger
-
It is now possible to change the default log threshold level at any point of the application's lifetime.
Logger.shared.logLevel = .debug
This will override the log level set anytime before by a user created logger.
-
We have set
.info
as the default log threshold level for Realm. You will now see some log message in your console. To disable useLogger.shared.level = .off
.
Fixed
- Several schema initialization functions had incorrect
@MainActor
annotations, resulting in runtime warnings if the first time a Realm was opened was on a background thread (#8222, since v10.34.0).
Deprecations
App.SyncManager.logLevel
andApp.SyncManager.logFunction
are deprecated in favour of setting a default logger.
Compatibility
- Realm Studio: 14.0.1 or later.
- APIs are backwards compatible with all previous releases in the 10.x.y series.
- Carthage release for Swift is built with Xcode 14.3.
- CocoaPods: 1.10 or later.
- Xcode: 13.4-14.3.
Internal
- Upgraded realm-core from v13.9.4 to v13.10.0.