Notification area icon in background thread

I tried to get data about the area in the background thread and add a notification block (iOS, Swift).

Basic example:

    func initNotificationToken() {

        DispatchQueue.global(qos: .background).async {
          let realm = try! Realm()
          results = self.getRealmResults()

        notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in

          switch changes {
           case .initial:
            self?.initializeDataSource()
            break
          case .update(_, let deletions, let insertions, let modifications):
           self?.updateDataSource(deletions: deletions, insertions: insertions, modifications: modifications)
           break
          case .error(let error):
            fatalError("\(error)")
            break
          }
        }
      }
    }

    func initializeDataSource() {
          // process the result set data 

         DispatchQueue.main.async(execute: { () -> Void in
            // update UI
         })
    }

    func updateDataSource(deletions: [Int], insertions: [Int], modifications: [Int]) {
          // process the changes in the result set data 

         DispatchQueue.main.async(execute: { () -> Void in
            // update UI
         })
    }

In doing so, I get

'Can only add notification blocks from within runloops'

I need to perform more extensive processing of the returned data and return to the main thread only after updating the interface after processing is complete.

Another way would probably be to re-fetch the data after any update in the background thread and then process it, but this seems like avoidable overhead.

Any suggestions for best practice to solve this problem?

+4
source share
1 answer

, :

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 , -, (, Realm). - , Realm , , , PerformBlock.

+6

Source: https://habr.com/ru/post/1667601/


All Articles