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 thegetSnapshot()
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 asvoid * ptr
. If you can change the function declaration tovoid 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
Multidimensional Dictionaries Possible in Swift
Xcode Error - Undefined Symbols for Architecture X86_64
Swift Alternative to Respondstoselector:
Swift: Overriding Typealias Inside Subclass
Swfitui List Make Scrolling Disabled
Swiftlint Overriding Project Settings Related to Spm
Getting an Issue with Upgrade to Xcode 10.2
Implicitlyunwrappedoptional in Init VS Later
Using State Variables as Inputs to a Func in Swiftui
Swift: Visual Glitches When Presenting a Main and Alternative (Login/Onboarding) Flow
How to Play Avplayeritems Immediately
Appending Text to Nstextview in Swift 3
How to Pass One Swiftui View as a Variable to Another View Struct
How to Use a Completion Handler to Put an Image in a Swiftui View