I have a setup like the following:
// Queues
private static let mainQueue = dispatch_get_main_queue()
private static let writeQueue = dispatch_queue_create("com.tablelist.Tablelist.queue.realm.write", DISPATCH_QUEUE_SERIAL)
// Realms
private static let defaultRealm: Realm = try! Realm()
private static func getDefaultRealm(block: (Realm) -> ()) {
Dispatch.async(mainQueue) {
block(defaultRealm)
}
}
private static func getWriteRealm(block: (Realm) -> ()) {
Dispatch.async(writeQueue) {
block(try! Realm())
}
}
Originally I had a writeRealm
but since GCD makes no guarantee about which thread a block in a queue is run, I was forced to create a new Realm
each time in the write func.
I then have a public func:
/**
Asynchronously write data to the realm
*/
public static func write(block: (Realm) -> ()) -> Promise<Realm> {
let promise = Promise<Realm>()
getWriteRealm { writeRealm in
do {
try writeRealm.write {
block(writeRealm)
}
getDefaultRealm { realm in
promise.resolve(realm)
}
}
catch {
Dispatch.main {
promise.resolve(error)
}
}
}
return promise
}
This allows the caller to pass in a block where it can do any importing, and then fetch any imports on the main thread when the promise resolves. The problem is, sometimes the imported data is available to the Realm
on the main thread and sometimes it isn't. Is there a better approach here?
EDIT: Just to clarify, if I change the write
func to grab the default realm in both cases, all of my tests pass.
SOLUTION:
private static func getDefaultRealm(block: (Realm) -> ()) {
Dispatch.async(mainQueue) {
defaultRealm.refresh() // refresh the realm to bring to most recent state
block(defaultRealm)
}
}
private static func getWriteRealm(block: (Realm) -> ()) {
Dispatch.async(writeQueue) {
let realm = try! Realm()
realm.refresh() // refresh the realm to bring to most recent state
block(realm)
}
}
SOLUTION 2: (After simplifying further)
private static func getDefaultRealm(block: (Realm) -> ()) {
let queue = dispatch_get_main_queue()
getRealm(queue, block: block)
}
private static func getWriteRealm(block: (Realm) -> ()) {
let queue = dispatch_queue_create("com.tablelist.Tablelist.queue.realm.write", nil)
getRealm(queue, block: block)
}
private static func getRealm(queue: dispatch_queue_t, block: (Realm) -> ()) {
Dispatch.async(queue) {
let realm = try! Realm()
realm.refresh()
block(realm)
}
}
tl;dr; call Realm.refresh()
to advance a transaction to the latest state.
Realm's transactions are isolated to provide self-consistency. This allows performing transactions on any thread, at any time, without ever requiring you explicitly lock or use other types of resource coordination.
Both read and write transactions in Realm are based off the most recent successful write commit when first initialized, and remain on that version until refreshed. Realms are automatically refreshed at the start of every runloop iteration, unless Realm's autorefresh
property is set to false
. If a thread has no runloop (which is generally the case in a background thread), then Realm.refresh()
must be called manually in order to advance the transaction to the most recent state.
Realms are also refreshed when write transactions are committed (Realm.commitWrite()
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With