Hi, I’m wondering why in Chapter 5, the ChatLocationDelegate
is stored in an instance variable of BlabberModel
. It seems like not really the right scoping to me, since shareLocation()
could be called multiple times on the same instance, and they would share the same instance variable. Meaning, an older delegate could go out of scope before its continuation was resumed if the variable is reassigned to a new delegate instance.
Initially I thought “well, why not just put it in the function? That seems like the right scope.” And it does work, and seems better, except that then you get a warning about writing to delegate
but never using it. My solution to that: delete the ChatLocationDelegate
’s init method, and move it all to a start(with:)
method that takes the continuation. Then you can initialize the delegate before the continuation block, and just assign the continuation inside the block. I’m curious why something like this wasn’t used in the book, since it’s almost the same interface, but I think better memory semantics / safer.
I did also go a couple steps further, in what I think makes a kind of nicer API (though arguably AsyncStream
should be used for this instead), though perhaps not the best for the context of a tutorial like this. I moved the withCheckedThrowingContinuation
call to inside ChatLocationDelegate
, so that the delegate just exposes an async interface. It looks like this:
class ChatLocationDelegate: NSObject, CLLocationManagerDelegate {
typealias LocationContinuation = CheckedContinuation<CLLocation, Error>
private var continuation: LocationContinuation?
private var manager: CLLocationManager?
func getLocation() async throws -> CLLocation {
return try await withCheckedThrowingContinuation({ continuation in
self.continuation = continuation
self.manager = CLLocationManager()
manager?.delegate = self
manager?.requestWhenInUseAuthorization()
})
}
...
}
And then to hide the delegate entirely, I added:
extension CLLocationManager {
class func getLocation() async throws -> CLLocation {
let delegate = ChatLocationDelegate()
return try await delegate.getLocation()
}
}
So then the call site just looks like: let location = try await CLLocationManager.getLocation()
.
Would love to hear thoughts on this! My coworkers and I have been enjoying the book, and discussing each chapter as we go through, one per week.