Search code examples
swiftasynchronousparse-platformpfobject

Set variable asynchronously and use within view controller


Let's say I have the following asynchronous query:

var kittens: [PFObject]!

self.tempView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "pushToView"))

var query = PFQuery(className: "Kittens")
query.findObjectsInBackgroundWithBlock({ (objects, error) in
    if let kittenObjects = objects as? [PFObject] {
        self.kittens = kittenObjects
    }
})

I have a method that presents a view controller:

func pushToView() {
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let viewController: KittensViewController = mainStoryboard.instantiateViewControllerWithIdentifier("kittensViewController") as! KittensViewController
    viewController.kittens = self.kittens
    self.presentViewController(viewController, animated: true, completion: nil)
}

Because I'm setting self.kittens inside of an asynchronous query block it obviously doesn't work when I try to access self.kittens inside the presented view controller (it's always null).

What's the best method to get something like this working?


Solution

  • There are a couple of options. The main thing is that you need to update KittensViewController when the query completes.

    • Make KittensViewController a property in the view controller that holds the kittens array. When the query completes, you not only update self.kittens, but also self.kittensViewController.kittens = kittenObjects. This is not best design, but it should work.

    • Better design would be to have the query in an own controller class that fires a NSNotification when done. Then, you could listen anywhere on your code when new kittens arrive and update your views.

    The Query throws the notification

        var query = PFQuery(className: "Kittens")
        query.findObjectsInBackgroundWithBlock({ (objects, error) in
            if let kittenObjects = objects as? [PFObject] {
                 dispatch_async(dispatch_get_main_queue()) {
                     NSNotificationCenter.defaultCenter().postNotificationName("KittensQueryDidFinish", object:kittenObjects, userInfo:dataDict))
            }
        })
    

    The ViewController registers for the notification and updates his model

    In your viewDidLoad add the following:

    NSNotificationCenter.defaultCenter().addObserverForName("KittensQueryDidFinish", object: nil, queue: NSOperationQueue.mainQueue) { note in
         let kittens = note.object
         [weak self].kittens = kittens
    }
    

    With that method, you get updates everywhere you need. Now you have to solve the problem when the kittens already have been loaded. Therefore, I'd suggest you introduce a shared controller object (aka singleton) that does the query and stores the kittens. Then you can access kittens from everywhere.