I am using significant location change monitoring. When I run the code in the simulator and check Freeway option I get periodic location updates. I grab these updates and save them in NSUserDefaults and than update tableview every 10s, so I can see if I got any updates. If I run the app on a real device, I get zero updates. I kept the phone in my pocket and travelled over 80km, been in 2 cities. Zero updates. Not sure if I messed something up. I am attaching the code. The code is copy paste, feel free to test. Just make sure to use TableViewController in storyboard and set cell id to ID. What am I missing? I am testing on iphone 5.
info.plist:
edit: Found this in apple docs. Should my locationManager
be created differently?
When an app is relaunched because of a location update, the launch options dictionary passed to your application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method contains the UIApplicationLaunchOptionsLocationKey key. The presence of that key signals that new location data is waiting to be delivered to your app. To obtain that data, you must create a new CLLocationManager object and restart the location services that you had running prior to your app’s termination. When you restart those services, the location manager delivers all pending location updates to its delegate.
edit2:
Based on this, the location should be updated at least every 15min. Bug in my code confirmed.
If GPS-level accuracy isn’t critical for your app and you don’t need continuous tracking, you can use the significant-change location service. It’s crucial that you use the significant-change location service correctly, because it wakes the system and your app at least every 15 minutes, even if no location changes have occurred, and it runs continuously until you stop it.
edit3: added this code to AppDelegate didFinishLaunchingWithOptions:
to see if app gets awaken. It does not get awaken-I see no 200 200 entry in table view. Something fishy is going on.
if let options = launchOptions {
print("options")
if (launchOptions![UIApplicationLaunchOptionsLocationKey] != nil){
locationManager.startUpdatingLocation()
self.lat.append(Double(200))
self.lon.append(Double(200))
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}
CODE: //AppDelegate:
import UIKit
import CoreLocation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var lat:[CLLocationDegrees]!
var lon:[CLLocationDegrees]!
var times:[String]!
var distances: [String]!
var window: UIWindow?
var locationManager: CLLocationManager!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
locationManager=CLLocationManager()
locationManager.delegate=self
locationManager.requestAlwaysAuthorization()
let isFirstLaunch = NSUserDefaults.standardUserDefaults().objectForKey("lat")
if isFirstLaunch == nil{
lat = [CLLocationDegrees]()
lon = [CLLocationDegrees]()
times = [String]()
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}else{
lat = NSUserDefaults.standardUserDefaults().arrayForKey("lat") as! [CLLocationDegrees]
lon = NSUserDefaults.standardUserDefaults().arrayForKey("lon") as! [CLLocationDegrees]
times = NSUserDefaults.standardUserDefaults().objectForKey("time") as! [String]
// distances = NSUserDefaults.standardUserDefaults().objectForKey("distance") as! [String]
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("location updated")
self.lat.append(locations[0].coordinate.latitude)
self.lon.append(locations[0].coordinate.longitude)
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
print("Location: \(locations[0].coordinate.latitude) \(locations[0].coordinate.longitude)")
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print("did change AS")
switch status {
case .AuthorizedWhenInUse:
locationManager.startMonitoringSignificantLocationChanges()
case .AuthorizedAlways:
print("to always")
locationManager.startMonitoringSignificantLocationChanges()
if lat.count==0{
self.lat.append((locationManager.location?.coordinate.latitude)!)
self.lon.append((locationManager.location?.coordinate.longitude)!)
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}
// locationManager.startUpdatingLocation()
break
default:
locationManager.stopMonitoringSignificantLocationChanges()
break
}
}
}
// View Controller
import UIKit
class TableViewController: UITableViewController {
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "updateTableView", userInfo: nil, repeats: true)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return appDel.lon.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ID", forIndexPath: indexPath)
cell.textLabel?.text = "\(appDel.lat[indexPath.row]) \(appDel.lon[indexPath.row]) \(appDel.times[indexPath.row])"
cell.textLabel?.font = UIFont.systemFontOfSize(9)
return cell
}
func updateTableView(){
self.tableView.reloadData()
}
}
In general, the issue of iPhone Significant Location not updating arose is due to a poor or unstable network connection, GPS issues or low battery level.
Firstly, make sure if the significant locations option is turned on or not. To do this, head to the iPhone “Settings” > “Privacy” > “Location Services” > “System Services” > “Signification Locations” and toggle it on.
Judging from the current information, iPhone updates the significant locations every 15 minutes.
Significant-Change Location Updates with no location changes Significant-change location updates wake the system and your app once every 15 minutes, at minimum, even if no location changes have occurred.
There are several problems with the code:
Here are the relevant documents:
The first document says a location manager object must be fully configured when iOS launches the app so the location event can be delivered to the app.
Upon relaunch, you must still configure a location manager object and call this method [startMonitoringSignificantLocationChanges] to continue receiving location events.
This is how I usually do it to ensure that the location manager is fully started regardless of if it was launched from the icon or by iOS (sorry I use ObjectiveC).
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"app launching");
bgTask = UIBackgroundTaskInvalid;
locationMgr = [[CLLocationManager alloc] init];
[locationMgr setDelegate:self];
if([locationMgr respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
[locationMgr setAllowsBackgroundLocationUpdates:YES];
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(@"relaunching because of significant location change - restarting SLC");
[locationMgr startMonitoringSignificantLocationChanges];
}
else
{
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(@"launching with authorization to always use location - starting SLC");
[locationMgr startMonitoringSignificantLocationChanges];
}
else
{
NSLog(@"launching with no authorization to always use location - requesting authorization");
if([locationMgr respondsToSelector:@selector(requestAlwaysAuthorization)])
[locationMgr requestAlwaysAuthorization];
}
}
return YES;
}
Notice the call to setAllowsBackgroundLocationUpdates. This is new in iOS9 and must be done everytime the app starts if you want to receive locations updates in the background. (It is like a "I'm not kidding, I know I asked for them in the background modes, but I really do want them").
Apps that want to receive location updates when suspended must include the UIBackgroundModes key (with the location value) in their app’s Info.plist file and set the value of this property to YES.
Finally you can't rely on didChangeAuthorizationStatus being called. If you have the authorization kCLAuthorizationStatusAuthorizedAlways then calling [locationMgr requestAlwaysAuthorization] doesn't result in a change in authorization status, so didChangeAuthorizationStatus isn't called.
If the authorization status is already known when you call the requestWhenInUseAuthorization or requestAlwaysAuthorization method, the location manager does not report the current authorization status to this method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With