Modern Concurrency: Beyond the Basics

Oct 20 2022 Swift 5.5, iOS 15, Xcode 13.4

Part 1: AsyncStream & Continuations

7. Wrapping Delegate With Continuation

Episode complete

Play next episode

Next
Save for later
About this episode
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 6. Unit Testing Next episode: 8. 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.

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

You can unlock the rest of this video course, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.

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.

Share Location

In ChatView, locate the first Button in the HStack.

Button(action: {
  Task {
    do {
      try await model.shareLocation()
    } catch {
      lastErrorMessage = error.localizedDescription
    }
  }
}, label: {
  Image(systemName: "location.circle.fill")
    .font(.title)
    .foregroundColor(Color.gray)
})

Managing authorizations

Jump to shareLocation() in BlabberModel. Add a location property:

let location: CLLocation = 
try await withCheckedThrowingContinuation { [weak self] continuation in

}
let location: CLLocation = 
try await withCheckedThrowingContinuation { [weak self] continuation in

}

ChatLocationDelegate

In the Utility group, open ChatLocationDelegate.

// First, create a type alias 
typealias LocationContinuation = CheckedContinuation<CLLocation, Error>
private var continuation: LocationContinuation?
init(continuation: LocationContinuation) {
  self.continuation = continuation
  // call super.init() so you can set self as the delegate 
  super.init()
  manager.delegate = self
  // then the location manager can request authorization
  manager.requestWhenInUseAuthorization()
}
self?.delegate = ChatLocationDelegate(continuation: continuation)
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
  switch manager.authorizationStatus {
  case .authorizedAlways, .authorizedWhenInUse:
    manager.startUpdatingLocation()
  case .notDetermined:
    break
  default:
    // TODO: resume continuation instead of break
    break
  }
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
  switch manager.authorizationStatus {
  case .authorizedAlways, .authorizedWhenInUse:
    manager.startUpdatingLocation()
  case .notDetermined:
    break
  default:
    🟩
    continuation?.resume(
      throwing: "The app isn't authorized to use location data"
    )
    continuation = nil
    🟥
  }
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  guard let location = locations.first else { return }
  // TODO: resume continuation
}
func locationManager(
  _ manager: CLLocationManager,
  didUpdateLocations locations: [CLLocation]
) {
  guard let location = locations.first else { return }
  🟩
  continuation?.resume(returning: location)  // returns, resumes etc: read below
  continuation = nil  // read next sentence below
  🟥
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
  // TODO: resume continuation
  🟩
  continuation?.resume(throwing: error)
  continuation = nil
  🟥
}

Using your delegate

Now, the complete workflow is in place: Once you set up the location manager with the delegate, it will try to fetch the current location and will use the injected continuation to return either a location or an error.

print(location.description)