Handling location updates in background for widget refresh

I'am developing an iOS widget for my weather app, where the user can set the widget to "My location". This means the widget needs to be refreshed on location changes. Since a widget can't run a location manager in the background, apple tech support wrote that you have to setup a location manager in the main app and share the updated location data over App groups to the widget. This part works fine. I also managed to setup a location manager running in the background, but it uses too much battery and shows always the location indicator on top (blue bar) if the app is running, but I don't need this since its not a navigation app or something similar. How to configure a lightweight location manager running in the background?

class WidgetLocationManager: NSObject, CLLocationManagerDelegate {
    static let shared: WidgetLocationManager = WidgetLocationManager()
    
    let manager = CLLocationManager()
    
    override init() {
        super.init()
        
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyKilometer
        manager.distanceFilter = 1000
        manager.allowsBackgroundLocationUpdates = true
        manager.pausesLocationUpdatesAutomatically = false
        manager.activityType = .other
        manager.showsBackgroundLocationIndicator = false
    }
    
    func setupWidgetLocationManager() {
        manager.requestWhenInUseAuthorization()
        
        manager.startUpdatingLocation()
        manager.startMonitoringSignificantLocationChanges()
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if manager.authorizationStatus == .notDetermined || manager.authorizationStatus == .denied || manager.authorizationStatus == .restricted {
            manager.stopUpdatingLocation()
            manager.stopMonitoringSignificantLocationChanges()
        }
        
        if manager.authorizationStatus == .authorizedAlways || manager.authorizationStatus == .authorizedWhenInUse {
            manager.startUpdatingLocation()
            manager.startMonitoringSignificantLocationChanges()
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        if let newestLocation = locations.last {
            UserDefaults(suiteName: "group.com.***")?.set(Double(newestLocation.coordinate.latitude), forKey: "newest_location_latitude")
            UserDefaults(suiteName: "group.com.***")?.set(Double(newestLocation.coordinate.longitude), forKey: "newest_location_longitude")
            UserDefaults(suiteName: "group.com.***")?.set(Double(newestLocation.altitude), forKey: "newest_location_altitude")
            
            WidgetCenter.shared.reloadAllTimelines()
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        
    }
}

Capability for background modes location is set, also mandatory strings in info.plist for location privacy info.

Accepted Reply

I've managed to fix all issues. I use significant location change service only now. Also testing with "freeway drive" on simulator doesn't work for significant location changes, you have to test it on a real device.

Replies

I've managed to fix all issues. I use significant location change service only now. Also testing with "freeway drive" on simulator doesn't work for significant location changes, you have to test it on a real device.