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
Uitableview Auto Resizing Row Constraint Breaking Mysteriously on iPhone 6Plus
Is Possible to Simulate Touch Event Using an External Keyboard on iOS Jailbroken
How to Present View Controller from Right to Left in iOS Using Swift
How to Detect That the App Is Running on a Jailbroken Device
Fatal Error: Init(Coder:) Has Not Been Implemented Error Despite Being Implemented
How to Add a Button with Click Event on Uitableviewcell in Swift
iOS Storyboard Passing Data Navigationviewcontroller
iOS Uiimage Storage Formats, Memory Usage and Encoding/Decoding
Fbsdkaccesstoken Currentaccesstoken Nil After Quitting App
Get the Frame of Uibarbuttonitem in Swift
Presenting Uiviewcontroller from Skscene
How to Do a Native "Pulse Effect" Animation on a Uibutton - iOS
How to Create Border in Uibutton
iOS 7 and Later: Set Status Bar Style Per View Controller
Storyboard Reference in Xcode, Where Should We Use It
How to Create Static Library and Can Add Just .A File on Any Project in iOS