Passing and storing closures/callbacks in Swift
Asked Answered
I

1

7

I would like to do the following in swift code:

I have to call my api in order to update several items. So I call the api for each item asynchronously. Each api call executes a callback function when it's done. These callbacks decrease a counter, so that when the counter reaches 0 I know that all my api calls are completed. When the counter reaches 0 I would like to call a final callback function (once, when all calls are complete), in order to update my UI and so forth. This final callback is passes to my service in the beginning and stored in a class property for later execution.

Executable Playground source:

// Playground - noun: a place where people can play

class MyService
{
    let api = MyApi()

    var storedFinalCallback: () -> Void = { arg in }
    var queue: Int                      = 0

    func update(items: [String], finalCallback: () -> Void )
    {
        // Count the necessary API calls
        queue               = items.count
        // Store callback for later execution
        storedFinalCallback = finalCallback

        for item in items {
            // Call api per item and pass queueCounter as async callback
            api.updateCall(item, callback: self.callback())
        }
    }

    func callback()
    {
        queue--
        // Execute final callback when queue is empty
        if queue == 0 {
            println("Executing final callback")
            storedFinalCallback()
        }
    }

}

class MyApi
{
    func updateCall(item: String, callback: ())
    {
        println("Updating \(item)")
    }
}

let myItems: [String]     = ["Item1", "Item2", "Item3"]
let myInstance: MyService = MyService()

myInstance.update(myItems, finalCallback: {() -> Void in
    println("Done")
})

The problem is that with this code the final callback is called in the wrong order. As the console output from the playground shows.

Apparently the callback function is already executed and not properly passed. However, this was the only way I was able to do it, without compiler errors.

Any help would be really appreciated - I have been stuck on this for two days now.

Ingeborgingelbert answered 2/4, 2015 at 20:5 Comment(4)
Do your api.update methods run asynchronously in parallel?Principally
Yes, they do.I figured that'd be best practise.Ingeborgingelbert
If you want to just pass the callback function without executing it, remove the two parentheses: api.updateCall(item, callback: self.callback)Principally
Change the method call to this api.updateCall(item,callback:self.callback) renders the following error: error: function produces expected type '()'; did you mean to call it with '()'? Feel free to copy my code above into a playground an try for yourself.Ingeborgingelbert
I
6

I finally found the working code:

// Playground - noun: a place where people can play

class MyService
{
    let api = MyApi()

    var storedFinalCallback: () -> Void = { arg in }
    var queue: Int                      = 0

    func update(items: [String], finalCallback: () -> Void )
    {
        // Count the necessary API calls
        queue               = items.count
        // Store callback for later execution
        storedFinalCallback = finalCallback

        for item in items {
            // Call api per item and pass queueCounter as async callback
            api.updateCall(item, callback: self.callback)
        }
    }

    func callback()
    {
        queue--
        // Execute final callback when queue is empty
        if queue == 0 {
            println("Executing final callback")
            storedFinalCallback()
        }
    }

}

class MyApi
{
    func updateCall(item: String, callback: () -> Void)
    {
        println("Updating \(item)")
        callback()
    }
}

let myItems: [String]     = ["Item1", "Item2", "Item3"]
let myInstance: MyService = MyService()

myInstance.update(myItems, finalCallback: {() -> Void in
    println("Done")
})
Ingeborgingelbert answered 4/4, 2015 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.