How to update user location on demand using silent push notifications? iOS
Asked Answered
Z

2

6

I want to track users current location only on demand. I need only initial location, not continuos updates. I thought best way to do this would be sending silent notification to user in order to get user's current location once. Receiving silent notification works just fine, but LocationManager doesn't get initial fix on the location. My Location Manager Delegate class instantiates normally but doesn't fire didUpdateLocations function at any point after location manager method startUpdatingLocation() is called. When App starts normally from AppDelegate:didFinishLaunchingWithOptions, LocationManager updates users location without problems.

I'm developing on Xcode 6.3.2 to iOS 8.3

My AppDelegate:DidReceiveRemoteNotification looks like this:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) {

    var bgTask = bgManager()
    bgTask.registerBackgroundTask()
    LocManager.locationMngr.startUpdatingLocation() // LocManager is global variable

    let delayInSeconds = 8.0
    let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
    dispatch_after(popTime, dispatch_get_main_queue()) {
            println(LocManager.updated)
            bgTask.endBackgroundTask()
            handler(UIBackgroundFetchResult.NewData)
    }
}

I first tried only with line:

LocManager.locationMngr.startUpdatingLocation()

but when it didn't work I added background task and dispatch_after to make sure my location Manager would get enough time to retrieve first location update before termination.

Here is the LocationManager class:

class LocationManager: NSObject, CLLocationManagerDelegate {
let locationMngr:CLLocationManager
var coord:CLLocationCoordinate2D
var updated:Bool
    override init() {
        locationMngr = CLLocationManager()
        coord = CLLocationCoordinate2D()
        updated = false
        super.init()
        locationMngr.delegate = self
        locationMngr.desiredAccuracy = kCLLocationAccuracyHundredMeters

        locationMngr.startUpdatingLocation() //Should call didUpdateLocations after some time
    }

    //Doesn't get called in AppDelegate remoteNotifications method
    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)
    {
        var loc:CLLocation = locations.first as! CLLocation
        self.coord = loc.coordinate
        locationMngr.stopUpdatingLocation()
        updated = true
    }

and Background Manager class:

class bgManager:NSObject{
    var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
    override init(){
        super.init()
    }
    func registerBackgroundTask() {
        backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
        [unowned self] in
        self.endBackgroundTask()
        }
    }

    func endBackgroundTask() {
        UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
        backgroundTask = UIBackgroundTaskInvalid
    }
}

JSON of received remote push:

"aps" : {
        "sound" : "",
        "content-available" : 1,
        "priority" : 5
    }

I have all required plist.info keys and background modes for fetch, notifications and location enabled in project Capabilities: Background Modes. plist.info snippet:

<dict>
    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>location-services</string>
        <string></string>
        <string></string>
    </array>
    <key>UIBackgroundModes</key>
    <array>
        <string>remote-notification</string>
        <string>fetch</string>
        <string>location</string>
    </array>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Your location is needed for this app.</string>
    ...

I've browsed SO furiously for similar problem and couldn't find any helping solution including this question:

IOS - Running background task for update user location using swift

Periodic iOS background location updates

Core Location delegate methods not getting called in iOS 8.3 Xcode 6.3.1

Swift: location not being updated when returned from background

Zsazsa answered 15/7, 2015 at 13:0 Comment(6)
I feel like this would almost certainly get your app rejected.Anaerobe
What kind of app are you making? Why do you need to do this?Ensheathe
@Anaerobe If I find other way to achieve same functionality I will do it. Any suggestions? :)Zsazsa
@Ensheathe I want to receive certain users locations on demand. I only need to receive other users' locations once and not on a continuous basis. To avoid spamming visible notifications, I want to do it silently.Zsazsa
You can use the navigation Apis to keep your app open in the background. Their navbar will way "foo using your location in the background". But once they fully close your application you can't get their location. This is intentional. I know I wouldn't want a random app knowing where I am whenever the creator wants to know.Anaerobe
Did you find a solution?Vaporimeter
S
2

I came across the same issue and went through the exact steps as OP to no avail. Eventually I figured out that I only had requested CLLocationManager.requestWhenInUseAuthorization() which only provides location when app is in foreground. I had to request CLLocationManager.requestAlwaysAuthorization() in order for app to request location in background. Also make sure you have NSLocationAlwaysUsageDescription entry in Info.plist file. With these changes, I was able to use CLLocationManager.startUpdatingLocation() directly and was able to receive the location updates in background.

Shift answered 14/11, 2016 at 19:27 Comment(1)
Can you post a working example of your code? I'm attempting to do something similar to OP.Imperium
O
2

When the app is launched in the background, calling startMonitoringSignificantLocationChanges() before startUpdatingLocation() worked for me.

Remember to call stopMonitoringSignificantLocationChanges() and stopUpdatingLocation() when you are finished.

Oared answered 1/12, 2016 at 14:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.