Search code examples
iosswiftcllocationmanagerenergy

Best way to get LocationUpdates with a good Accuracy without draining down the Battery


I've been trying to implement a LocationView Controller which constantly updates the Users Location to the Server. As i debugged the Application and while in use i noticed, that the SourceCode I provided drained my Phones Battery a lot faster than usual. Do I have to cope with this, or is there any way, without reducing the LocationRequirements, to improve the PowerUsage in the following Code?

class LocationViewController: UIViewController {
  // MARK: - Outlets
  @IBOutlet var mapView: MKMapView!

  fileprivate var locations = [MKPointAnnotation]()
  var updatesEnabled: Bool = false;

  var defaultCentre: NotificationCenter = NotificationCenter.default
  //- NSUserDefaults - LocationServicesControl_KEY to be set to TRUE when user has enabled location services.
  let UDefaults: UserDefaults = UserDefaults.standard;
  let LocationServicesControl_KEY: String = "LocationServices"


  public var lastPosition: Date?;

  public lazy var locationManager: CLLocationManager = {
    let manager = CLLocationManager()
    manager.distanceFilter = 20;
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.delegate = self
    manager.requestAlwaysAuthorization()
    return manager
  }()

  public var routeLine: MKPolyline = MKPolyline() //your line
  public var routeLineView: MKPolylineView = MKPolylineView(); //overlay view
  // MARK: - Actions
  @available(iOS 9.0, *)
  @IBAction func enabledChanged(_ sender: UISwitch) {
    if sender.isOn {
      self.UDefaults.set(true, forKey: self.LocationServicesControl_KEY)
      locationManager.startUpdatingLocation()
      locationManager.allowsBackgroundLocationUpdates = true
      self.updatesEnabled = true;
    } else {
      self.UDefaults.set(false, forKey: self.LocationServicesControl_KEY)
      locationManager.stopUpdatingLocation()
      self.updatesEnabled = false;
      self.timer.invalidate()
      locationManager.allowsBackgroundLocationUpdates = false
    }
  }

  @IBAction func accuracyChanged(_ sender: UISegmentedControl) {
    let accuracyValues = [
      kCLLocationAccuracyBestForNavigation,
      kCLLocationAccuracyBest,
      kCLLocationAccuracyNearestTenMeters,
      kCLLocationAccuracyHundredMeters,
      kCLLocationAccuracyKilometer,
      kCLLocationAccuracyThreeKilometers]
    locationManager.desiredAccuracy = accuracyValues[sender.selectedSegmentIndex];
  }

  // MARK: - Override UIViewController
  override func viewDidLoad() {
    mapView.delegate = self;
  }
}

// MARK: - MKMapViewDelegate
extension LocationViewController: MKMapViewDelegate {
  func addRoute() {
    mapView.remove(self.routeLine);
    var pointsToUse: [CLLocationCoordinate2D] = [];
    for i in locations {
      pointsToUse += [i.coordinate];
    }
    self.routeLine = MKPolyline(coordinates: &pointsToUse, count: pointsToUse.count)
    mapView.add(self.routeLine)
  }
}

// MARK: - CLLocationManagerDelegate
extension LocationViewController: CLLocationManagerDelegate {

  func isUpdateValid (newDate: Date) -> Bool{
    var interval = TimeInterval()
    if(lastPosition==nil){lastPosition=newDate}
    interval = newDate.timeIntervalSince(lastPosition!)
    if ((interval==0)||(interval>=self.UpdatesInterval)){
      return true
    } else {
      return false
    }
  }

  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    guard let mostRecentLocation = locations.last else {
      return
    }
    if(isUpdateValid(newDate: mostRecentLocation.timestamp)){
      let position = MKPointAnnotation();
      position.coordinate=mostRecentLocation.coordinate;
      self.locations.append(position);
      self.addRoute();

      self.locationAPI.postLocation(location: mostRecentLocation)
      lastPosition=mostRecentLocation.timestamp
    }
  }
}

Solution

  • You need to limit the time when you have the GPS "lit" somehow. If you've got it set to give high accuracy GPS readings constantly, it's going to keep the GPS powered up and you're going to use a lot of power. There's nothing to be done about that.

    Possibilities:

    You could wake up the GPS once every 10 minutes, run it until you get a good reading, then turn it back off.

    You get a good reading when your app first starts, then subscribe to significant location changes, and when you get an update, start location updates, get a new fix, and then turn off location updates again.

    I'm not sure how you'd do the first option since Apple doesn't let your app run indefinitely in the background. You'd need your app to be awake and running in the foreground the whole time, which also drains the battery a lot faster than if it is allowed to go to sleep.