I was trying to fetch realm data on the background thread and add a notification block (iOS, Swift).
Basic example:
To add a notification on a background thread you have to manually run a run loop on that thread and add the notification from within a callout from that run loop:
class Stuff {
var token: NotificationToken? = nil
var notificationRunLoop: CFRunLoop? = nil
func initNotificationToken() {
DispatchQueue.global(qos: .background).async {
// Capture a reference to the runloop so that we can stop running it later
notificationRunLoop = CFRunLoopGetCurrent()
CFRunLoopPerformBlock(notificationRunLoop, CFRunLoopMode.defaultMode.rawValue) {
let realm = try! Realm()
results = self.getRealmResults()
// Add the notification from within a block executed by the
// runloop so that Realm can verify that there is actually a
// runloop running on the current thread
token = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
// ...
}
}
// Run the runloop on this thread until we tell it to stop
CFRunLoopRun()
}
}
deinit {
token?.stop()
if let runloop = notificationRunLoop {
CFRunLoopStop(runloop)
}
}
}
GCD does not use a run loop on its worker threads, so anything based on dispatching blocks to the current thread's run loop (such as Realm's notifications) will never get called. To avoid having notifications silently fail to do anything Realm tries to check for this, which unfortunately requires the awakward PerformBlock dance.