1

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?

gotnull
  • 26,454
  • 22
  • 137
  • 203

2 Answers2

1

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.

bhr
  • 2,279
  • 1
  • 23
  • 31
1

If var kittens: [PFObject]! is a property of your viewController, I think the easiest way to achieve what you want is declare it like so:

var kittens: [PFObject]! {
        didSet {
            dispatch_async(dispatch_get_main_queue(), { [weak self] in
                if let strongSelf = self {
                    strongSelf.pushToView()
                }
            })
        }

So every time you set kittens it will perform pushToView() function. And you will have to change this line:

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

To perform the query and not the pushToView(). For example:

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

Of course, you will need to create the function:

func performFetch() {
  var query = PFQuery(className: "Kittens")
  query.findObjectsInBackgroundWithBlock({ (objects, error) in
      //should be created weak self to avoid retain cycles
      if let kittenObjects = objects as? [PFObject] {
          self.kittens = kittenObjects
      }
  })
}

But if kittens is not a property you could create the performFetch() function, change the gesture recognizer and add self.pushToView() after kittens is set using dispatch_async dispatch_get_main_queue() (as in the didSet) because you need to update the UI in the main queue.

Dario
  • 3,105
  • 1
  • 17
  • 16