How I can return value from async block in swift
Like @rmaddy said, you have no other way than to use completion handlers.
func getAppConfigFromDB(_ key: String, completion: @escaping ((String) -> Void)) {
let value = String()
backgroundthread.async {
let inst = AppConfigDB.init(_APP_CONFIG_DB_PATH)
value = inst.getConfigurationInfo(key) // I want to return from here.
completion(value)
}
}
You call the method like this.
getAppConfigFromDB("") { (value) in
// Use value to do something
}
swift calling async function without a return value
What you're describing is a Task. For example:
Task { await `do`() }
stuff()
This will run do()
concurrently with stuff()
. If you need to keep track of when do()
completes, you can await the task's value:
let task = Task { await `do`() }
stuff()
await task.value // Doesn't actually return anything, but will block
This kind of Task runs in the context of the current Actor, which is usually what you want. If you want something independent of the current Actor, you can use Task.detached()
instead.
If you've previously used DispatchQueues, in many of the places you would have written queue.async { ... }
, you can now write Task { ... }
. The new system is much more powerful, but it maps fairly nicely to the old system if you want it to.
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
}
Returning data from async call function and save variables after calling webservice in swift
First, there's no point in using resultadoWebService
. So I'd simplify your snippet like so:
WebService.llamarWebService { datos in
print([datos[0],datos[1],datos[2],datos[3],datos[4]])
}
Second, you asked about updating variables. Actually, though, there's no point in updating local variables, but perhaps you want to update properties of your calling object. If so, update them inside the closure, and then trigger whatever UI update (or whatever), is appropriate for the results:
var var1: String?
var var2: String?
func performRequestAndUpdateUI() {
WebService.llamarWebService { datos in
guard datos.count == 5 else {
print("didn't get the number of items data I expected")
return
}
guard datos[0] == "OK" else {
print("not everything was OK")
return
}
// note, you probably want to dispatch all model updates to the main queue
// to simplify synchronization. also UI updates must happen on main queue,
// too.
dispatch_async(dispatch_get_main_queue()) {
self.var1 = datos[1]
self.var2 = datos[2]
...
// do whatever UI updates you want here
}
}
// but don't try to use `datos` results here, because the above runs
// asynchronously and we don't have the result by the time we get here
// in our code.
}
Now, whether datos
will always have five items, and whether you want to check that datos[0]
was OK
is all up to you, but hopefully it illustrates the idea, namely that you're dealing with an asynchronous method, so you can't just immediately use the values passed back in the closure, but rather you have to constrain your use of them to the closure itself. And if you're updating the model and/or UI, make sure to dispatch that back to the main thread. But you cannot immediately update variables, because the response from the web service is asynchronous, and won't be called until later.
I might suggest a slightly safer (and simpler) retornarDatos
, which excises the !
forced unwrapping operators, perhaps using optional chaining like so:
class func retornarDatos(data: NSData) -> [String]? {
return String(data: data, encoding: NSUTF8StringEncoding)?
.componentsSeparatedByString(",")
}
Or, if you really want to return an empty string array upon error like the current rendition, you could do:
class func retornarDatos(data: NSData) -> [String] {
return String(data: data, encoding: NSUTF8StringEncoding)?
.componentsSeparatedByString(",") ?? [String]()
}
By the way, I'd suggest you get your arms around the above code/concepts, but once you do, you may want to reevaluate your web service design. I would suggest considering using JSON for the response, as the parsing process is more robust and it's easier to differentiate between fundamental web service errors and a web service that successfully returned something (whether it was "OK" or not).
Swift write an async/await method with return value
Thanks, vadian comment. I used a closure as an input parameter of the method.
// MARK: - PassCode Methods
func checkPassCode(completionHandler:@escaping (_ flag:Bool) -> ()) {
let storePin = getStorePin()
let userPin = self.pin.joined(separator: "")
AsymmetricCryptoManager.sharedInstance.decryptMessageWithPrivateKey(storePin as Data) { (success, result, error) -> Void in
if success {
let pin = result!
print("userPin is: \(userPin)")
print("storePin is: \(pin)")
completionHandler(userPin == pin)
} else {
print("Error decoding base64 string: \(String(describing: error))")
completionHandler(false)
}
}
}
func getStorePin() -> NSData {
if let pin = self.keychain.get("pin") {
return NSData(base64Encoded: pin, options: []) ?? NSData()
}
return NSData()
}
and then call this method:
checkPassCode { success in
if success {
print("sucess")
} else {
print("not sucess!")
}
}
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.
How to exit a for loop with data from an async function in swift?
To see what the issue is with what you're apparently trying to do, let's simulate it in a simpler way. I'll use an asynchronous random number generator. Try this in a playground:
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func asyncRand(_ completion: @escaping (Int) -> ()) {
let d = Double.random(in: 1...3)
delay(d) {
let i = Int.random(in: 1...100)
completion(i)
}
}
func go() {
for i in 1...12 {
asyncRand() { result in
print(i, result)
}
}
print("finished")
}
go()
The result in a typical run might be:
finished
8 13
1 15
9 9
10 56
7 57
3 87
2 70
11 88
6 82
12 16
4 81
5 46
So there are two issues here, because of the asynchronous nature of the central asyncRand
call: the results come back in no order, over time, and the go
method itself (the loop) finishes before any results come back, so there is no opportunity to find out which result is the maximum.
To straighten that out, while we are waiting for async/await
to appear in Swift (possibly as soon as next week), you can use a dispatch group. Ignoring any threading issues, we might do this:
func go() {
let group = DispatchGroup()
var pairs = [[Int]]()
for i in 1...12 {
group.enter()
asyncRand() { result in
print(i, result)
pairs.append([i,result])
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
print("finished")
if let maximum = pairs.max(by: {$0[1] < $1[1]}) {
print(maximum)
}
}
}
Now the result is (for example):
11 52
8 6
2 1
4 6
12 77
1 88
7 45
9 36
6 25
3 22
10 78
5 33
finished
[1, 88]
So you see we have "waited" until all the results have come back, and we have picked out the pair where the largest value was returned.
So you could do something along those lines. But whether that's a good idea is another question.
Synchronous and Asynchronous code behaviour
First of all, you should not show pictures of code, only the code itself. Otherwise it is hard to check your code.
Next, your code does not execute synchronously by design:
The parameter completion
of your function checkApp
is a block that is executed asynchronously after your function finished.
So, you first call the function and let it execute independently.
Then return status
is executed, which is with highest probability false
.
Eventually, your function finishes and sets the value of status
to whatever is the result of the function, but this is (again with highest probability) not returned.
EDIT:
If you want to wait for the completion to finish (which may be not a good idea on the main thread, because it would be blocked), you can wrap your code in a block like this:
// Before call to checkApp:
let lock = DispatchGroup.init()
// At the end of the completion block:
lock.leave()
// Before return status
let waitResult = lock.wait(timeout: .now() + 100)
if waitResult != .success { fatalError("Timeout") }
Related Topics
Firebase Custom Event Parameters Not Visible in Console
Converting Nsstring to Nsdate (And Back Again)
Ios 7 Status Bar Back to iOS 6 Default Style in Iphone App
Assertion Failure in Dequeuereusablecellwithidentifier:Forindexpath:
Cell Spacing in Uicollectionview
Returning Method Object from Inside Block
Ios :How to Get Facebook Album Photo'S Picker
How to Write a File to a Folder Located At Apple'S Files App in Swift
How to Create a Custom Pop Up View With Swift
Returning Data from Async Call in Swift Function
Attempt to Present Uiviewcontroller on Uiviewcontroller Whose View Is Not in the Window Hierarchy
Evenly Space Multiple Views Within a Container View
When Should I Compare an Optional Value to Nil
Getting and Setting Cursor Position of Uitextfield and Uitextview in Swift