I\'m looking for a way to get a background location update every n minutes in my iOS application. I\'m using iOS 4.3 and the solution should work for non-jailbroken iPhones
Here is what I use:
import Foundation
import CoreLocation
import UIKit
class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {
static let instance = BackgroundLocationManager()
static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds
static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server
let locationManager = CLLocationManager()
var timer:NSTimer?
var currentBgTaskId : UIBackgroundTaskIdentifier?
var lastLocationDate : NSDate = NSDate()
private override init(){
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
locationManager.activityType = .Other;
locationManager.distanceFilter = kCLDistanceFilterNone;
if #available(iOS 9, *){
locationManager.allowsBackgroundLocationUpdates = true
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil)
}
func applicationEnterBackground(){
FileLogger.log("applicationEnterBackground")
start()
}
func start(){
if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
if #available(iOS 9, *){
locationManager.requestLocation()
} else {
locationManager.startUpdatingLocation()
}
} else {
locationManager.requestAlwaysAuthorization()
}
}
func restart (){
timer?.invalidate()
timer = nil
start()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case CLAuthorizationStatus.Restricted:
//log("Restricted Access to location")
case CLAuthorizationStatus.Denied:
//log("User denied access to location")
case CLAuthorizationStatus.NotDetermined:
//log("Status not determined")
default:
//log("startUpdatintLocation")
if #available(iOS 9, *){
locationManager.requestLocation()
} else {
locationManager.startUpdatingLocation()
}
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if(timer==nil){
// The locations array is sorted in chronologically ascending order, so the
// last element is the most recent
guard let location = locations.last else {return}
beginNewBackgroundTask()
locationManager.stopUpdatingLocation()
let now = NSDate()
if(isItTime(now)){
//TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
}
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
CrashReporter.recordError(error)
beginNewBackgroundTask()
locationManager.stopUpdatingLocation()
}
func isItTime(now:NSDate) -> Bool {
let timePast = now.timeIntervalSinceDate(lastLocationDate)
let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
return intervalExceeded;
}
func sendLocationToServer(location:CLLocation, now:NSDate){
//TODO
}
func beginNewBackgroundTask(){
var previousTaskId = currentBgTaskId;
currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
FileLogger.log("task expired: ")
})
if let taskId = previousTaskId{
UIApplication.sharedApplication().endBackgroundTask(taskId)
previousTaskId = UIBackgroundTaskInvalid
}
timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
}
}
I start the tracking in AppDelegate like that:
BackgroundLocationManager.instance.start()