Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Receiving Location even when app is not running in Swift

Still very new to Swift. I have come from an Android background where there is BroadcastReceiver that can deliver location info to a service even though the app isn't running.

So I was looking for something similar in iOS/Swift and it appears that before this wasn't possible but it may be now. I am developing for iOS 10 but would be great if it was backwards compatible.

I found

startMonitoringSignificantLocationChanges

which I can execute to start delivering location updates, although this raises a few questions. Once I call this and my app is NOT running, are the updates still being sent ? And how would the app wake up to respond ?

Also restarting the phone and when it return, does this mean I still need call startMonitoringSignificantLocationChanges again meaning that I would have to wait for the user to execute my app. Or does it remember the setting after reboot ?

Still a little confused how to get around this, here's a brief explanation of what I am trying to do.

I would like to update the location of the phone even though the app is not running, this would be sent to a rest service every so often.

This way on the backend services I could determine if somebody is within X meters of somebody also and send them a push notification.

like image 320
Martin Avatar asked Jun 05 '17 12:06

Martin


1 Answers

It may or may not be a good solution but if I were you I would have used both startMonitoringSignificantLocationChanges and regionMonitoring.

Here is the sample I made which worked well with iOS 13.

Lets take regionMonitoring first. We have certainly no problems when the app is in foreground state and we can use the CLLocationManager's didUpdate delegate to get the location and send it to the server.

Keep latest current location in AppDelegate's property, lets say:

var lastLocation:CLLocation?

//And a location manager
var locationManager = CLLocationManager()

We have two UIApplicationDelegates

func applicationDidEnterBackground(_ application: UIApplication) {
    //Create a region
}

func applicationWillTerminate(_ application: UIApplication) {
    //Create a region
}

So whenever the user kills the app or makes the app go to background, we can certainly create a region around the latest current location fetched. Here is an example to create a region.

func createRegion(location:CLLocation?) {

    if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
            let coordinate = CLLocationCoordinate2DMake((location?.coordinate.latitude)!, (location?.coordinate.longitude)!)
            let regionRadius = 50.0

            let region = CLCircularRegion(center: CLLocationCoordinate2D(
                latitude: coordinate.latitude,
                longitude: coordinate.longitude),
                                          radius: regionRadius,
                                          identifier: "aabb")

            region.notifyOnExit = true
            region.notifyOnEntry = true

            //Send your fetched location to server

            //Stop your location manager for updating location and start regionMonitoring
            self.locationManager?.stopUpdatingLocation()
            self.locationManager?.startMonitoring(for: region)
        }
        else {
            print("System can't track regions")
        }
    }

Make use of RegionDelegates

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    print("Entered Region")
}

func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
    print("Exited Region")

    locationManager?.stopMonitoring(for: region)

    //Start location manager and fetch current location
    locationManager?.startUpdatingLocation()
}

Grab the location from didUpdate method

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    if UIApplication.shared.applicationState == .active {
    } else {
        //App is in BG/ Killed or suspended state
        //send location to server
        // create a New Region with current fetched location
        let location = locations.last
        lastLocation = location

        //Make region and again the same cycle continues.
        self.createRegion(location: lastLocation)
    }
}

Here I have made a 50m region radius circle. I have tested this and it is called generally after crossing 100m from your center point.

Now the second approach can me using significantLocationChanges

On making the app go background or terminated, we can just stop location manager for further updating locations and can call the startMonitoringSignificantLocationChanges

self.locationManager?.stopUpdatingLocation()
self.locationManager?.startMonitoringSignificantLocationChanges()

When the app is killed, the location is grabbed from didFinishLaunching method's launchOptions?[UIApplicationLaunchOptionsKey.location]

if launchOptions?[UIApplicationLaunchOptionsKey.location] != nil {
    //You have a location when app is in killed/ not running state
}

Make sure to keep BackgroundModes On for Location Updates enter image description here

Also make sure to ask for locationManager?.requestAlwaysAuthorization() by using the key

<key>NSLocationAlwaysUsageDescription</key>
    <string>Allow location</string>

in your Info.plist

There can be a third solution by taking 2 LocationManagers simultaneously.

  1. For region
  2. Significant Location Changes

As using significantLocationChanges

Apps can expect a notification as soon as the device moves 500 meters or more from its previous notification. It should not expect notifications more frequently than once every five minutes. If the device is able to retrieve data from the network, the location manager is much more likely to deliver notifications in a timely manner.

as per the give Apple Doc

So it totally depends on your requirements as the location fetching depends on many factors like the number of apps opened, battery power, signal strength etc when the app is not running.

Also keep in mind to always setup a region with good accuracy.

I know that this will not solve your problem completely but you will get an idea to move forward as per your requirements.

like image 177
Rajan Maheshwari Avatar answered Nov 09 '22 00:11

Rajan Maheshwari