locationManagerShouldDisplayHeadingCalibration not called - ios

I have a simple iOS app which uses the heading. However as the accuracy is very bad (10 to 25 degrees), I implemented the locationManagerShouldDisplayHeadingCalibration function. However it never gets called. Here is my code:
...
locmanager = [CLLocationManager new];
[locmanager setDelegate:self];
[locmanager setDesiredAccuracy:kCLLocationAccuracyBest];
[locmanager setHeadingFilter:kCLHeadingFilterNone];
[locmanager setHeadingOrientation:CLDeviceOrientationLandscapeLeft|CLDeviceOrientationLandscapeRight];
if ([CLLocationManager locationServicesEnabled])
{
[locmanager startUpdatingLocation];
[locmanager startUpdatingHeading];
}
...
- (void)locationManager:(CLLocationManager*)manager didUpdateHeading:(CLHeading*)newHeading
{
// This one gets called
}
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
// This one does NOT get called
NSLog ("Here");
return YES;
}
What should I do to get the compass calibration ? Thanks.

Apple Official Documentation:
Core Location may call this method in an effort to calibrate the onboard hardware used to determine heading values. Typically, Core Location calls this method at the following times:
The first time heading updates are ever requested
When Core Location observes a significant change in magnitude or inclination of the observed magnetic field
So it's entirely possible that your calibration if already fine and need not be set. Note that this method will never get called in simulator.
Try using MKMapView with MKUserTrackingMode.FollowWithHeading and see if calibration window appears. Your delegate might still not get called, because MKMapView calls it through it's own private location manager delegate.
You may also try turning off Compass Calibration in Settings -> Location Services-> System Services on your device.

see CLLocationManager requestAlwaysAuthorization method
add required keys to your info.plist file (now required)
NSLocationAlwaysUsageDescription
bla bla why we use GPS.
NSLocationWhenInUseUsageDescription
bla bla why we use GPS.
must be setup on main UI thread !!

Related

CLLocationManager stopUpdatingLocation doesn't stop

Below are some snippets of my code
BOOL checkingForLocation;
.
.
.
- (IBAction)LocationButtonTapped:(id)sender {
[locationManager startUpdatingLocation];
checkingForLocation = YES;
}
.
.
.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
[locationManager stopUpdatingLocation];
// locationManager = nil;
if (checkingForLocation) {
checkingForLocation = NO;
NSLog(#"here");
// fetch data from server using location and present view controllers
}
}
didUpdateLocations is called multiple times because locationManager is continually updating the location per startUpdatingLocation. My app has a bug where the code within if(checkingForLocation) runs multiple times and renders the view multiple times causing undesired results.
1) Why is it that stopUpdatingLocation doesn't stop further calls to didUpdateLocations?
2) Furthermore, if the checkingForLocation has already been set to NO why does the subsequent calls to didUpdateLocations still treat checkingForLocation as YES. Is there a race condition there?
Through searching other SO questions, I found that setting locationManager = nil will stop the extra calls to didUpdateLocations, which it does fix the issue in my case, but no real explanation. It seems that stopUpdatingLocation should take care of that. Any insight is much appreciated.
Thank you,
didUpdateLocations: can be called frequently and at any time because of its async nature and optimisations :
Because it can take several seconds to return an initial location, the
location manager typically delivers the previously cached location
data immediately and then delivers more up-to-date location data as it
becomes available. Therefore it is always a good idea to check the
timestamp of any location object before taking any actions. If both
location services are enabled simultaneously, they deliver events
using the same set of delegate methods.
Actually, if you uncomment locationManager = nil; it should help, but if you want to use your checkingForLocation property, you should make the assignment synchronised. #synchronized(self) is the easiest way in this case.

Latidude and Longitude are incorrect first time app is launched ios

I am using CLLocationManager to get the user's current location. I need the user's current location because I am using the Open Weather Api to display the weather data wherever the user is. My problem is that the first time I open the app the lat and long value are 0, 0. But then if I run the app again it works fine. So only on the first launch of the app incorrect lat and long values are used. I do all my set up in viewDiDLoad, here is the code...
locationManager = [[CLLocationManager alloc] init];
[locationManager requestAlwaysAuthorization];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
[self->locationManager requestWhenInUseAuthorization];
[locationManager startUpdatingLocation];
I don't know what I am doing wrong. Is it something to do with getting the lat and long in the view controller instead of App Delegate?
Here is how I am getting the location values:
locationManager.location.coordinate.latitude
locationManager.location.coordinate.longitude
It's happening because when you launch your app first time Location manager asks for user permission to access current location and when user allows - It takes little time in process of fetching current location and you're accessing coordinates before completion of that process.
When you launch your app again Location manager detects that you already gave the permission so it gives you current location coordinate very quickly.
So the solution is - you've to wait until it completes location update opt. You can put some kinda delay there using dispatch OR can use Location manager delegate method didUpdateUserLocation OR if your UIViewController isn't the initial view controller - in this case you should implement this process in AppDelegate, use your code in didFinishLaunching, so when you'll open your view controller (whatever navigation flow you're using) location details will be there cause in meantime location manager will track your location.
Hope it'll help you!
You should check horizontalAccuracy of the CLLocation object for a negative value and do not use the lat/lon value if that is the case. It is highly likely that when you see the lat/lon values are 0/0, the GPS hasn't secured a location lock.

Location Notification's based on Latest iOS Feature

I would like to know whether the following is implementable ?
I want my iOS app to give users a notification when the reach a particular location as set in the app (even if the app is killed)
You can achieve this with the new User Notification framework introduced in iOS 10.
There have a UNLocationNotificationTrigger which let you specify a CLRegion. A notification will be post when the user’s device enters or leaves the CLRegion. Use the CLRegion object to specify whether to deliver notifications on entry, on exit, or both.
Refer to:
UserNotifications framework:
https://developer.apple.com/reference/usernotifications
UNLocationNotificationTrigger:
https://developer.apple.com/reference/usernotifications/unlocationnotificationtrigger
When the app is killed you can't , but you can still do it in background mode.
First Way:
UNLocationNotificationTrigger notifies user when user enters the specified location or exit the specified location this link provides exact implementation.
Second Way:
monitoring significant changes can do this for you but the result may not be 100%.
call following method from applicationDidEnterBackground and applicationWillTerminate:;
- (void)startSignificantChangeUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startMonitoringSignificantLocationChanges];
}
and when app enters foreground applicationWillEnterForeground: call following method (modify it as per your requirements i.e. accuracy & distance filter)
- (void)startStandardUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
// Set a movement threshold for new events.
locationManager.distanceFilter = 500; // meters
[locationManager startUpdatingLocation];
}
From apple documentation:
The significant-change location service delivers updates only when there has been a significant change in the device’s location, such as 500 meters or more. 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.
For 100% accuracy you can involve server end and send silent notifications from server which will give your application a 30 second window to determine user location and fire a local notification.

didUpdateToLocation called only twice on 3G

the problem is when i turn internet connection from wifi to 3G, location does not updating and did Update To Location is never called.anyone can help me? i have to send updated current location to server through web service after every 5 seconds.web service is calling but every time the same coordinates goes to server.i want to send coordinates of updated location but on 3G the location update method never called when i change location
[mapview animateToLocation:newLocation.coordinate];
Currentmarkers.position = CLLocationCoordinate2DMake(newLocation.coordinate.latitude,newLocation.coordinate.longitude);
deleg.currentCoordinates = [[CLLocation alloc]initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
Following is the code which i used to create location manager :
currentLocation = [[CLLocationManager alloc]init];
currentLocation.distanceFilter = kCLDistanceFilterNone;
currentLocation.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
currentLocation.delegate = self;
[currentLocation startUpdatingLocation];
Switching from WiFi to 3G should not have anything to do with it.
You need to post your code that creates the location manager, configures it, sets you up as the delegate, as well as your location manager delegate methods. If you have the desiredAccuracy or the distanceFilter values set high enough, you won't get notified until you move quite a distance.
Try setting the accuracy to the "best" value, and the distance filter value to kCLDistanceFilterNone for testing.
You may also need to add a scrolling text view to your interface that logs location updates, and then take your device and go for a walk of a kilometer or so. Then you should move var enough to be sure to get location updates.

When should I stop updating location manager?

I have an app that makes a call to get the user's location:
-(void)getLocation{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
//SET USER LOCATION
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
self.userLocation = [locations lastObject];
NSLog(#"location in IntroVC %f, %f", self.userLocation.coordinate.latitude, self.userLocation.coordinate.longitude);
}
My question is, because that NSLog keeps spitting out a new location infinitely, when should I stop calling the location? Well I guess its really up to my app's functionality, but doesnt this cause battery drain? If so, I should really look into the best way of stopping the updates.
Your Distance filter of the location manager is set to be kCLDistanceFilterNone. This causes the didUpdateLocations method to be called infinite time.
locationManager.distanceFilter = kCLDistanceFilterNone;
Change this line as
locationManager.distanceFilter = 10;
and try again. Change the value as needed.
So now the didUpdateLocation will not be called infinite times. :)
Hopefully this helps.
Depending on the nature of your app, you may well want to turn location services off when you go into the background or when the screen is locked. These notifications hooks are provided generally in your app delegate file (.m). Yes you are right, location services significantly drain battery and it a highly recommended practice for iOS apps using location services to use it cautiously.
Apple seems to have thought about this and has provided an API that notifies the app only if the user has moved "significantly" Apple the significant change location service. The definition of "moved significantly" varies on various aspects depending on WiFi availability, cell tower availability, GPS availability etc. Luckily all this is obfuscated within this API.
I call this method after updating the location in didupdateloactions, so when my app goes in background it remove location icon from top status bar.

Resources