The app I’m working on runs Realm writes serially using a global actor. To minimize contention, we try to keep the actual write transactions as focused as possible. One technique we’ve been using is to create unmanaged Realm objects off of the write actor and then capture references to those objects in a closure that gets executed on the actor:
let dog = Dog()
dog.name = "Rex"
dog.age = 10
writeRealm { realm in
realm.add(dog)
}
writeRealm
looks like this:
@RealmWriteActor func writeRealm<T>(_ block: @Sendable (Realm) throws -> T) throws -> T {
try autoreleasepool {
let realm = try Realm(configuration: ...)
defer {
realm.invalidate()
}
return try realm.write {
try block(realm)
}
}
}
We’re running into trouble with combining this approach with Strict Concurrency Checking since Dog
is not Sendable
. We’ve explored a few options, but so far haven’t found anything satisfactory:
-
Use
ThreadSafe
/ThreadSafeReference
- These only work with managed Realm objects. Since we need to capturedog
before it’s been added to a Realm, we can’t use this. - freeze/thaw - These also only work with objects that have already been added to a Realm.
-
Create an
@unchecked Sendable
wrapper that holds an unmanaged Realm object. - We were able to get this working similarly to the technique demonstrated in the docs for ThreadSafeReference with the help of API-level property wrappers. However, we weren’t able to come up with an acceptable solution for returning unmanaged Realm objects from thewriteRealm
closure other than making the wrapper itself the return type and manually unboxing, which added too much complexity at the call site for our taste.
What other techniques would you recommend trying, and what ideas do you have for how Realm could better support Strict Concurrency Checking in the future?