Search code examples
swiftnotificationsrealmkey-value-observing

Realm Swift How to get notified when Result has been updated


I am using a Realm result object Result<AnObject>as a datasource for uitableview. there is some late API calls in different view controller that load more AnObject objects. what I want is to get notified when my datasource has been update to update the table view. I did some searchs and knew I need to use KVO, but I could not find any example to how to use it with realm.My code is something like below:

class myViewController: UIViewController, UITableViewDatasource {
    let realm = try! Realm()
    override func viewDidLoad() {
        super.ViewDidLoad()
        var datasource = realm.objects(AnObject.self)
        // I need to some how observe the change of datasource
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return datasource.count
    }
    ...
}

Update I tried using realm.addNotificationBlock(the same way in @joern answer) before it works, but the prolem is the block will run when any realm object updated not only the datasouce type. which reloads the table too many unnesseccary times.

Update 2 My App has a CalendarViewController which contains a FSCalenar View in the upper half and a container in the lower half linked to EventsViewController which have tableview for the events. I have so many events for long period of time to get from the API. so I make about 20 call to the API each of them gets some events. and add all calls operations in NSOperationQueue then set Operations priorities according to what I need to load first. So each API call update the datasource object when it finishes. I need the events tableview to relaod then. API call happens in APIManager class method that has been called by CalendarViewController


Solution

  • You don't have to use KVO. You can use Realm's notification feature:

    Realm instances send out notifications to other instances on other threads every time a write transaction is committed. These notifications can be observed by registering a block:

    let token = realm.addNotificationBlock { notification, realm in
        let realm = try! Realm()
        self.datasource = realm.objects(AnObject.self)
        self.tableView.reloadData()
    }
    

    As long as you keep a strong reference to this token you will get the notifications.

    Update:

    If you don't want to use Realm's notifications you can post you own notification whenever a API call returns results and then reload your table view accordingly:

    Add your EventViewController to the default NSNotificationCenter and add an action:

    override func viewDidLoad() {
        super.viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("didReceiveReloadNotification:"), name: "RELOAD_NOTIFICATION", object: nil)
        ...
    }
    
    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
    func didReceiveReloadNotification(notification: NSNotification) {
        let realm = try! Realm()
        datasource = realm.objects(AnObject.self)
        tableView.reloadData()
    }
    

    And then whenever one of your API request operations finishes post the notification:

    dispatch_async(dispatch_get_main_queue()) { () -> Void in
        NSNotificationCenter.defaultCenter().postNotificationName("RELOAD_NOTIFICATION", object: nil)
    }