Stopping and Resuming Background Location Activity with CLLocationUpdates and CLBackgroundActivitySession

Hello,

This is my first post in the forums, and I'm still learning my way with iOS Development and Swift. My apologies if the formatting is not correct, or If I'm making any mistakes.

I'm currently trying to implement an iOS App where the device needs to share the location with my server via an API call.

The use case is as follows: the server expects location updates to determine if a device is inside/outside a geofence. If the device is stationary, no locations need to be sent. If the device begins moving, regardless of whether the app is in foreground, background, or terminated, the app should resume posting locations to the server.

I've decided to use the CLLocationUpdate.liveUpdates() stream, together with CLBackgroundActivitySession().

However, I have not been able to achieve the behavior successfully. My app either maintains the blue CLActivitySession indicator active, regardless of whether the phone is stationary or not, or kills the Indicator (and the background capability) and does not restore it when moving again. Below I've attached my latest code snippet (the indicator disappears and does not come back).

    // This method is called in the didFinishLaunchingWithOptions
    func startLocationUpdates(precise: Bool) {
        // Show the location permission pop up
        requestAuthorization()

        // Stop any previous sessions
        stopLocationUpdates()

        Task {
            do {
                // If we have the right authorization, we will launch the updates in the background
                // using CLBackgroundActivitySession
                if self.manager.authorizationStatus == .authorizedAlways {
                    self.backgroundActivity = true
                } else {
                    self.backgroundActivity = false
                    self.backgroundSession?.invalidate()
                }

                // We will start collecting live location updates
                for try await update in CLLocationUpdate.liveUpdates() {
                    // Handle deprecation
                    let stationary = if #available(iOS 18.0, *) {
                        update.stationary
                    } else {
                        update.isStationary
                    }

                    // If the update is identified as stationary, we will skip this update
                    // and turn off background location updates
                    if stationary {
                        self.backgroundSession?.invalidate()
                        continue
                    }

                    // if background activity is enabled, we restore the Background Activity Session
                    if backgroundActivity == true { self.backgroundSession = CLBackgroundActivitySession() }

                    guard let location = update.location else { continue }

                   // Do POST with location to server
                }
            } catch {
                print("Could not start location updates")
            }
        }
    }

I'm not sure why the code does not work as expected, and I believe I may be misunderstanding how the libraries Work. My understanding is that the liveUpdates stream is capable of emitting values, even if the app has gone to the background/terminated, thus why I'm trying to stop/resume the Background Activity using the "stationary" or "isStationary" attribute coming from the update.

Is the behavior I'm trying to achieve possible? If so, I'm I using the right libraries for it? Is my implementation correct? And If not, what would be the recommended approach?

Regards

The API you are using is not capable of the kind of fine control you are seeking.

That said, I am not sure if you actually need to implement this kind of complexity. When the stationary property is set, you will stop getting updates until the device moves again. So there is no need to stop the updates.

The problem with stopping them is, if the app gets terminated while the location updates are not active, then it would never get launched again once the device starts moving.

The old CoreLocation API could give you a little bit more control by using GPS updates, Significant Location Change APIs, and Region Monitoring all together, but then the recovery from the app getting terminated is going to be more clunky, causing possible location update misses after termination.

If the only reason you want to do all this is to avoid the blue location indicator, my recommendation is to leave that alone and let the users know that your app is tracking their location. Anything you do to avoid it will increase complexity and possibly reduce the user experience around your app's functionality.

Stopping and Resuming Background Location Activity with CLLocationUpdates and CLBackgroundActivitySession
 
 
Q