Background location service stops working in a period of time

272 views Asked by At

I'm developing an iOS application that users can save their traveling route to server (by posting their locations through API). The issue I am struggling with is several users reported that the route they saved is interrupted in the middle of the trip. In detail, when users review their saved route on map, there is a part of route being just a straight line because somehow locations of that route section are not sent to server or just because the device can not receive locations at that time. The weird thing is that the rest of route was recorded normally so seems like the location service stopped working for a period of time but after that it started again so my app could record it fine.

And the most frustrating thing is that I can not reproduce this issue.

Here are circumstances of the issue that user reported:
- User started the app then locked device screen and put it in their pocket, they did not touch it in the whole journey. No battery drain or crash happened.
- After driving about 8-9km and everything worked fine, route recording was interrupted in the next ~ 65km, then well-recorded again in the rest ~ 80km.

Below is my project setup:
- Background Modes in ON in Capabilities with Location updates.
- Locations received from location service are filtered based on timestamp and accuracy and saved to core data with a “isSent” flag marking if a location is sent successfully to server. This way my app can cover the case when network connection is down.
- Locations marked with false “isSent” flag will be sent to server every 30 seconds.

My LocationManager code:

class LocationManager: NSObject, CLLocationManagerDelegate {

    var locationManager: CLLocationManager = {
        var _locationManager = CLLocationManager()
        _locationManager.delegate = self
        _locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        _locationManager.activityType = .automotiveNavigation
        _locationManager.allowsBackgroundLocationUpdates = true
        _locationManager.pausesLocationUpdatesAutomatically = false

        return _locationManager
    }()


    func startLocationService() {
        locationManager.startUpdatingLocation()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.pausesLocationUpdatesAutomatically = false
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        var positionsToSavedToDb: [DbPositionModel] = []

        for location in locations {
            let howRecent = location.timestamp.timeIntervalSinceNow

            guard abs(location.timestamp.timeIntervalSinceNow) < 10 && location.horizontalAccuracy > 0 && location.horizontalAccuracy < 33 else {
                continue
            }

            if self.locations.count > 0 {
                let distanceSinceLastLocation = location.distance(from: self.locations.last!)
                let timeSinceLastLocation = location.timestamp.timeIntervalSince(self.locations.last!.timestamp)
                if (distanceSinceLastLocation < 5 && abs(timeSinceLastLocation) < 10) {
                    continue
                }
            }

            // Create DbPositionModel from location and append to positionsToSavedToDb
        }

        // Save positionsToSavedToDb to core data
    }

    @objc func each30Seconds(_ timer: Timer) {
        // Select in database DbPositionModel objects with false “isSent” flag to send to server
    }
}

Can you guys help me find out black holes in my code or anything I can do to reproduce / fix this issue? Thanks a lot!!!

1

There are 1 answers

1
mfaani On

Your setup looks fine to me. Just one question. When you say it didn't work for 60km and then it started working for 80km, was that all while in background? I mean the user didn't need to enter foreground for it to start working again did they?

Your limit for location.horizontalAccuracy is 33. You're thinking that it's going to be very accurate. I'm not sure, maybe the device/city are a bad combination, and then you're returning early. I suggest that you log the reason why you exit early. Their city might be different from yours. Also I've heard that the GPS of the iPhoneX even though has the correct location, it returns a high number for its horizontalAccuracy. Is this happening mostly for iPhoneX users?

enum LocationAccuracyError: Error {
    case stale(secondsOld: Double)
    case invalid
    case lowAccuracy(metersOff: Double)
}

extension LocationAccuracyError: LocalizedError{
    var errorDescription: String? {
        switch self {
        case .stale(let seconds):
            return NSLocalizedString("location was stale by: \(seconds) seconds", comment: "")
        case .invalid:
            return NSLocalizedString("location was invalid)", comment: "")
        case .lowAccuracy(let metersOff):
            return NSLocalizedString("location's horizontal Accuracy was off by likely more than: \(metersOff) meters" , comment: "")
        }
    }
}

And then have a function like this to check each location.

private func checkLocationAccuracy(from location: CLLocation) throws {

    let ageOfLocation = -location.timestamp.timeIntervalSinceNow

    if ageOfLocation >= maximumAcceptedStale {
        throw LocationAccuracyError.stale(secondsOld: ageOfLocation)
    }

    if location.horizontalAccuracy <= 0 {
        throw LocationAccuracyError.invalid
    }

    if location.horizontalAccuracy > MaximumAcceptedHorizontalAccuracy{
        throw LocationAccuracyError.lowAccuracy(metersOff: location.horizontalAccuracy)
    }
}

Your end usage would be like:

do {
       try checkLocationAccuracy(from: location)

   } catch let error {
       writelog("Bad Location: \(error.localizedDescription)")
   }

I'd also add logs around your app state as well e.g. add a log to capture didEnterBackground