Leave a rating/review
Notes: 08. Wrapping Callback With Continuation
At the time of recording, Xcode 14 flags a runtime error in the location delegate continuation. If this happens to you, use Xcode 13 for the rest of Part 1.
Refresh your browser to make sure the course server is running or restart the server in Terminal. Continue with your project from the previous episode or open the starter project for this episode.
Build and run the app, login and check that the location button prints a location. If necessary, rerun the app to set a simulated location.
Wrapping callback APIs with continuation
Many of Apple’s “old” asynchronous APIs use callbacks: When you call a method like URLSession dataTask, you supply a completion handler, and this handler runs when the method finishes its work.
Manual continuations are useful for converting completion handlers to asynchronous functions.
The starter project contains a custom AddressCoder to convert a location into the nearest address. Its method addressFor(location:) requires a completion handler. It simulates a callback API. In this episode, you’ll wrap this in a continuation so it fits seamlessly into Blabber.
In BlabberModel, add an address property to shareLocation(), below the location property:
let address: String =
try await withCheckedThrowingContinuation { continuation in
}
This is like what you did when you wrapped the location manager delegate.
This time, you’ll return a String, the human-friendly address for the location coordinates.
Inside the closure, call addressFor(location:):
let address: String = try await
withCheckedThrowingContinuation { continuation in
🟩
AddressEncoder.addressFor(location: location) { address, error in
}
🟥
}
In the completion callback, you receive an optional address and an optional error, and you’ll supply a completion handler in the closure.
Use a switch statement to handle the possible cases:
let address: String = try await
withCheckedThrowingContinuation { continuation in
AddressEncoder.addressFor(location: location) { address, error in
🟩
switch (address, error) {
// type action on same line, except for last case
case (nil, let error?): // If you get an error, throw it
continuation.resume(throwing: error)
case (let address?, nil): // If you get an address, return it
continuation.resume(returning: address)
case (nil, nil): // If you get nothing at all, throw a generic error
continuation.resume(throwing: "Address encoding failed")
// copy-paste 2nd case, remember to move let out of parentheses
case let (address?, error?): // If you get both address and error...
continuation.resume(returning: address) // return the address...
print(error) // and print the error
}
🟥
}
}
These are pretty much the same actions you’d write into a normal completion handler, except now you pipe them through the continuation. Finally, use your address property:
try await say("📍 \(address)")
Build and run the app, login, and tap the location button. If you get an error or can’t see the location icon, stop the app and run it again, grant permission and set a location before you try the location button again. If it still doesn’t work, delete the app from the simulator and try again.
Now you know how to wrap delegates and callbacks in continuations, your async/await code can work alongside your existing codebase, not against it.
In the next episode, you’ll learn more about debugging and testing your asynchronous code.