Arsh Aulakh
Arsh Aulakh

Reputation: 144

How to wait for a closure completion before returning a value

How to wait to return a value after a closure completion.

Example:

func testmethod() -> String {
   var abc = ""
   /* some asynchronous service call block that sets abc to some other value */ {
      abc = "xyz"
   }
   return abc 
}

Now I want the method to return only after xyz value has been set for variable and not empty string.

How to achieve this?

Upvotes: 8

Views: 5972

Answers (4)

vadian
vadian

Reputation: 285270

Since Swift 5.5 the recommended way is async/await. If the API doesn't provide an async version you can create your own with a Continuation

func testMethod() async -> String {
    return await withCheckedContinuation({ continuation in
        someAsynchronousServiceCallBlock() { result in
            continuation.resume(returning: result)
        }
    })
}

You have to call the mathod in an asynchronous context for example in a Task

Task {
    let result = await testMethod()
}

Upvotes: 2

Deven Patel
Deven Patel

Reputation: 1

Yes, it is possible to wait for the closure function to populate the data and then return the variable. Although, it is suggested to avoid using semaphore to do that, I think that is the only way.

func foo() -> String {
    var str = ""
    let semaphore = DispathSemaphore(value: 1)  //1 if you are waiting for one closure function to signal the semaphore to continue the main thread execution

    getAsync() {
        //populate the variable
        str = "bar"
        semaphore.signal()
    }
    semaphore.wait()
    return str
}

Upvotes: 0

KonDeichmann
KonDeichmann

Reputation: 968

this is absolutely not possible, because it is just not how asynchronous tasks are working.

what you could do is something like this:

func testmethod(callback: (abc: String) -> Void) {
   asyncTask() {
     callback(abc: "xyz")
   }
}

Have a nice day.

EDIT (for newer Swift Versions):

func testMethod(callback: @escaping (_ parameter: String) -> Void) {
    DispatchQueue.global().async { // representative for any async task
        callback("Test")
    }
}

Upvotes: -5

Jakub Truhlář
Jakub Truhlář

Reputation: 20720

It is possible (However make sure this is what you really want.).

You have to use something that will block a thread until a resource is available, like semaphores.

var foo: String {
    let semaphore = DispatchSemaphore(value: 0)
    var string = ""

    getSomethingAsynchronously { something in
        string = something
        semaphore.signal()
    }

    semaphore.wait()
    return string
}

Bare in mind that the thread you are working on will be blocked until the getSomethingAsynchronously is done.

Upvotes: 12

Related Questions