Passing and Storing Closures/Callbacks in Swift

Passing and storing closures/callbacks in Swift

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")
})

How to pass data between controller, checker and back using callback closures Swift

The closure is actually a parameter of the method that receives and calls it, so if you want check(word:) to call some closure when it finishes checking, you'll need to add that as a parameter:

func check(word: String, closure: ()->Void)

If you want to pass a parameter to the closure, you'll need to specify that too:

func check(word: String, closure: (status: CheckResult)->Void)

I just made up CheckResult here; I just needed some type that you can use to convey the result of the check. It could be a an enumeration, for example:

enum CheckResult {
case ok;
case bad;
case empty;
}

Now, inside your check function, you can call the closure with the appropriate value, like: closure(.ok). To be more concrete, let's fill in your empty function:

class CheckTextField {
private let correctWord = "apple"

func check(word: String, closure: (_ status: CheckResult)->Void) {
if word.count == 0 {
closure(.empty)
} else if word == correctWord {
closure(.ok)
} else {
closure(.bad)
}
}
}

Then we can use it this way:

let checker = CheckTextField()

checker.check(word:"apple") { print($0) } // ok
checker.check(word:"pear") { print($0) } // bad
checker.check(word:"jackfruit") { print($0) } // bad
checker.check(word:"") { print($0) } // empty

You can replace the print($0) with code to set the label color or whatever else you might want to do. That's nice, because it keeps details about your view out of the CheckTextField class.

Swift - pass escaping closure to C API callback

You need a wrapper class so that a void pointer to the instance can be tunneled through the C function to the callback. The combination of passRetained() and takeRetainedValue() ensures that the wrapper instance is released only after the completion function has been called.

func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {

class Wrapper {
let completion: (GetSnapshotResult) -> Void
init(completion: @escaping (GetSnapshotResult) -> Void) {
self.completion = completion
}
}

let wrapper = Wrapper(completion: completion)
let observer = UnsafeMutableRawPointer(Unmanaged.passRetained(wrapper).toOpaque())

CAPIGetSnapshot(observer) { theObserver in
let theWrapper = Unmanaged<Wrapper>.fromOpaque(theObserver!).takeRetainedValue()
theWrapper.completion(
.success( snapshot: UIImage(), "test")
)
}
}

Some remarks:

  • I am assuming that the C function passes the ptr argument to the callback.

  • passRetained(wrapper) retains the object. That ensures that the wrapper instance is not released when the getSnapshot() function returns.

  • takeRetainedValue() in the closure consumes the retain. As a consequence, the wrapper instance is released when the closure returns.

  • completion is a closure and closures are reference types. wrapper.completion holds a reference to that closure as long as the wrapper instance exists.

  • Of course you can use the same variable names (“observer”, “wrapper”) inside the closure. I chose different names here (“theObserver”, “theWrapper”) only to emphasize that they are different variables, i.e. that the closure does not capture context anymore.

  • observer needs to be a mutable raw pointer only because the first argument of the C function is declared as void * ptr. If you can change the function declaration to

    void CAPIGetSnapshot(const void * ptr, void(*callbackOnFinish)(const void *))

    then let observer = UnsafeRawPointer(...) works as well.

  • For more information about conversion between object references to void pointers see for example How to cast self to UnsafeMutablePointer<Void> type in swift.

Instead of a custom wrapper class you can also take advantage of the fact that arbitrary Swift values are automatically boxed in a class type when cast to AnyObject (see for example AnyObject not working in Xcode8 beta6?).

func getSnapshot(completion: @escaping (GetSnapshotResult) -> Void) {

let wrapper = completion as AnyObject
let observer = UnsafeRawPointer(Unmanaged.passRetained(wrapper).toOpaque())

CAPIGetSnapshot(observer) { theObserver in
let theCompletion = Unmanaged<AnyObject>.fromOpaque(theObserver!).takeRetainedValue()
as! ((GetSnapshotResult) -> Void)
theCompletion(
.success( snapshot: UIImage(), "test")
)
}
}

The forced unwraps and forced casts are safe here because you know what it passed to the function. A failure to unwrap or cast would indicate a programming error. But I would prefer the first version instead of relying on this “magic”.

Wrapping C callbacks (without context) into Swift Closures

Solved by using a global variable

public struct glfw {
static func setErrorCallback(cbFun: @escaping ErrorFun) {
_g.errorCB = cbFun
glfwSetErrorCallback { err, desc in
_g.errorCB!(Error(rawValue: err)!, String(utf8String: desc!)!)
}
}
var errorCB: ErrorFun?
}
var _g = glfw()

How does a callback/closure know to wait after Asynchronous call is finished?

To understand closures and how they work one must understand that they are simply variables as Int or String, but they also have return value, most commonly Void. In a nutshell they allow you to wrap a block of code into single variable you can call later in the code.

Let's take a look at a simple closure definition:

let simpleClosure: (Int -> Void) = {
intValue in
print("Number is \(intValue)")
}

You can see that closure is instance like any Int or String, but with return type, in this case Void or ().

All closures are defined in { } braces with input values defined first (in this case it is intValue). This intValue will come from the Int parameter in closure definition. Let's see how we can call this closure.

simpleClosure(5)

This will print following:

// Number is 5

What happened here is that number 5 is passed to the Int closure parameter and you get it through intValue variable as described above. Since we are returning Void we don't need to write return statement and the closure finished execution.

Now to the question, closure doesn't know automatically when it is executed, rather it is called when some task is executed. Asynchronous network calls is pretty much the same. You perform network task synchronously (on low level TCP protocol) and notify the programmer when the task is finished via closure. This is the example of how it may be implemented:

func networkRequest(url: String, closure: (NSData?,ErrorType?) -> ()) {
runRequest(url)
let response = getServerResponse()
closure(response.data, response.error)
}

To summarize up, it's up to programmer to decide when closure is executed and that operation does not happen automatically.

ios memory leak using closure callbacks

In this closure call expression:

{ [weak self] in self!.onSubserviceReady }()

The compiled code generates a closure which does not have a strong reference to self.

Then, the closure is invoked by (), the closure evaluates self!.onSubserviceReady.

As you know this is a method reference and it returns the method as a closure. (Let's call it a method-closure.)

And in Swift, all method-closures have implicit strong references to self, it is irrelevant if self was a weak reference or not. The expression self!.onSubserviceReady (or self.onSubserviceReady, when self is non-Optional) always returns the same method-closure, which has a strong reference to self.

Once invoked, [weak self] does not affect the evaluated result. So, when you do not make a strong reference to the closure itself, [weak self] has no effect but just make self Optional.


On the other hand, your closure expression:

{ [weak self] in self?.onSubserviceReady($0) }

It is a closure itself and you are not invoking the closure there. So, a new closure is generated, which has a weak reference to self, and the closure (which is not a method-closure!) is passed to the initializer and held in an instance property.


You should better always create a new weak-self closure, rather than using a method-closure, if you want to avoid leaks caused by closures.

How to pass callback functions in Swift

Ok, now with the full code I was able to replicate your issue. I'm not 100% sure what the cause is but I believe it has something to do with referencing a class method (displayTimeRemaining) before the class was instantiated. Here are a couple of ways around this:

Option 1: Declare the handler method outside of the SecondViewController class:

func displayTimeRemaining(counter: Int) -> Void {
println(counter)
}

class SecondViewController: UIViewController {

// ...
var timer = Timer(duration: 5, handler: displayTimeRemaining)

Option 2: Make displayTimeRemaining into a type method by adding the class keyword to function declaration.

class SecondViewController: UIViewController {

var timer: Timer = Timer(duration: 5, handler: SecondViewController.displayTimeRemaining)

class func displayTimeRemaining(counter: Int) -> Void {
println(counter)
}

Option 3: I believe this will be the most inline with Swift's way of thinking - use a closure:

class SecondViewController: UIViewController {

var timer: Timer = Timer(duration: 5) {
println($0) //using Swift's anonymous method params
}

how to call Swift closure callback

The callback function is supposed to be called

func getUsers() {
self.userView?.startLoading()
userService.getUsers(delay: 5000) { users in
print("\(users.count) users added")
}
}

With trailing closure syntax the completion parameter name can be omitted. The in keyword after the return parameter is mandatory.



Related Topics



Leave a reply



Submit