I have a function that queries a certain user in order to access an array of that user. I returns the user and I can access their array. However, the call is asynchronous and what is being returned is nil. The function over all has a completion handler, however, inside there is a query call and by default that query returns Void.
func _getAllMatches(completionHandler: ((UIBackgroundFetchResult) -> Void)!) -> Int{
var toReturn = [GTLUserUser]()
let query = GTLQueryUser.queryForUserList()
query.userBucket = "messages-20-messagestabletes-1465782960"
service.executeQuery(query, completionHandler: {(ticket, response, error) -> Void in
if error != nil{
self._showErrorDialog(error)
return
}
else{
let userCollection = response as! GTLUserCollection
if let newUsers = userCollection.items() as? [GTLUserUser]{
toReturn = newUsers
completionHandler(UIBackgroundFetchResult.NewData)
}
}
})
return toReturn[0].likedArray.count
}
How do I wait for this query to return and assign to "toReturn" so that it will actually return something instead of returning nothing.
Since it's an asynchronous method, you cannot return the value, but instead follow the completion handler pattern, including the data returned as a parameter:
func performAllMatchesQueryWithCompletionHandler(completionHandler: (UIBackgroundFetchResult, [GTLUserUser]?, ErrorType?) -> ()) {
let query = GTLQueryUser.queryForUserList()
query.userBucket = "messages-20-messagestabletes-1465782960"
service.executeQuery(query) { ticket, response, error in
guard error == nil else {
completionHandler(.failed, nil, error)
return
}
if let userCollection = response as? GTLUserCollection, let newUsers = userCollection.items() as? [GTLUserUser] {
completionHandler(.newData, newUsers, nil)
} else {
completionHandler(.noData, nil, nil)
}
}
}
I infer from your use of UIBackgroundFetchResult
, that you're doing a background fetch. If so, your performFetchWithCompletionHandler
might look like so:
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
performAllMatchesQueryWithCompletionHandler { fetchResult, users, error in
switch fetchResult {
case .failed:
// do whatever you want if there was an error
case .noData:
// do whatever you want when there is no data
case .newData:
// do whatever you want with `users`, presumably updating your model or what have you
}
completionHandler(fetchResult)
}
}
You could then call this method from viewDidLoad
or wherever appropriate for your app.
Note, I removed the leading underscore on the method names, as that's not a common convention in Swift, but call your methods whatever you want. And I renamed _getAllMatches
to performAllMatchesQueryWithCompletionHandler
, as it makes it more clear that you're performing an asynchronous query.
In comments, you say that you're not doing background fetch, but rather are populating a table. So you might do something like:
func retrieveDataForTableView(tableView: UITableView) {
performAllMatchesQueryWithCompletionHandler { fetchResult, users, error in
switch fetchResult {
case .failed:
// do whatever you want if there was an error
case .noData:
// do whatever you want when there is no data
case .newData:
// do whatever you want with `users`, presumably updating your model or what have you
// once you've updated your model, you can reload the table:
tableView.reloadData()
}
}
}
Note, I've assumed that this completion handler is running on the main thread. If not, you'd want to dispatch_async(dispatch_get_main_queue()) { ... }
the code that updates the model and calls reloadData
.
Personally, I wouldn't be inclined to use UIBackgroundFetchResult
for my performAllMatchesQueryWithCompletionHandler
if I wasn't really doing background fetch. I'd probably use my own enumeration for that, to avoid any confusion regarding the intent of this code.