Location Manager - delegate must respond to locationManager:didUpdateLocations

Hello everyone,

I'm currently working with the Swift Composable Architecture (version 0.59.0) and the Composable Core Location (version 0.3.0). I've set it up according to the official instructions, but I've encountered an error that I'm struggling to resolve.

The error message I'm receiving is:

Assertion failure in -[CLLocationManager requestLocation], CLLocationManager.m:1337
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Delegate must respond to locationManager:didUpdateLocations:'

Here's the relevant block of code:

case .onAppear:
    return .merge(
        locationManager
            .delegate()
            .map({ action in
                return .locationManager(action)
            }),
        locationManager.requestWhenInUseAuthorization().fireAndForget()
    )
case .locationManager(.didChangeAuthorization(.authorizedAlways)),
     .locationManager(.didChangeAuthorization(.authorizedWhenInUse)):
    return locationManager.requestLocation().fireAndForget()
case .locationManager(.didChangeAuthorization(.denied)),
     .locationManager(.didChangeAuthorization(.restricted)):
    return .none
case let .locationManager(.didUpdateLocations(locations)):
    print("Received locations: \(locations)")
    return .none
case .locationManager:
    return .none

Has anyone encountered a similar issue or can offer insights on how to address this? Any guidance or suggestions would be immensely appreciated.

Thank you in advance for your assistance!

Vlado

Hi, so the reason for an error is that CLLocationManager called method on delegate before it was set.
I assume that you are using protocol based reducers (because you use relatively new version of TCA, but why not 1.2?). How do you store/pass the locationManager? Do you use @Dependency property wrapper or pass it in a reducer init? If you use @Dependency then this version of Composable Core Location is not really compatible without some tweaks to make sure that CLLocationManager is initialised on correct thread/actor.

If you not using Dependencies, than you can also try one thing. In your code:

.merge(
        locationManager
            .delegate()
            .map({ action in
                return .locationManager(action)
            }),
        locationManager.requestWhenInUseAuthorization().fireAndForget()
    )

You setup delegate and request location in parallel and requesting is just faster in reaching CLLocationManager than delegate setup. Even if you use .concatenate then there is no guarantee that delegate setup will be finished before request for location. Just to test it quickly you can try to setup delegate on onAppear action but request location using some button to split these to effects. It it works than you have to chain these effect together and make sure that order is preserved.

Little disclaimer: I didn't used combine versions of effects for some time, so I can be wrong ;)

You can check my strip down fork of composable-core-location adapted to swift concurrency: GitHub - Malauch/composable-core-location at lite (lite branch is what I currently use/experiment with).
You can also check that PR on Swift Additional Dependencies: Add `LocationManagerDependency` (#69) by RemiBardon · Pull Request #70 · tgrapperon/swift-dependencies-additions · GitHub It's something much more fully featured.