I'm developing an iOS application that users can save their traveling route to server (by posting their locations through API). The issue I am struggling with is several users reported that the route they saved is interrupted in the middle of the trip. In detail, when users review their saved route on map, there is a part of route being just a straight line because somehow locations of that route section are not sent to server or just because the device can not receive locations at that time.
The weird thing is that the rest of route was recorded normally so seems like the location service stopped working for a period of time but after that it started again so my app could record it fine.
And the most frustrating thing is that I can not reproduce this issue.
Here are circumstances of the issue that user reported:
- User started the app then locked device screen and put it in their pocket, they did not touch it in the whole journey. No battery drain or crash happened.
- After driving about 8-9km and everything worked fine, route recording was interrupted in the next ~ 65km, then well-recorded again in the rest ~ 80km.
Below is my project setup:
- Background Modes in ON in Capabilities with Location updates.
- Locations received from location service are filtered based on timestamp and accuracy and saved to core data with a “isSent” flag marking if a location is sent successfully to server. This way my app can cover the case when network connection is down.
- Locations marked with false “isSent” flag will be sent to server every 30 seconds.
My LocationManager code:
class LocationManager: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager = {
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
_locationManager.activityType = .automotiveNavigation
_locationManager.allowsBackgroundLocationUpdates = true
_locationManager.pausesLocationUpdatesAutomatically = false
return _locationManager
}()
func startLocationService() {
locationManager.startUpdatingLocation()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var positionsToSavedToDb: [DbPositionModel] = []
for location in locations {
let howRecent = location.timestamp.timeIntervalSinceNow
guard abs(location.timestamp.timeIntervalSinceNow) < 10 && location.horizontalAccuracy > 0 && location.horizontalAccuracy < 33 else {
continue
}
if self.locations.count > 0 {
let distanceSinceLastLocation = location.distance(from: self.locations.last!)
let timeSinceLastLocation = location.timestamp.timeIntervalSince(self.locations.last!.timestamp)
if (distanceSinceLastLocation < 5 && abs(timeSinceLastLocation) < 10) {
continue
}
}
// Create DbPositionModel from location and append to positionsToSavedToDb
}
// Save positionsToSavedToDb to core data
}
#objc func each30Seconds(_ timer: Timer) {
// Select in database DbPositionModel objects with false “isSent” flag to send to server
}
}
Can you guys help me find out black holes in my code or anything I can do to reproduce / fix this issue? Thanks a lot!!!
Your setup looks fine to me. Just one question. When you say it didn't work for 60km and then it started working for 80km, was that all while in background? I mean the user didn't need to enter foreground for it to start working again did they?
Your limit for location.horizontalAccuracy is 33. You're thinking that it's going to be very accurate. I'm not sure, maybe the device/city are a bad combination, and then you're returning early. I suggest that you log the reason why you exit early. Their city might be different from yours. Also I've heard that the GPS of the iPhoneX even though has the correct location, it returns a high number for its horizontalAccuracy. Is this happening mostly for iPhoneX users?
enum LocationAccuracyError: Error {
case stale(secondsOld: Double)
case invalid
case lowAccuracy(metersOff: Double)
}
extension LocationAccuracyError: LocalizedError{
var errorDescription: String? {
switch self {
case .stale(let seconds):
return NSLocalizedString("location was stale by: \(seconds) seconds", comment: "")
case .invalid:
return NSLocalizedString("location was invalid)", comment: "")
case .lowAccuracy(let metersOff):
return NSLocalizedString("location's horizontal Accuracy was off by likely more than: \(metersOff) meters" , comment: "")
}
}
}
And then have a function like this to check each location.
private func checkLocationAccuracy(from location: CLLocation) throws {
let ageOfLocation = -location.timestamp.timeIntervalSinceNow
if ageOfLocation >= maximumAcceptedStale {
throw LocationAccuracyError.stale(secondsOld: ageOfLocation)
}
if location.horizontalAccuracy <= 0 {
throw LocationAccuracyError.invalid
}
if location.horizontalAccuracy > MaximumAcceptedHorizontalAccuracy{
throw LocationAccuracyError.lowAccuracy(metersOff: location.horizontalAccuracy)
}
}
Your end usage would be like:
do {
try checkLocationAccuracy(from: location)
} catch let error {
writelog("Bad Location: \(error.localizedDescription)")
}
I'd also add logs around your app state as well e.g. add a log to capture didEnterBackground
Related
I have a tracking function but it doesn't update location while in background.
1st case: Tracking while app is in the foreground -> the tracking is actually happening but doesn't get precise coordinates. I will change to locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation to see if improves accuracy of the tracking.
2nd case: Tracking while screen is off -> the tracking is a straight line from a to b, tracking doesn't update coordinates.
3rd case: Tracking while app is in back ground(pressed home button) -> tracking is happening as case 1.
I found a post that explains that if authorisation is set to always you have to specify you want to keep updating location while in background, but nothing has changed. This is the code and info.plist :
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
// locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
// locationManager.allowsBackgroundLocationUpdates = true //for getting user location in background mode as well
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow //map following user
configureLocationServices()
addDoubleTap() // enabling duble tap gesture recognizer
// mapView.isUserInteractionEnabled = true
let location = locationManager.location?.coordinate
let region = MKCoordinateRegionMakeWithDistance(location!, 1000, 1000) // set mapView based on user location coordinates
mapView.setRegion(region, animated: true)
centerMapOnLocation()
// alerts coordinates to post to Firebase
let alertDrawLatitude = alertDrawCoordinates?.latitude // not used ?
let alertDrawLomgitude = alertDrawCoordinates?.longitude
let title: String? = alertNotificationType
var subtitle: String? = alertNotificationType
// user alert notification. takes coordinates from alertNotificationArray( populated with firebase returning coordinate for all alerts
displayAlerts()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let mostRecentLocation = locations.last else { return }
self.actualRouteInUseCoordinatesArray.append(mostRecentLocation.coordinate)
}
func configureLocationServices() {
if authorizationStatus == .notDetermined{
locationManager.requestAlwaysAuthorization()
} else if authorizationStatus == .authorizedAlways {
locationManager.showsBackgroundLocationIndicator = true //set update location even if in background. very imposrtant!!
}
}
UPDATE:
changing the accuracy only made things worse.
with AccuracyBest:
and with AccuracyBestForNAvigation
second tracking is actually worse.. how can navigation apps rely on this kind of tracking? is there anything wrong with my code for LocationManager?
SECOND UPDATE:
it now get updated location when in background, but is way off..I never passed the yellow street and it shows like I waked for 10 minutes after it..
THIRD EDIT:
I found out that I should filter out GPS raw data, so I'm using a Kalman filter, and it really smooths out the resulting tracking.
So I'm fine tuning two parameters, and in order to be able to change those parameters I added two textfields #IBOutlet weak var filterValueTextField: UITextField! and #IBOutlet weak var horizontalAccuracyTextField: UITextField!and connected those to the parameters
hcKalmanFilter?.rValue = Double(String( describing:filterValueTextField?.text!))! and guard mostRecentLocation.horizontalAccuracy < Double(String( describing: horizontalAccuracyTextField?.text!))! else { return }.
My problem is now that it finds nil while unwrapping value in the horizontalAccuracy parameter.
If in horizontalAccuracy I just put a value it accepts an integer, but when I take it from the texField converting the textfield.text to Int, compiler throws an error Binary operator '<' cannot be applied to operands of type 'CLLocationAccuracy' (aka 'Double') and 'Int', while if I convert it to Double doesn't, but it finds nil.
Why the filterValue finds a value from it's textField, and the horizontal Accuracy doesn't? they're declared, and use the same way.
Any idea?
First of all you have limited time while your app goes to background, and that time is depend upon load on your device's OS, but most probably it is approx. 30 seconds. So this is the reason your are not getting location updates while your screen is off or while your app goes to background.
But Apple allows app to run in background for some tasks and location update is one of them, so you can fetch location updates even if your app goes to background by enabling Background Fetch capability for your app.
For more details please follow below official doc. of Apple:
https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/handling_location_events_in_the_background
And secondly try to maintain your locationmanager object in global scope of your app like you can place it in AppDelegate or in Singleton class if you are maintaining any for your app, so it will always be available.
Sometimes location that you receive does not have desired accuracy, especially when you've just started tracking, first couple of locations are going to be well off. You can use location's horizontal accuracy property to filter location witch have, for example, less then 50m accuracy
I’m coding a project in where I need to know the user’s exact position. (LocationManager)
The problem is that for example underground (metro) due to the weak signal the LocationManager can not determine the user’s location, there for it gives back cached location only..
What I do now is that I check whether the location is too old, if so then wait for a new one..
Okay but there’s a problem.. if user doesn’t move at all then location won’t get updated aswell because it will be only a cached location.. the timestamp check will say its too old..
How can I fix that?
You can check cache locations by timeStamp you can write in LocationManagerDidUpdateLocation
e.g
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let newLocation = locations.first {
let age: TimeInterval = -newLocation.timestamp.timeIntervalSinceNow
if age > 120 {
return
}
// ignore old (cached) updates
if newLocation.horizontalAccuracy < 0 {
return
}
}
}
Although it works normal when app is active, it crashes when app is terminated and wakes for location update
My code to handle app wakes up for location update on didFinishLaunchingWithOptions
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var glat : String = ""
var glong : String = ""
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsLocationKey] != nil {
let locationManager = CLLocationManager() //or without this line, both crashes
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
let status = CLLocationManager.authorizationStatus()
if (status == CLAuthorizationStatus.AuthorizedAlways) {
locationManager.startMonitoringSignificantLocationChanges()
}
}
return true
}
Here is the locationmanager delegate on AppDelegate
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lat = manager.location?.coordinate.latitude,
let long = manager.location?.coordinate.longitude {
print(glat + " " + glong)
glat = String(lat)
glong = String(long)
//Line 339
updateloc(String(lat), long: String(long))
}
}
Function to send location info to server
func updateloc(lat : String, long : String) {
let session = NSURLSession.sharedSession()
//Line 354
let request = NSMutableURLRequest(URL: NSURL(string: "URLTO/updateloc.php")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let data = "lat=\(lat)&long=\(long)"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
let res = response as! NSHTTPURLResponse
dispatch_async(dispatch_get_main_queue(), {
if (res.statusCode >= 200 && res.statusCode < 300)
{
do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
var success = 0
if let dictJSON = resultJSON as? [String:AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
if success == 1
{
print("ok")
}
} else {
print("no 'success' key in the dictionary, or 'success' was not compatible with Int")
}
} else {
print("unknown JSON problem")
}
} catch _{
print("Received not-well-formatted JSON")
}
}
})
}
})
task.resume()
}
Here is the crash log
Crashed: com.apple.main-thread
0 0x10015d998 specialized AppDelegate.updateloc(String, long : String) -> () (AppDelegate.swift:354)
1 0x10015ddf8 specialized AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift:339)
2 0x100159d0c #objc AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift)
3 CoreLocation 0x1893d08b8 (null) + 21836
4 CoreLocation 0x1893ccaac (null) + 5952
5 CoreLocation 0x1893c6e48 (null) + 880
6 CoreFoundation 0x18262cf84 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
7 CoreFoundation 0x18262c8bc __CFRunLoopDoBlocks + 308
8 CoreFoundation 0x18262ad04 __CFRunLoopRun + 1960
9 CoreFoundation 0x182554c50 CFRunLoopRunSpecific + 384
10 GraphicsServices 0x183e3c088 GSEventRunModal + 180
11 UIKit 0x18783e088 UIApplicationMain + 204
12 0x10015a324 main (AppDelegate.swift:20)
App crashes when app wakes for a location update in startMonitoringSignificantLocationChanges mode
I really can't see any mistake here. Anyone can help me to fix it ?
As a starting point, I would suggest moving your location manager functionality to a separate class, and having your location class subscribe to the UIApplicationDidFinishLaunchingNotification notification to handle what happens when the app is restarted.
A way to do this would be to have your class a Singleton and have it relay the location updates through the NSNotificationCenter.
startMonitoringSignificantLocationChanges will wake up your app in the background if its enabled when the app gets terminated by the OS.
Depending on what you want to achieve, you could subscribe to two different events and start stop the relevant location services base on the app delegate events.
As a broad (shot in the dark) example:
class LocationCommander {
let locationManager = CLLocationManager()
let defaultCenter = NSNotificationCenter.defaultCenter()
//- NSUserDefaults - LocationServicesControl_KEY to be set to TRUE when user has enabled location services.
let UserDefaults = NSUserDefaults.standardUserDefaults()
let LocationServicesControl_KEY = "LocationServices"
init(){
defaultCenter.addObserver(self, selector: #selector(self.appWillTerminate), name: UIApplicationWillTerminateNotification, object: nil)
defaultCenter.addObserver(self, selector: #selector(self.appIsRelaunched), name: UIApplicationDidFinishLaunchingNotification, object: nil)
}
func appIsRelaunched (notification: NSNotification) {
//- Stops Significant Location Changes services when app is relaunched
self.locationManager.stopMonitoringSignificantLocationChanges()
let ServicesEnabled = self.UserDefaults.boolForKey(self.LocationServicesControl_KEY)
//- Re-Starts Standard Location Services if they have been enabled by the user
if (ServicesEnabled) {
self.updateLocation()
}
}
func appWillTerminate (notification: NSNotification){
let ServicesEnabled = self.UserDefaults.boolForKey(self.LocationServicesControl_KEY)
//- Stops Standard Location Services if they have been enabled
if ServicesEnabled {
self.locationManager.stopUpdatingLocation()
//- Start Significant Location Changes to restart the app
self.locationManager.startMonitoringSignificantLocationChanges()
}
NSUserDefaults.standardUserDefaults().synchronize()
}
func updateLocation () {
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized){
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.startUpdatingLocation()
//- Save Location Services ENABLED to NSUserDefaults
self.UserDefaults.setBool(true, forKey: self.LocationServicesControl_KEY)
} else {
//- Unauthorized, requests permissions
}
}
}
Please keep in mind that this code has not been tested, I've extracted segments of an old project which might have some syntax errors due to the updates to the language.
Overall we are doing this:
Subscribe to relevant notifications
When app is about to terminate, check if we are getting location updates, if we are, stop the standard service and start the significant changes service to wake up the app when a new update is available.
When the app wakes up, it will notify the class which will decide if we continue to track notification updates or not (Based on a value stored on NSUserDefaults).
Your app might be crashing due to instantiating a new location manager which then tries to restart location services that might already be running.
I hope this helps!
The documentation says
For services that relaunch the app, the system adds the UIApplicationLaunchOptionsLocationKey key to the options dictionary passed to the app delegate at launch time. When this key is present, you should restart your app’s location services right away. The options dictionary does not include information about the location event itself. You must configure a new location manager object and delegate and start your location services again to receive any pending events.
Furthermore it says:
Calling this method [means startMonitoringSignificantLocationChanges] several times in succession does not automatically result in new events being generated. Calling stopMonitoringSignificantLocationChanges in between, however, does cause a new initial event to be sent the next time you call this method.
This implies more than might be clear on first glance. First, the fact that a location update triggered your app's relaunch doesn't mean you immediately get it via the delegate's locationManager:didUpdateLocations: method. Second, if you are in background (and thus not relaunched, but just became active!), things are yet different again. In fact, a common problem I've seen people encounter is that they don't know whether they're in background (inactive) or terminated/just relaunched. You can't even easily test/see this, because an app that was terminated by the system still shows up when pressing the home button twice. Check the list shown under Attach to Process... in XCode's Debug menu to see whether your app is still running (and in background) or not (btw, terminating an app via the Stop button in XCode results in this inconsistency if you wanna play around with the various ways you can become active or relaunched). I just mention this here because a lot of questions ask "How come X happens when my app gets active again?" and the people answering assume different things than what the asking person meant by that.
Anyways, if your App was in fact relaunched, your locationManager variable contains a brand new instance now, and you should get an update right after calling startMonitoringSignificantLocationChanges. I'll get to the problem you might still encounter then below.
If your App just became active again, you should call stopMonitoringSignificantLocationChanges, then startMonitoringSignificantLocationChanges again to get an update immediately (in short: always stop then start to properly restart, it doesn't hurt even if you weren't running already).
Now to the problem I think that results (in part) from this mess with Apple's way of relaunching after location updates. I'm not entirely sure what's happening without playing around with your code, but I hope this is of some help:
In your application:didFinishLaunchingWithOptions: method you set a local constant with let locationManager = CLLocationManager(). This one then is restarted and triggers location updates. However, it might get deallocated before the delegate's locationManager:didUpdateLocations: method is called, after all, the callback happens asynchronously (and application:didFinishLaunchingWithOptions: might return before that is called). I have no idea how or why this even works and gets as far as you say it does, maybe it's luck, or it has to do with how CLLocationManager internally works. Anyways, what I think then happens is that in locationManager:didUpdateLocations:, you don't get meaningful location data anymore, because the CLLocationManager instance responsible for the call is not valid anymore. lat and long are empty or even nil and your request's URL (which you haven't shown) is invalid.
I would suggest to first:
Ensure that your class's constant locationManager is properly set in application:didFinishLaunchingWithOptions: (right after you checked the presence of the UIApplicationLaunchOptionsLocationKey flag). Get rid of the local constant of the same name.
Stop and then restart the location updates. This should call your delegate's locationManager:didUpdateLocations: method immediately with the latest location data (the one that triggered your app's relaunch or becoming active again).
Ensure you get meaningful data in your locationManager:didUpdateLocations: method. Don't call updateloc if that's not the case (you might also want to move the line print(glat + " " + glong) to after you set the class's variables to the new values, so you see on the console whether what you get makes sense).
There"s one small thing I can't tell on the top of my head: If your app is just getting active again (and not relaunched) I'm not sure whether the UIApplicationLaunchOptionsLocationKey is even set at all. You might wanna investigate that one, too. If becoming active isn't a "relaunch" in that sense, though, your locationManager should still be happy an working anyways, at least that's what I get from the documentation (and it's been a while since I've tried that myself). Let me/us know if you're still having problems, I'm curious how it'll turn out. :)
Try this
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_DEFAULT.rawValue), 0)) {
updateloc(String(lat), long: String(long))
}
In an iPhone app that I'm building, GPS accuracy is a user-controlled value that can be set to either KCLLocationAccuracyNearestTenMeters or KCLLocationAccuracyHundred Meters. The code works fine to capture a track, but when monitoring the app in the Location Energy Impact Instrument I’m seeing some unexpected behavior. When accuracy is set to nearest 10 meters the app works as expected, however, if it’s set to 100 meters the setting remains OK for between a half and two seconds, as shown by the Instrument, but then it switches to KCLLocationAccuracy Best. This setting isn't an option in the app, or even a String anywhere in the code.
One twist here, I am running this as Swift 3.0 using Xcode 8, but it's working as it did before and the new platform doesn't seem to be an issue (and shouldn't have this kind of impact). It does mean that some API's have changed and may look different (and to my taste, better) below.
Obviously, this resetting has a very counterproductive impact on energy consumption, which shows in the Instrument which goes from Low to High energy usage at the point where it switches. I can't identify any point where the app would be doing this unintentionally - in fact, during this one-second period it should only be appending points returned by didUpdateLocations to a pending buffer for later processing, as shown below.
In a shared constants declaration:
let defaultTrackingAccuracy = kCLLocationAccuracyNearestTenMeters
let alternateTrackingAccuracy = kCLLocationAccuracyHundredMeters
var trackingAccuracy = kCLLocationAccuracyNearestTenMeters
var waypointInterval = 100
In a settings ViewController (can also reset waypointInterval but did not):
#IBAction func accuracySwitched(_ sender: UISwitch) {
if accuracySwitch.isOn {
Set.shared.trackingAccuracy = Set.shared.defaultTrackingAccuracy
} else {
Set.shared.trackingAccuracy = Set.shared.alternateTrackingAccuracy
}
}
LocationManager instantiation in a Model portion of code:
lazy var locationManager: CLLocationManager = {
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = Set.shared.trackingAccuracy
_locationManager.allowsBackgroundLocationUpdates = true
_locationManager.distanceFilter = Double(Set.shared.waypointInterval)
return _locationManager
}()
Starting the location manager updates in the Model:
func setupLocationManager()
{
if CLLocationManager.authorizationStatus() != .authorizedAlways {
locationManager.requestAlwaysAuthorization()
if CLLocationManager.authorizationStatus() != .authorizedAlways {
delegate?.displayNotice("Unable to Capture Track", alertMessage: "This device requires authorization to use location services in order to capture a track. \n\nPress Continue to return to the Track List.", buttonText: "Continue")
}
}
if !CLLocationManager.locationServicesEnabled() {
delegate?.abortTrackCapture(.gpsUnavailable)
}
locationManager.startUpdatingLocation()
}
Processing points returned by the location manager instance, in the same Model portion:
#objc(locationManager:didUpdateLocations:) func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
assert(locations.first != nil, "didUpdateLocations called with empty array.")
if recordStatus == .record {
pendingPointsBuffer = pendingPointsBuffer + locations
}
lastPosition.latitude = locations.last!.coordinate.latitude
lastPosition.longitude = locations.last!.coordinate.longitude
lastPosition.elevation = locations.last!.altitude
}
Every few seconds, there is a routine that takes points from the buffer and posts them into a CoreData database, but that only accesses the buffer - it has no interaction with the location manager (so it can sleep through periods when the app is in background while the code above keeps loading points into the buffer).
The user interface is in a separate ViewController module, but shouldn’t be doing anything during the two seconds in question.
I'm thinking that there may be some OS based setting or process that could be doing this, but I haven't found anything that seems to do that.
Thanks for your input - any thoughts are appreciated.
I am creating an app which tracks user. When user login to app I have started significant Location updates. Then i put my application in background. and then started traveling with my car. After 500m-1km I got significant location change. On getting update I have stopped significant location updates and started standard GPS. it gives m location updates for 2-3 min after that i see this "Location icon should now be in state 'Inactive'" and my app does not get any location updates.
If app is foregrounded then again app starts getting location updates.
I am using shared location instance. I have iPhone 5 and iOS 8.1.3. I am using Swift.
Adding some code.. I have done like this
gpsManager = CLLocationManager()
gpsManager.delegate = self
gpsManager.desiredAccuracy = kaccuracy
gpsManager.distanceFilter = kdistanceFilter
gpsManager.pausesLocationUpdatesAutomatically = false
gpsManager.headingFilter = kheadingFilter
gpsManager.activityType = kactivityType
isTracking = false
isMonitoringSignificantChanges = false
//on login
var sharedGPSManager=GeoLocation.sharedInstance
sharedGPSManager.delegate=self
let status=sharedGPSManager.authorizationStatus()
if(status == 0)
{
NSLog("Allowed to track")
}
else
{
NSLog("Not allowed")
}
sharedGPSManager.startSignificantLocationTracking()
func gpsManager(manager:GeoLocation, didUpdateLocations locations:AnyObject)
{
var sharedGPSManager=GeoLocation.sharedInstance
sharedGPSManager.stopSignificantLocationTracking()
task=UIApplication.sharedApplication().beginBackgroundTaskWithName("wifiUpload", expirationHandler: { () -> Void in
}
sharedGPSManager.delegate = self
let status = sharedGPSManager.authorizationStatus()
if(status == 0)
{
NSLog("Allowed to track")
}
else
{
NSLog("Not allowed")
}
sharedGPSManager.startTracking()
//process location data
endBackgroundTask(task)
}
// Start location tracking
func startTracking(){
if(isMonitoringSignificantChanges){
gpsManager.stopMonitoringSignificantLocationChanges()
}
gpsManager.stopUpdatingLocation()
if CLLocationManager.headingAvailable()
{
gpsManager.startUpdatingHeading()
}
gpsManager.startUpdatingLocation()
isTracking=true
isMonitoringSignificantChanges=false
}
Finally I got the Answer on Apple developer forum.keeping significant Location always on worked for me....
You need to add Location Update capability in background mode
Project Target > Capabilities > Turn Background mode ON > Tick all which is applicable for your app
You have to enable Required background modes in info.plist as "App registers for location updates" then your application will give you location updates in background.
And you have to use self.locationManager.startUpdatingLocation() not the significant Location updates.