1

I have an iOS app that needs to track user's geolocation for a research project. However, the app gets automatically suspended by the iOS after a few hours and the geolocation tracking stops working.

Geofencing is not sufficient for my case as it's not accurate enough.

Is there any way to can prevent the app from getting suspended (unless user manually terminate it)?

One way I thought of is to play a silent music indefinitely, and display the music controls on lockscreen through MPRemoteCommandCenter, like how Spotify works.

Would that keep the app alive? (as I believe Spotify never gets killed unless user manually terminate it?)

Avery235
  • 4,756
  • 12
  • 49
  • 83
  • 5
    You can check this https://stackoverflow.com/questions/35515528/ios-gps-tracking-app-that-runs-all-the-time – Taras Chernyshenko Dec 26 '18 at 15:59
  • @TarasChernyshenko I have implemented all that but it's not working. In the answer: "App can go to suspended state, but when location manager receive new location app goes to background state" this doesn't seem to be the case. My app stopped sending location update to the server after it got suspended (which typically happens after a few hours/days). – Avery235 Dec 28 '18 at 06:37

2 Answers2

0

I have similar app which uses user location for tracking. Check if you have all these permissions in info.plist. And specifically tell users why you are using location permissions

<key>NSLocationAlwaysUsageDescription</key>
<string>Application needs permission to access your current location. 
</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Application needs permission to access your current location. 
</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Yogesh Tandel
  • 1,738
  • 1
  • 19
  • 25
  • Yes I have all of these. Your app is able to continue tracking user location even if the user doesn't open the app for days on iOS? – Avery235 Dec 28 '18 at 13:39
  • no if the app is closed. it wont track. Tracking is enabled if the app is in background – Yogesh Tandel Dec 28 '18 at 13:40
  • But the app will be suspended after staying in background for too long. Does your app automatically resume from suspension when user location changes? – Avery235 Dec 29 '18 at 00:48
  • If not, the user would have to regularly open the app to prevent it from getting suspended? – Avery235 Dec 29 '18 at 01:02
  • It stays in the background and the app keeps updating the location. I will share the code in couple of hours once I reach office – Yogesh Tandel Dec 29 '18 at 01:45
  • I have added my part of code used to another answer. I have an app which is used for tracking riders data and location once he starts the ride. The rider location is updated even when the app is in background -- https://itunes.apple.com/us/app/roaring-hawks/id1155878497?mt=8 – Yogesh Tandel Dec 29 '18 at 04:29
  • Yes I know it'll work in the background. What I meant is the app will go from background to suspended mode if the user doesn't open the app for a long time. – Avery235 Dec 29 '18 at 05:10
  • You get a blue bar on top when background location update is on. I have not seen my app getting suspended, – Yogesh Tandel Dec 29 '18 at 05:13
0

Here is a part of my code. I have removed the unwanted part so you might have to edit while using it.

import GooglePlaces
import GoogleMaps



class StartRideViewController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate{

var mapView: GMSMapView!
var locationManager = CLLocationManager()

override func viewDidLoad() {
    super.viewDidLoad()

let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
    notificationCenter.addObserver(self, selector: #selector(appMovedToForeGround), name: Notification.Name.UIApplicationDidBecomeActive, object: nil)

}


@objc func appMovedToBackground() {
    print("App moved to background!")
    print(isStartRide)
    if isStartRide == false{
        btn_Share.isHidden = true
        locationManager.allowsBackgroundLocationUpdates = false
        locationManager.stopUpdatingLocation()
    }
}

@objc func appMovedToForeGround() {
    //getMeRidersData()
}

func initiateLocation(){
locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        locationManager.distanceFilter = 1
        locationManager.startUpdatingLocation()
        //locationManager.startUpdatingHeading()
        locationManager.allowsBackgroundLocationUpdates = true

        //checkForLocationServices()
        checkLocationAuthorizationStatus()

guard let myLatitude = locationManager.location?.coordinate.latitude else{
        return
    }
    guard let myLongitude = locationManager.location?.coordinate.longitude 
else{
        return
    }


    showMap(myLatitude:myLatitude, myLongitude:myLongitude)

}
func showMap(myLatitude:Double, myLongitude:Double){
    let camera = GMSCameraPosition.camera(withLatitude: myLatitude, 
longitude: myLongitude, zoom: 17)
    mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
    //mapView = GMSMapView.map(withFrame: CGRect(x: 0, y: 0, width: 
self.view.frame.width, height: self.view.frame.height - 250), camera: camera)

    mapView?.center = self.view.center
    self.view.addSubview(mapView!)

    mapView.padding = UIEdgeInsetsMake(150, 0, 80, 0)
    mapView.settings.myLocationButton = true
    mapView.delegate = self
    //mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    mapView.isMyLocationEnabled = true
    frameForMapView.addSubview(mapView)

}

func checkLocationAuthorizationStatus() {
    let status = CLLocationManager.authorizationStatus()
    if status == CLAuthorizationStatus.notDetermined{
        print("NotDetermined")
        locationManager.requestWhenInUseAuthorization()
        CLLocationManager.locationServicesEnabled()
        locationManager.requestLocation()
    }else {
        print("Problem with authorization")
    }
}


// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let location: CLLocation = locations.last!
    print("Location: \(location)")
    moveMyImageOnMap()
    print(isStartRide, gotCounterFromLastRide , counter)
    if isStartRide && gotCounterFromLastRide{
        updateMyLocationToDataBase()
    }
}

//    func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
//        print(newHeading)
//    }

// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    switch status {
    case .restricted:
        print("Location access was restricted.")
    case .denied:
        print("User denied access to location.")
        // Display the map using the default location.
    case .notDetermined:
        print("Location status not determined.")
    case .authorizedAlways: fallthrough
    case .authorizedWhenInUse:
        print("Location status is OK.")
        if mapView != nil {
            mapView.isMyLocationEnabled = true
        }

    }
}

// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    locationManager.stopUpdatingLocation()
    print("Error: \(error)")

    let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String

    let msg :String = "You have denied the app to access your location. Please enable the location services in your settings for the app to get the location";
    let alertController = UIAlertController(title: "Allow \(appName) to access your location while you are using the app?", message: msg, preferredStyle: .alert)
    let cancelAction = UIAlertAction(title: "CANCEL", style: UIAlertActionStyle.default, handler: nil)

    let settingsAction = UIAlertAction(title: "SETTINGS", style: .default) { (_) -> Void in
        guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
            return
        }

        if UIApplication.shared.canOpenURL(settingsUrl) {
            UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
                print("Settings opened: \(success)") // Prints true
            })
        }
    }


    alertController.addAction(cancelAction)
    alertController.addAction(settingsAction)

    self.present(alertController, animated: true, completion: nil)
}


}
Yogesh Tandel
  • 1,738
  • 1
  • 19
  • 25