Reputation: 1229
This is a fundamental problem with executing asynchronous blocks, and I haven't found a good solution yet.
The following code works, but it blocks the main thread, and I'd like to avoid that.
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
//check if transaction already exists
let trans = PFQuery(className: "Transactions")
trans.whereKey("itemId", equalTo: self.pfObject.objectId!)
//I need the count of objects in the query before I can proceed, but I don't want to block the main thread
let count = trans.countObjects()
if(count > 0){
return false
}else{
return true
}
}
This problem isn't specific to this part of my application. Normally, I can just set "count" (or whatever variable I need) in the closure of something like query.countObjectsInBackGroundWithBlock(), but I can't do that when I need to return something on the main thread.
Is there a solution to make my application wait for return without blocking the main thread? I actually don't think that there is in this case without redesigning a large portion of code, but I just want to make sure I'm not being naive.
What are the accepted solutions for these types of problems?
Upvotes: 1
Views: 290
Reputation: 415
Edit: this is basically Rob's answer with more characters
Rob's answer is very good. An alternative way, and please let me know if this is bad practice, is to call self.performSegueWithIdentifier
(I hope I got the method name right, from the completion block of countObjectsInBackgroundWithBlock (or, as danh points out, Parse discourage count queries, use something else, maybe findObjectsInBackgroundWithBlock and count the [AnyObject]? array).
//IBAction method
func buttonFunc() {
let query = PFQuery(classname: "ClassName")
query.whereKey(itemId, equalTo: self.pfObject.objectId!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
//maybe do some error checking etc.
if objects.count > 1 {
performSegueWithIdentifier(theParametersAndStuff)
}
}
}
If you need to pass the objects to the next ViewController you can prepareForSegue, since it will only be called if the conditional in the completion block of the buttonFunc
returns true.
(as I said, if someone think this is bad practice, please tell me why, I'm curios :) )
Edit: Of course you could put a Activity Indicator or similar. An alternative way is to preload that data, before the current viewController is shown, to either enable or disable the button triggering the segue (as danh pointed out).
Upvotes: 0
Reputation: 437947
The proper solution for this is to not call anything from shouldPerformSegueWithIdentifier
which won't finish very quickly (within milliseconds). If you need to perform some network request that may take a few seconds, you should:
Retire shouldPerformSegueWithIdentifier
;
Remove the segue from your UI control to the next scene;
Instead, connect up your button to an @IBAction
which will programmatically perform the asynchronous request, and only if you get the result you expected, manually perform the segue programmatically (see Switching Views Programmatically in Swift).
Upvotes: 5