CLLocationManager didUpdateLocations is just called twice in background mode - swift

I want to use CLLocationManager for updating the users current GPS Location in foreground and background.
I registered the app for using the location background mode in info.plist and under the targets capabilities.
On the iPhone simulator everything works fine but on my real iPhone the function didUpdateLocations just gets called twice when app is in background.
I use a gpx file which simulates GPS data in Xcode. It works when the app is in foreground, I can see the progress on a MapView.
This is the code from my ViewController:
lazy var locationManager: CLLocationManager! = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.pausesLocationUpdatesAutomatically = false
//manager.distanceFilter = 250
return manager
}()
//Location manager delegate function
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if(UIApplication.sharedApplication().applicationState == .Background){
print("background call")
}else{
print("foreground call")
}
// Save GPS coordinates
saveGpsCoordinates(locations[0])
}
I’m using:
IOS 9 on an iPhone 5 and Xcode 7.0 beta 5
Does someone have an idea why I don’t get any background delegate calls on my real device?
Thanks.

I had a similar issue after moving to iOS9.
As noted in the answer to the question below, make sure you are setting allowsBackgroundLocationUpdates to YES
allowsBackgroundLocationUpdates in CLLocationManager in iOS9

Two things:
I don't seen anywhere in your code a call to startUpdatingLocation.
(Assuming 1 is correct) You said you are using a GPX file to simulate locations, if you are using a GPX file to simulate locations than that is not using the locationManager to get locations and since you did not call startUpdatingLocation, the locationManager is not actually running in the foreground or background.

Make sure you set pausesLocationUpdatesAutomatically to TRUE. Because you get more location updates when this is set to FALSE, even locations which look the same.
TRUE is also the default value.

you should add NSLocationAlwaysUsageDescription in info.plist. Good luck!

Related

How to make my Google Map follow the user at all times and not let them scroll away

I have a GMSMapView connected to my mapView variable. It is all set up and even puts a random polyline on the map for the user. Currently I have a "navigate" button but all it does is zoom in on the user. I would like to make it so once they hit "navigate" it will make the camera follow the user and not let them scroll away from their location (much like the Google Maps app). How can I make this possible? Thanks!
I'm assuming you've already gotten a CLLocationManager and have started updating user's location with it. Once that's done, you want to set the mapView's camera in the CLLocationManager's delegate method whenever the location changes.
Something along the lines of this:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = manager.location {
// Create camera with the user's new location
let camera = GMSCameraPosition.cameraWithLatitude(location.coordinates.latitude, longitude: location.coordinates.longitude, zoom: 17.0)
// Animate the map to the new camera position
mapView.animateToCameraPosition(camera)
}
}
Play around with the zoom parameters to get the result you desire.
Hope it helps.

iOS 7 - didUpdateLocations delegate not called after app goes in background

I'm able to receive location updates on all (simulators and iPad device) but my iPhone device.
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.locationManager.delegate = self;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = locations.lastObject;
NSLog(#"didUpdateLocations newLocation = %#", newLocation);
.
.
}
I receive didUpdateLocations (NSLog message) as long as app is in foreground but after I press Home button (i.e. app goes in background), I stop receiving notifications in didUpdateLocations. The 'console' doesn't show the NSLog message and thereafter even the location icon in status bar goes away within 10 seconds.
What works an what doesn't -
Appears that iOS 6 installed on my other iPhone 3gs works fine. The little location icon doesn't go away.
Test on iPad device with iOS 7 and the app keeps running in background.
All simulators (iPhone and iPad) with iOS 6 as well as iOS 7 works as expected.
When i test it on my iPhone 4 with iOS 7, the app doesn't run in background. So probably, this issue is only on iOS 7 (and even some specific iPhone devices).
I think I found the cause of this problem. The "Settings" app in iOS 7 has General > "Background App Refresh" which was turned OFF :(
As soon as I turned it "ON", my app started running in background :)
The above solution did not work for my particular case in iOS 8.X, therefore, I would like to post the solution that worked for me in hopes that it might help others.
I successfully setup silent Push Notifications which I was receiving in the Foreground, Background, and Suspended states. Once I received the PN I would need to obtain the location updates. I was able to do so while the application was in the Foreground, however, I was not able to do so while the application was in the background.
What I needed to do was add a key to the info.plist file called NSLocationAlwaysUsageDescription and give it a value. The value can be something like "Your location is needed in the background."
After adding the key I then added this code within my AppDelegate.m's application:didFinishLaunchingWithOptions: method -
// Check for iOS 8.X. Without this guard the code will crash with "unknown selector" on iOS 7.
if([[self locationManager] respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[[self locationManager] requestAlwaysAuthorization];
}
Once I added these two pieces I was able to successfully obtain location updates in the background.
Thanks,
audible

startMonitoringSignificantLocationChanges but after some time didUpdateLocations is not called anymore

I have a very strange behaviour. I'm writing an application that uses both fetch and location background mode (don't know it it's important for the problem). I correctly set up the CLLocationManager with a delegate and I start monitoring significant location changes (startMonitoringSignificantLocationChanges) in the AppDelegate.
Immediately I get called didUpdateLocations that gives me some locations. After that no more events are fired even I simulate a different location in Xcode (also using a GPX file) and even I stop and restart the monitoring. I'm not receiving any location both in background and foreground.
The strange thing is that if I start updating locations (using the GPS) it works correctly (doing the same tests... starting and stopping exactly like with startMonitoringSignificantLocationChanges). Obviously I've both didUpdateLocations and didFailWithError (I receive no errors).
This is my initialisation (called by the initialisation of an object in my AppDelegate didFinishLaunchWithOptions):
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
I know that distanceFilter and accuracy are not important for significant location changes and I've already set pausesLocationUpdatesAutomatically to NO.
Am I missing something? And also does exist a way to read if a location fired is coming from wifi, cellular or gps?
Thanks in advance.
You can simulate location for significant change on simulator only with "freeway drive" option from simulator location menu. It will be triggered with some minutes interval.
Allocate the manager on the main thread and check if the manager or the app has always permissions could do the trick.
Apple's documentation note is:
Note: Significant-change location updates require an authorization
status of kCLAuthorizationStatusAuthorizedAlways.
You can check the status using:
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
And maybe
if (status != kCLAuthorizationStatusAuthorizedAlways)
You can request permissions again.

iOS 6 and Mapkit : User location

I was suprise when i saw that my app can't know where I am located with a MapKit on iOS6 !
I just want to zoom to user's location when the map is starting ! How can I do it ?
Please help me
Thank you so much !!
(PS: I saw that the CLCoordinate for sending a request for routing is working fine)
Do you mean that showsUserLocation doesn't have any effect? Or does your location monitoring isn't updating you position? I tested both in iOS 6, works for me. Maybe you could post some code.
EDIT:
Seems like location monitoring is not activated in your app.
First of all you have to do sth like this to enable the cllocation manager:
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
//set the delegate for the location manager
locationManager.delegate = self;
// set your desired accuracy
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
[locationManager startUpdatingLocation];
At that point the blue location indicator should be visible. As long as you've set showsUserLocation = YES on your map.

HowTo initialise MKMapView with a given user location?

My app knows the current user position (CoreLocation.framework).
As soon as the user opens a new MapView his iPhone starts searching for the current position again.
Is it possible to skip that or to change the first user position for mkMapView?
Edit:
Is it possible to overwrite MKMapView and use an other LocationManager?
Yes, it is possible to have a separate location manager object and assign its value to the mapview (BTW, I'm using '=' below as list prefix to prevent the SO code-formatter from borking).
= In your UIViewController maintain two separate properties: one to a MKMapView and one to a CLLocationManager.
= Create a XIB file with the MKMapView and any other window chrome you want. Connect the outlets to the controller propeties. Make sure MKMapView does NOT follow user location.
= Have the UIViewController implement the CLLocationManagerDelegate protocol--especially the locationManager:didUpdateToLocation:fromLocation: method which will be called whenever a new location value is available. We'll be setting the controller as the delegate for the location manager.
= In the viewController's loadView method, load the NIB with the MKMapView in it. To give user feedback you may want to put up a UIActivityIndicatorView spinner and set it to startAnimating. Then you start with:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
self.locationManager.distanceFilter = 10; // or whatever
[self.locationManager startUpdatingLocation];
= In locationManager:didUpdateToLocation:fromLocation: check to see if the event was updated since the last N seconds. Then tell the location manager to stop updating, the spinner to stop animating, and get the lat/long data and assign it to the map view along with a view span and region so it zooms and centers to the right place.
= Now here's the tricky part: the blue marble 'throbber' is a feature of the mapview tracking user location. You'll have to momentarily 'fake it' until the real one kicks in (or just use a separate marker for the current location and maintain its position yourself). Personally I'd go with the blue marble that the user is familiar with.
= To make it so it shows right at startup you will need to create a custom MKAnnotationView with just the blue marble graphic added at the location returned by the location manager. This means taking a snapshot of a Mapview with the location showing, then photoshopping just the blue marble out and using it as the image for a custom annotation view.
= If you want it to actively follow the map, you can enable the userlocation tracking of the mapview and when it gets the actual data, you hide your previously set marker and let the mapview do the updating. The other option is to allow the existing location manager to continue receiving updates every second or so and update the position of the blue marble annotation yourself.
= To let the mapview's own userLocation do the updating add to viewDidLoad:
[self.map.userLocation addObserver:self
forKeyPath:#"location"
options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld)
context:NULL];
self.map.showsUserLocation = YES; // starts updating user location
= Implement observeValueForKeyPath. It gets called when the location attribute of the mapview's userlocation has a value:
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([self.map isUserLocationVisible]) {
[self.locationManager stopUpdatingLocation];
self.ownBlueMarble.hidden = YES;
}
// The current location is in self.map.userLocation.coordinate
}
= To avoid the warm-up delay in showing current location, keep a reference to the viewController containing the map and the location manager so it doesn't go away (it's a bit of a memory hog but if you release it you'll have to wait again until MapView loads the tiles and is ready to go).
= In viewWillLoad you can stuff the last known location into the custom bluemarble annotation and show it. Toggle on/off the userLocation tracking and when you get the notification the same hide-the-annotation-show-the-real-marble trick will work. The mapview's own location manager kicks in and when it has the data, you can make your annotation marker disappear.
= You might want to implement the viewController's viewWillDisappear method and manually turn off userLocation tracking on the mapview so it's off by default the next time the view is brought up. You'll also want to get the last known userLocation and save it for the next get-go. That way, you can do all the positioning marker-juggling in the viewWillAppear method and not have to worry about userLocation interfering until you're ready for it.
Good luck.
In your controller:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CLLocationCoordinate2D coord = {latitude: 37.423617, longitude: -122.220154};
MKCoordinateSpan span = {latitudeDelta: 1, longitudeDelta: 1};
MKCoordinateRegion region = {coord, span};
[mapView setRegion:region];
}
When map appears it's going to be centered close to Palo Alto
Try setting the showUserLocation property to false on initialisation and then restore the region the map view previously had (you will obviously have to store this before the previous map is destroyed).
Is this what you wanted?
You don't need to use another Location Manager, you can add whatever points you want to the map and update them via whatever other logic you want.
Let's say you had a socket connection to a remote control car, and that car send back socket data containing geo-location information in the payload. You could parse that out, and update the location of the thumbnail on your map in real time. The "userLocation" property is not needed for that, but you could show it if you wanted to.
The location of the user is read-only, you can use it or not. That doesn't mean you need another location manager to do anything you might need to do. It sounds like your app doesn't really need that feature, but I could be misunderstanding your question.