1

I've developing an indipendent WatchOS app whose aim is identifying when an user leaves a specific area, sending consequentially a notification. In order to do that, the application heavily relies on background location updates.

So far, app is working fine. It fetches position based on distanceFilter property of CLLocationManager. The problem is battery. The approach I followed keep background location updates in execution, even though they're fetched only when a specific distance is "covered".

My idea then was to disable location update when the area is left by the user, and also disable this service during night hours. However, I'm facing serious problem with this type of approach.

My main problem is that disabling location update while in background does not allow me to resume it. I tried doing this with:

  • A Timer.
  • scheduleBackgroundRefresh(withPreferredDate:userInfo:scheduledCompletion:) method, calling startLocationUpdates() in the delegate

Nothing seems to work. My question is:

There is a way for resume background location updates if it was previously disabled? Thank you in advance.

UPDATE n.2: I've tried to execute location updates with WKApplicationRefreshBackgroundTask but it just ignore requestLocation() function (suggested by @RomuloBM)

//In extension delegate handle() function
case let backgroundTask as WKApplicationRefreshBackgroundTask:
                // Be sure to complete the background ta
                LocMng = LocationManager() // I even tried to create a new element!
                LocMng.LocMng.requestLocation()// it is just ignored
                backgroundTask.setTaskCompletedWithSnapshot(false)

I call a background task with this function in my LocationManager:

//In didUpdateLocation
if background {
            WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: Date(timeIntervalSinceNow: 30), userInfo: nil){ _ in
                print("Done")
                self.background = false
                self.LocMng.stopUpdatingLocation()
            }
        }

For reference, here is my LocationManager class:

enum ScanningMode {
    case Precise
    case Normal
}

class LocationManager : NSObject, CLLocationManagerDelegate, UNUserNotificationCenterDelegate, ObservableObject {

    let LocMng = CLLocationManager()
    let NotMng = UNUserNotificationCenter.current()
    var modeOfScanning: ScanningMode!
    var region: CLCircularRegion!
    var previousLocation: CLLocation!
    // variables for position...

    override init() {
        super.init()
        // stuff for my app...

        modeOfScanning = .Precise
        setupManager()
        setupNotification()
        startLocalization()


    }

    private func startLocalization(){
        switch modeOfScanning!{
        case ScanningMode.Precise:
            LocMng.desiredAccuracy = kCLLocationAccuracyBest
            LocMng.distanceFilter = 15
        case ScanningMode.Normal:
            LocMng.desiredAccuracy = kCLLocationAccuracyHundredMeters
            LocMng.distanceFilter = 80
        }
        LocMng.startUpdatingLocation()
    }

    private func setupManager(){
        LocMng.requestAlwaysAuthorization()
        LocMng.delegate = self
        LocMng.desiredAccuracy = kCLLocationAccuracyBest
    }

    private func setupNotification(){
        NotMng.delegate = self
        NotMng.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
                   if granted {
                       print("NotificationCenter Authorization Granted!")
                   }
        }
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == CLAuthorizationStatus.authorizedAlways{
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        LocMng.allowsBackgroundLocationUpdates = true

        // For the sake of clarity, I will cut out this chunk of code and 
        // just showing how I execute my action based on the result of location
        // This is just an example
        actualLocation = locations[length-1]
        //find if in a forget
        if previousLocation != nil{
            if !region.contains(actualLocation!.coordinate) &&     region.contains(previousLocation!.coordinate){
                  //Schedule notification
                  LocMng.stopUpdatingLocation() // <- this does not allow me to resume
            }
        }
        previousLocation = actualLocation
    }

}
King Powa
  • 441
  • 3
  • 9
  • 1
    See [that answer](https://stackoverflow.com/a/56602553/8761048) – Romulo BM Jan 14 '20 at 13:38
  • @RomuloBM sadly I'm not working with HTTPS server so I have no way of sending remote notification. – King Powa Jan 14 '20 at 13:55
  • 1
    Unfortunately it's the only way of execute code while the app is in background with a specific time. You can use [background fetch](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/updating_your_app_with_background_app_refresh) too, but you can only set a "minimum background fetch interval"this does not guarantee that it will occur exactly on the interval that you want – Romulo BM Jan 14 '20 at 16:10
  • Shouldn't update service work everytime in background? – King Powa Jan 15 '20 at 09:38
  • 1
    The background fetch? No, you will set a minimum interval, and after the interval, the OS will call your fetch at the better appropriate time that(only apple know how) it judge the best – Romulo BM Jan 15 '20 at 17:37
  • I tried what you've suggested, but I couldn't manage to get it worked. I will update the post to show you my attempt. – King Powa Jan 16 '20 at 08:19

0 Answers0