Modern Concurrency: Beyond the Basics

Oct 20 2022 · Swift 5.5, iOS 15, Xcode 13.4

Part 1: AsyncStream & Continuations

07. Wrapping Delegate With Continuation

Episode complete

Play next episode

About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 06. Unit Testing Next episode: 08. Wrapping Callback With Continuation

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 07. Wrapping Delegate 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.

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

Refresh your browser to make sure the core 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 and login. You'll implement the show location button in this episode and the next. In this episode, you'll use a manual continuation to make the core locationManager delegate work with Swift concurrency. In chat view, locate the first button in the HStack. This button action calls the model shareLocation method. Jump to shareLocation in BlabberModel. Add a location property. This is a manual continuation. It's useful for converting delegate methods into asynchronous functions. Let's take a look at the API. There are two flavors of manual continuation, CheckedContinuation and UnsafeContinuation. The first does run time checks and the second one doesn't. You'll use CheckedContinuation in these episodes. There are unsafe equivalents for everything you'll do. You can get a CheckedContinuation or a CheckedThrowingContinuation. withCheckedContinuation wraps the closure and gives you a CheckedContinuation back. withCheckedThrowingContinuation wraps a throwing closure. Use this when you need error handling. You must resume the continuation once, and exactly once. Enforcing this rule is the difference between Checked and UnsafeContinuations. You resume a continuation with one of the following methods. resume resumes the suspended task without a value. resume returning: resumes the suspended task and returns the given value. resume throwing: resumes the suspended task, throwing the provided error. And resume with: resumes with a result containing a value or an error. Now back to your location property. In this closure, you'll create a locationManager delegate and you'll inject continuation into it. Then you'll be able to use continuation in the delegate methods. In this app, you want to share the user location only once when the user taps the location button. But the standard core location manager doesn't have a callback API that does this, so you'll implement your own delegate type and code the logic to stop location updates after the first one comes through. In the utility group, open ChatLocationDelegate. You'll add two methods here to handle location updates and location errors. There's already a CLLocationManager to feed your proxy delegate with any updates. Add a private optional continuation property. First, create a typealias. Your delegate holds onto the continuation until it receives the location so you need to store it in a property. Next, create an initializer for injecting the continuation. Call super.init so you can set self as the delegate. Then the locationManager can request authorization. Now go back to BlabberModel to finish the closure in shareLocation. You create a ChatLocationDelegate injecting the continuation provided by with CheckedThrowingContinuation. Now back to ChatLocationDelegate to implement the first delegate method. This method gets called immediately after the locationManager is created when the location permissions update. If the user grants permission, the locationManager starts getting location data. If the user hasn't responded to the permissions request, which would happen the first time they run the app, you do nothing. For all other cases, you want to throw an error. Use your continuation to implement the default case. You resume the continuation with an error. After resuming, you destroy the continuation because doing anything else with it is illegal. Reminder, you must call a continuation resume method exactly once from each code path. And then always set it to nil to make sure you don't try to use it more than once. Next, look at the delegate method that's called when the user's location updates. The locations argument contains a list of CLLocation values. Here, it's safe to return the first one to your own code. Again, use the continuation to implement this. Calling continuation.resume returning: returns that first locations element and resumes the original code execution at the suspension point. The suspension point back in shareLocation in BlabberModel. And destroy the continuation to make sure you can't accidentally use it again. Finally, use your continuation in the delegate method that handles errors. If the manager fails to fetch the device location, it calls this method on its delegate so you can update your app. Now the complete workflow is in place, once you set up the locationManager with a delegate, it will try to fetch the current location and will use the injected continuation to return either a location or an error. To review how this works, start in shareLocation in BlabberModel. You create and store the delegate to make sure it isn't immediately released from memory. When ChatLocationDelegate initializes the manager, it calls the ChangeAuthorizationDelegate method. After the user grants permission, the manager fetches the device location. The manager calls the delegate with an array of CLLocations. The delegate calls continuation and resumes by returning the first available CLlocation. The original call site let location equals try await withCheckedThrowingContinuation resumes execution letting you use the return location value. To test the result, add a print statement at the very bottom of the function outside the withCheckedThrowingContinuation closure. Now delete the app from the simulator, then build and run. Click the location button in the Xcode debug toolbar and select one of the locations. I'll pretend to be in Mumbai. The location icon fills with color. Login then tap the location button. Tap an Allow button. Your location appears in the console. It's just latitude and longitude. Paste it into a browser to see where it is. Great work. You've integrated one of the oldest iOS APIs into your state of the art Swift concurrency app. In the next episode, to show your simulated address as a check message, you'll wrap a continuation around an API that uses a completion handler callback.