Replies: 7 comments 11 replies
-
We are currently trying to determine what is possible and what would actually be useful. Real-world examples of parts of an app where "What is possible" is complicated by that the concurrency implementation in Swift 5.6 is still quite incomplete and it is unclear which of the things in the evolution documents are actually going to happen and which are using "future directions" as a euphemism for "things we recognize could be useful but we aren't going to do". Some rambley thoughts on some of the things we might want to do: More
|
Beta Was this translation helpful? Give feedback.
-
The Base Problem Is CorrectI'm happy to see that you guys have nailed the fundamental concurrency problem for Realm: "Let an app do a huge background write/commit without beachballing the main thread." That's spot-on. Nobody wants to have 15 different threads all trying to write simultaneously; it's always going to be 1 UI thread and 1 background thread doing a big import/update. Unfortunately, the current advice in the docs is impossible: "If you're doing background writes or using device sync, don't do any writes on the main thread." I can avoid big writes on the main thread, but when a user toggles a checkbox or selects an item from a pop-up button, those tiny writes HAVE to be done on the main thread. Concurrency Toe-DipAssuming that we do not hit any
I know that 10.26.0 introduced Yielding Writes?When I have, say, 100,000 updates to do on a background Realm, I currently batch those into smaller write blocks. But it would be awesome if there were a "yielding" write available. Meaning I could tell Realm: "Listen, I want to run this background write block where I update 100,000 objects, but if the main thread suddenly needs to write something, pause this write transaction, yield to the main thread, and then pick back up when the main thread is done." That only makes sense when the background write is a unit of discrete updates where a clean "pause" can occur, such as looping through 10,000 objects one at a time. Or possibly as a manual check that the developer could include, similar to how we can check if cancellation has been requested for a |
Beta Was this translation helpful? Give feedback.
-
This is now finally the primary thing that I'm working on and we'll hopefully be able to ship some better async support soon. The first big piece to arrive is support for strict concurrency/sendable checking, which is actually pretty useful even if you aren't using async/await. It currently will unfortunately warn for safe uses of frozen objects, but otherwise does a pretty good job of catching invalid threading. The next bit will be actor-confined Realms with much nicer support for async writes. This lets you do things like the following:
There's an incomplete and very lightly tested implementation of this on the "tg/actor" branch if you want to play with it, but many perfectly reasonable things will currently not work. |
Beta Was this translation helpful? Give feedback.
-
10.40.0 will ship two significant improvements to async/await support. async/await can now be used with Actor-isolated Realms let you use Realm inside @globalActor actor BackgroundActor: GlobalActor {
static var shared = BackgroundActor()
}
@BackgroundActor backgroundThreadFunction() async throws {
let realm = try await Realm(actor: BackgroundActor.shared)
try await realm.asyncWrite {
_ = 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)")
} Alternatively, you may wish to use Realm inside an actor, which is now supported: 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)
}
}
} There's a few more things on our roadmap that didn't make it into the initial set of work. Currently frozen objects are awkward with Sendable checking. Because the static type for frozen and live objects are the same, the compiler has no idea that some objects are safe to pass between threads and others aren't, and using frozen objects produces a lot of spurious sendability warnings. To fix this, we plan to introduce some sort of
Running an async query is currently possible but awkward. You can do weird things like the following to run a query on background thread and then get the result back in the original context, but obviously this needs a better API: var iterator = realm.objects(SwiftObject.self).sorted(by: [\.name]).collectionPublisher.values.makeAsyncIterator()
let results = try await iterator.next()
// or
for await results in realm.objects(SwiftObject.self).sorted(by: [\.name]).collectionPublisher.values {
// ...
break
} |
Beta Was this translation helpful? Give feedback.
-
What is the recommended way while @threadsafe does not work? passing Id and fetching? Also using |
Beta Was this translation helpful? Give feedback.
-
Is there a roundup of latest best practices (for when we're able to use latest swift versions)? It's a bit much to absorb the details of the changes such as is found in these threads and the changelog to piece it together. thanks |
Beta Was this translation helpful? Give feedback.
-
When I use this approach I get a warning in Xcode: 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)
}
}
} |
Beta Was this translation helpful? Give feedback.
-
It would be useful to know how Realm intends to support Swift Concurrency.
The old "thread-isolation" paradigm doesn't work in async/await because once a Task suspends to await something, it can resume on ANY thread, not just the one on which it was previously running.
People evaluating Realm as an option for their apps should know where the project stands on migrating to modern concurrency. A general plan, timeline, etc. It's a MAJOR inflection point in the Swift language and API design.
Beta Was this translation helpful? Give feedback.
All reactions