How to Return Value After the Execution of the Block? Swift

how to return value after the execution of the block? Swift

You cannot simply return here as the reverseGeocodeLocation function is running asynchronously so you will need to use your own completion block:

var superPlace: MKPlacemark!

func getPlaceMarkInfo(coordinate: CLLocationCoordinate2D, completion: (superPlace: MKPlacemark?) -> ()) {
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
geocoder.reverseGeocodeLocation(location) { (arrayPlaceMark, error) -> Void in
if error == nil {
let firstPlace = arrayPlaceMark!.first!
let addressDictionaryPass = firstPlace.addressDictionary as! [String : AnyObject]

self.superPlace = MKPlacemark(coordinate: location.coordinate, addressDictionary: addressDictionaryPass)
completion(superPlace: superPlace)
} else {
completion(superPlace: nil)
}
}
}

Swift, return value from asynchronous block

Your Swift class User needs to implement the Printable protocol so that it can be used by println.

Something like this:

Class User: Printable {
let name = "Bob"
var description: String {
return "User: \(name)"
}
}

Read more in this Apple post

If you are feeling lazy, make your User class a subclass of NSObject which already implements the Printable protocol

Edit

A quick search of your println error turned this up. Not sure why the error falls through to println but heres a fix:

Try removing the first parameter of your function call, in Swift you don't need to type the first parameter to avoid redundancy:

a_user = list.getUserById(userId: "xyz...

Should be:

a_user = list.getUserById("xyz...

How can i return value from async block?

You can pass completion closure and after success or failure request execute it.

func xmlRequest(_ id:Int, completion: @escaping ((String) -> Void)){
var array1:Array<Any> = Array<Any>();
array1.append("body" as Any);
array1.append("date" as Any);
array1.append("author_id" as Any);

let con:NSDictionary = NSDictionary();
var field2:Array<Any> = Array<Any>();
field2.append([["id", "=", [id]]]);
let url = Login.BASEURL+XMLRPC_SERVICE_OBJECT;
let param3 = [Login.DATABASE, Login.LOGIN_ID, Login.PASSWORD, "mail.tracking.value","search_read",field2,con] as [Any];

AlamofireXMLRPC.request(url, methodName: "execute_kw", parameters: param3).responseXMLRPC { (response: DataResponse<XMLRPCNode>) in
var valueToReturn:String = "default"
switch response.result {
case .success(let value):
valueToReturn = "success"
case .failure(let error):
valueToReturn = "error"
}

completion(valueToReturn)
}
}

More about closures you can read at Apple documentation =)

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

Completion handler returning before task finishes

Note that query.initialResultsHandler is asynchronous and your code will execute in the following order:

private func getSteps(completion: (Int) -> ()) {
// #1
var val: Int = 0
...
// #2
query.initialResultsHandler = { query,result,error in
// #5
if let myresult = result {
myresult.enumerateStatistics(from: startDate, to: Date()) { (statistic, value) in
if let count = statistic.sumQuantity() {
// #6
val = Int(count.doubleValue(for: HKUnit.count()))
}
}
}
// #7
}
// #3
healthStore.execute(query)
// #4
completion(val)
}

Note that #4 is executed before #6.


The solution is to move completion inside the asynchronous block:

private func getSteps(completion: (Int) -> ()) {
...
query.initialResultsHandler = { query,result,error in
if let myresult = result {
myresult.enumerateStatistics(from: startDate, to: Date()) { (statistic, value) in
if let count = statistic.sumQuantity() {
val = Int(count.doubleValue(for: HKUnit.count()))
}
}
}
completion(val) // <- move here
}
healthStore.execute(query)
}

How can I return value from completion block to use it in other viewController?

queryCompletionBlock is executed asynchronously so by the time print(self.kcalUnitValid) is called queryCompletionBlock wouldn't have finished its execution yet, in other words, the order in how execution happens is like below sequentially:

  • 1.- queryOperation.queryCompletionBlock(..) is called
  • 2.- print(self.kcalUnitValid) is called // prints empty
  • 3.- after some seconds the result of queryOperation.queryCompletionBlock(..) is returned

Code

What you could do it probably is this:

func KcalCloudKitData() {
// .. code omitted
queryOperation.queryCompletionBlock = {cursor, error in
if error != nil {
// .. code omitted
} else {
for value in self.allRecords {
self.kcalUnitValid.append(value.object(forKey: "KcalValidUnit") as! [String])
}

// Segue to another ViewController
// And also sending kcalUnitValid
self.performSegue(withIdentifier: "AppLoaded", sender: self.kcalUnitValid)
}
}
// .. code omitted
}

// Actually in this method is when you decide
// what data you are going to send to the AnotherViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AppLoaded" {
let anotherViewController = segue.destination as! AnotherViewController
anotherViewController.kcalUnitValid = sender as! Array<[String]>
}
}

Resources:

Watch this video explaning async programming in Swift

Function not returning value from closure

So the reason why you're having this issue is because AlamoFire.request is asynchronous. There's a great explanation of asynchronous vs synchronous here But basically when you execute something asynchronously, the compiler does not wait for the task to complete before continuing to the next task, but instead will execute the next task immediately.

So in your case, the AlamoFire.request is executed, and while it's running, the next line after the block is immediately run which is the line to return leagueId which will obviously still be equal to zero since the AlamoFire.request task (function) has not yet finished.

This is why you need to use a closure. A closure will allow you to return the value, after AlamoFire.request (or any other asynchronous task for that matter) has finished running. Manav's answer above shows you the proper way to do this in Swift. I just thought I'd help you understand why this is necessary.

Hope this helps somewhat!

Edit:

Manav's answer above is actually partially correct. Here's how you make it so you can reuse that value the proper way.

var myLeagueId = 0;
getLeagueId(country: "brazil", season: "2019",success: { (leagueId) in

// leagueId is the value returned from the closure
myLeagueId = leagueId
print(myLeagueId)
})

The code below will not work because it's setting myLeagueId to the return value of getLeagueId and getLeagueId doesn't have a return value so it won't even compile.

myLeagueId = getLeagueId(country: "brazil", season: "2019",success: { (leagueId) in
print(leagueId)
})

How can I run code after the return statement in a function in Swift?

The answer to your question is no. But the solution to your problem is simple.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let validator:NSPredicate = NSPredicate(format:"SELF MATCHES %@","[A-Za-z0-9- ]+")
if(validator.evaluateWithObject(string) || string == "" /* i.e. backspace */) {
DispatchQueue.main.async {
print("about to process textField, timestamp: \(Date().timeIntervalSince1970)")
self.process(textField)
}
print("about to return true, timestamp: \(Date().timeIntervalSince1970)")
return true
}
else {
return false
}
}

DispatchQueue.main.async will defer execution to the next iteration of the run loop. With those print() statements you'll see this in the console. Your timestamps will be different but you'll see the small difference (about 15/1000 of a second in this case).

about to return true, timestamp: 1612578965.103658

about to process textField, timestamp: 1612578965.1188931

If you require a specific delay use DispatchQueue.main.asyncAfter

The best explanation I have found on this is Matt Neuburg's book "iOS 14 Programming Fundamentals with Swift". See the Delayed Performance section.

Code being executed after completion handler is called

return will exit out of the current context, which is the closure associated with the performAndWait. After that closure returns, execution continues with the next statement after performAndWait which is whatever is at #2.

You can move the code from point #2 inside the closure



Related Topics



Leave a reply



Submit