iOS - Swift - Function That Returns Asynchronously Retrieved Value

iOS - Swift - Function that returns asynchronously retrieved value

Yes, it is possible to do this. Its called a closure, or more commonly a callback. A callback is essentially a function that you can use as an argument in another functions. The syntax of the argument is

functionName: (arg0, arg1, arg2, ...) -> ReturnType

ReturnType is usually Void. In your case, you could use

result: (image: UIImage?) -> Void

The syntax of calling a function with one callback in it is

function(arg0, arg1, arg2, ...){(callbackArguments) -> CallbackReturnType in
//code
}

And the syntax of calling a function with several callbacks is (indented to make it easier to read)

function(
arg0,
arg1,
arg2,
{(cb1Args) -> CB1Return in /*code*/},
{(cb2Args) -> CB2Return in /*code*/},
{(cb3Args) -> CB3Return in /*code*/}
)

If your callback function escapes the main function (the callback is called after the main function returns), you must add @escaping in front of the callback's argument type

You're going to want to use a single callback that will be called after the function returns and that contains UIImage? as the result.

So, your code could look something like this

func imageFromFile(file: PFFile, result: @escaping (image: UIImage?) -> Void){
var image: UIImage?

file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in
//this should be 'error == nil' instead of 'error != nil'. We want
//to make sure that there is no error (error == nil) before creating
//the image
if error == nil {
image = UIImage(data: data!)
result(image: image)
}
else{
//callback nil so the app does not pause infinitely if
//the error != nil
result(image: nil)
}
}
}

And to call it, you could simply use

imageFromFile(myPFFile){(image: UIImage?) -> Void in
//use the image that was just retrieved
}

Swift Function returning a value from asynchronous firebase call

The problem with

var recipients : [String] = [""]
DispatchQueue.main.async {
GetRecipientsFor(GroupChatID: gchatID) { result in
print(result)
recipients = result
}
}
print(recipients) // Completes before recipients = result

is that the last line is happening before the async call.

To explain futher print(recipients) happens before recipients = result. All logic using recipients needs to happen within that completion block. All you need to do is

func getRecipients(completion: @escaping ([String]) -> ()) {
var recipients : [String] = [""]
DispatchQueue.main.async {
GetRecipientsFor(GroupChatID: gchatID) { result in
print(result)
completion(result)
}
}
}

if you want to have further logic included you can call a function within the completion i.e. handleResults(result). I think it would be very beneficial to read more about closures/completion blocks/and async calls.

Wait for async function return value in swift

The way you have set this function will not work as the query method is asynchronous. This can be fixed in two ways:

1) Use the PFQuery synchronous category:
http://parse.com/docs/ios/api/Categories/PFQuery(Synchronous).html

The only disadvantage to this approach is that the method will become blocking so make sure to call it from a background thread.

2) Restructure the function to use a completion block instead of a return value..i.e:

    func loadCurrentUserData(completion: (score: Int!, error: NSError?) ->()) {
let query = PFQuery(className: "userScore")
let userId = PFUser.currentUser()!
var currentUserScore: Int = 0

query.whereKey("user", equalTo: userId)
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
let scoreReceived = objects![0]["score"] as! Int
currentUserScore = scoreReceived
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.userScore.text = "\(scoreReceived)"
})
completion(score: currentUserScore, error: nil);
} else {
print("Error: \(error!) \(error!.userInfo)")
}
completion(score: currentUserScore, error: error);

}
}

Swift: Retrieve value from asynchronous call before view appears

Note:

  • enter group before calling asynchronous method
  • leave group is each of the respective completion/failure handlers
  • dispatch UI updates in notify block to main queue

Thus:

func updateEntries() {
guard let accessToken = NSUserDefaults.standardUserDefaults().valueForKey("accessToken") as? String else { return }
guard let cachedEntryKey = String(accessToken) + "food_entries.get" as? String else { return }

let group = dispatch_group_create()
dispatch_group_enter(group)

cache.fetch(key: cachedEntryKey).onSuccess { data in
...
// if successful, set labels in swipeView to data retrieved from cache
...
dispatch_group_leave(group)
} .onFailure { error in
print(error)
...
// if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
...
dispatch_group_leave(group)
}

dispatch_group_notify(group, dispatch_get_main_queue()) {
print("Retrieved Data")
self.removeProgressHUD()
}

}

Function returns before asynchronous request finishes

Define function with completion Handler:

func searchQuery(query: String, completionHandler:(returntweets: [Tweet]) -> Void) {
var query_URL = query.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
if let query_URL = query_URL {
TwitterClient.sharedInstance.GET("https://api.twitter.com/1.1/search/tweets.json?q=\(query_URL)", parameters: nil, success: { (operation: AFHTTPRequestOperation!, response: AnyObject!) -> Void in
var tweets:[Tweet] = parseJSON(response)
println(tweets.count)
completionHandler(returntweets: tweets)

}, failure: { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in

})
}
}

Now call it where you required tweets:

searchQuery(query: "youQuery",{ (returntweets) -> Void in
//Do something
println(returntweets.count)
}


Related Topics



Leave a reply



Submit