Finding out if location services are enabled is not working - ios

I have an app which uses the device location. If they allow the location, I want to run my method getDataFromJson and run my app as normal. If they deny it, or have denied it before, I wish to show them a view explaining they need to go to settings and allow it.
I have a lot of code, but it doesn't work at the moment. Can anyone help explain where the problem is?
Many thanks!
- (void)viewDidLoad
{
[super viewDidLoad];
if ([CLLocationManager authorizationStatus] == YES) {
//are enabled, run the JSON request
[self getDataFromJson];
} else {
//is not enabled, so set it up
NSLog(#"no");
[locationManager location];
};
}
-(CLLocationCoordinate2D) getLocation{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
CLLocationCoordinate2D coordinate = [location coordinate];
return coordinate;
}
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusDenied) {
//location denied, handle accordingly
locationFailView.hidden = NO;
mainView.hidden = YES;
}
else if (status == kCLAuthorizationStatusAuthorized) {
//hooray! begin tracking
[self getDataFromJson];
}
}
//class to convert JSON to NSData
- (IBAction)getDataFromJson {
CLLocationCoordinate2D coordinate = [self getLocation];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
...
}

+ (CLAuthorizationStatus)authorizationStatus
Return Value A value indicating whether the application is authorized
to use location services.
Discussion The authorization status of a given application is managed
by the system and determined by several factors. Applications must be
explicitly authorized to use location services by the user and
location services must themselves currently be enabled for the system.
This authorization takes place automatically when your application
first attempts to use location services.
+ (BOOL)locationServicesEnabled
Returns a Boolean value indicating whether location services are
enabled on the device.
You may check this two state: locationServicesEnabled and authorizationStatus then decide which method should use.
AuthorizationStatus should check with states:
typedef enum {
kCLAuthorizationStatusNotDetermined = 0,
kCLAuthorizationStatusRestricted,
kCLAuthorizationStatusDenied,
kCLAuthorizationStatusAuthorized
} CLAuthorizationStatus;
but you check equal with bool value in viewDidLoad method.

Related

Multiple Local Notifications glitch from didEnterRegion:

I'm Currently monitoring several locations that are backed by core data.
In other words, I have set up a for loop that loops through all of the stored entities in core data and creates a monitored region for all of the entities.
The problem here is that the for loop triggers multiple local notifications when entering one of the regions. The number of notifications almost directly corresponds to the number of monitored regions. So I'm fairly confident this may be whats causing the bug, but I'm not 100 percent sure.
I've noticed that this seems to be a common issue with region monitoring, but I haven't been unable to find an example that incorporates a for loop.
How can I stop multiple notifications being triggered when didEnterRegion gets called?
The method below is called in viewDidLoad. The [DataSource sharedInstance].fetchedResultItems is an array that is populated with the fetchedObjects from a fetched request.
-(void)startMonitoringRegions{
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways ||
authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse) {
self.locationManager.distanceFilter = 10;
[self.locationManager startUpdatingLocation];
for (POI *items in [DataSource sharedInstance].fetchResultItems){
NSString *poiName = items.name;
NSNumber *poiLatitude = items.yCoordinate;
NSLog(#"value: %#", poiLatitude);
NSNumber *poiLongitude = items.xCoordinate;
NSLog(#"value: %#", poiLongitude);
NSString *identifier = poiName;
CLLocationDegrees latitude = [poiLatitude floatValue];
CLLocationDegrees longitude = [poiLongitude floatValue];
CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake(latitude, longitude);
self.regionRadius = 10;
self.region = [[CLCircularRegion alloc] initWithCenter:centerCoordinate radius:400 identifier:identifier];
[self.locationManager startMonitoringForRegion:self.region];
NSLog(#"region: %#", self.region);
NSLog(#"monitored regions %#", self.locationManager.monitoredRegions);
}
}
}
}
Here is the didEnterRegion method
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
NSLog(#"entered region!!");
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
if (localNotification) {
localNotification.fireDate = nil;
localNotification.alertBody = [NSString stringWithFormat:#"You are near %#", self.region.identifier];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
}
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
// [[UIApplication sharedApplication]presentLocalNotificationNow:localNotification];
}
Regions are act as a shared resources. When you enter any region a call will be forwarded to all of the location manager. I think somewhere somehow you are creating multiple location manager objects. That is actually causing the multiple calling of didEnterRegion. The number of time didEnterRegion is called depending upon the number of LocationManager you registered. You should write the code in AppDelegate, in this method
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Place your code here
}
Just a troubleshooting tip. You can use Obj-C equivalent of the following to see what regions are currently being monitored by the app. Perhaps reviewing the identifiers will shed some light on the problem.
for region in locationManager.monitoredRegions {
debugPrint(region.identifier)
}
And for a clean start you can delete all regions with this:
for region in locationManager.monitoredRegions {
locationManager.stopMonitoringForRegion(region)
}

Unable to get longitude and latitude in ios8 by GPS

I am using iOS8 and using the following code for getting longitude and latitude by GPS:
-(void)CurrentLocationIdentifier
{
locationManager = [CLLocationManager new];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0 &&
[CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse)
//[CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedAlways)
{
// Will open an confirm dialog to get user's approval
[locationManager requestWhenInUseAuthorization];
//[_locationManager requestAlwaysAuthorization];
} else {
[locationManager startUpdatingLocation]; //Will update location immediately
}
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
CLLocation *location = [locations lastObject];
NSLog(#"lat%f - lon%f", location.coordinate.latitude, location.coordinate.longitude);
}
- (void)locationManager:(CLLocationManager*)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
case kCLAuthorizationStatusNotDetermined: {
NSLog(#"User still thinking..");
}
case kCLAuthorizationStatusDenied: {
NSLog(#"User hates you");
} break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways: {
[locationManager startUpdatingLocation]; //Will update location immediately
} break;
default:
break;
}
}
After running this code I am getting the output at log is : User still thinking. Can anyone help me with this I am new to iOS and CoreLocation.Framework ?
Please help me in finding the Error and how can I solve that.
Add this string in InfoPlist.strings files
1) NSLocationWhenInUseUsageDescription
2) NSLocationAlwaysUsageDescription
Try this code
locationManagerApp=[[CLLocationManager alloc] init];
locationManagerApp.delegate = self;
locationManagerApp.distanceFilter = kCLDistanceFilterNone;
locationManagerApp.desiredAccuracy = kCLLocationAccuracyHundredMeters;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
[locationManagerApp requestAlwaysAuthorization];
}
[locationManagerApp startUpdatingLocation];
CLLocation *location1 = [locationManagerApp location];
CLLocationCoordinate2D coordinate = [location1 coordinate];
self.latValue= [NSString stringWithFormat:#"%f", coordinate.latitude];
self.longValue = [NSString stringWithFormat:#"%f", coordinate.longitude];
NSLog(#"Latitude = %#", self.latValue);
NSLog(#"Longitude = %#", self.longValue);
And run project in device.
In iOS 8 you need to do two extra things to get location working: Add a key to your Info.plist and request authorization from the location manager asking it to start. There are two Info.plist keys for the new location authorization. One or both of these keys is required. If neither of the keys are there, you can call startUpdatingLocation but the location manager won’t actually start. It won’t send a failure message to the delegate either (since it never started, it can’t fail). It will also fail if you add one or both of the keys but forget to explicitly request authorization.
So the first thing you need to do is to add one or both of the following keys to your Info.plist file:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
Both of these keys take a string which is a description of why you need location services. You can enter a string like “Location is required to find out where you are” which, as in iOS 7, can be localized in the InfoPlist.strings file.

iOS 8 Core Location works everytime on WiFi, intermittent on 4G

So it works when I'm on WiFi. But on 4G, it only works if I had been on Wifi and it already has the location. A lot of times without WiFi, the phone will say it's using my location but its not updating the label nor is it uploading the coordinates to the server. Here's the code:
- (void)viewDidLoad {
[super viewDidLoad];
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([self->locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self->locationManager requestWhenInUseAuthorization];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation *newLocation = [locations lastObject];
NSString *locationLat = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.latitude];
NSString *locationLong = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.longitude];
latLongLabel.text = [NSString stringWithFormat:#"Lat: %# - Long%#",locationLat,locationLong];
[self postLocation:locationLat secondArg:locationLong];
}
- (void)postLocation: (NSString *)latitudeString secondArg:(NSString *)longitudeString {
//POST COORDINATES TO MY SERVER
}
- (IBAction)startUpdating:(id)sender {
[locationManager startUpdatingLocation];
}
My suspicion is that when you are on 4G, the location updates still work fine (although wifi triangulation makes them a bit more precise but only Apple knows how, as the implementation is private), BUT there might be an issue sending those values to the server quickly or reliably enough via 4G connection. (for example in London it is slow as hell with so many people around)
You might narrow the debugging by simply logging the location update directly to some UIlabel on your view, and not going through server infrastructure.
AS a last resort I would make sure
that you set your CLActivityType property to CLActivityTypeFitness
and pausesLocationUpdatesAutomatically is set to NO.

Core Location can't get data in viewDidLoad

I wrote a simple iOS application that retrieves location information and then uses the location to request Yahoo Weather.
The problem is that even when I call the Core Location in the viewDidLoad, it won't give me the result immediately.
So why can't I get the location information?
How can I get the location information in viewDidLoad?
The pseudocode currently is something like:
- (void)viewDidLoad
{
[super viewDidLoad];
self.locManager = [[CLLocationManager alloc] init];
self.locManager.delegate = self;
self.locManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locManager.distanceFilter = 100;
[self.locManager startUpdatingLocation];
//won't get the current location right now, so the output will be null
NSLog(#"Current Location Longitude: %#", self.longitudeString);
NSLog(#"Current Location Latitude: %#", self.latitudeString);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = [locations lastObject];
self.longitudeString = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
self.latitudeString = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
Location updates are not provided as instantly as you are expecting, you need to wait few seconds (2-3 or may be more) to get precise location update. If you want to have location data in viewDidLoad then you should init your location manager (and call startUpdatingLocation) before invoking the ViewController (since then it is not guaranteed that you will have location-data in viewDidLoad).

iOS Waiting for Location Manager to Init

I'm developing an iOS app based on the map and location tracking. When the user first boots the app it asks for permission to track location etc. just fine. The only problem is while it's doing that, I have code setting up the initial map view and other location-based variables before the user has clicked OK.
I've found I can put these initiation steps after a while loop that waits for the user to change the location manager's permissions as below but this can't possibly be best practice, not to mention it leads to some strange behavior in the transition between the splash screen and the map:
BOOL firstrun = TRUE;
while ([[locationManager class] authorizationStatus] == kCLAuthorizationStatusDenied || [[locationManager class] authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
NSLog(#"Waiting for location permission");
}
...initiation code...
Is there a "location access granted" listener for the alert box or a similar function in the location manager delegate I don't know about? I see no such method in the docs. Anyone know what the best practice is here? Thank you so much.
EDIT
I start my location tracking as follows:
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
[locationManager startMonitoringSignificantLocationChanges];
self.musicmap.delegate = self;
[self.mymap setShowsUserLocation:true];
Thanks
I would recommend making your class a CLLocationManagerDelegate and then implementing this method:
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
//Your code goes here
}
More information about CLLocationManagerDelegate can be found here.
Hope that helps!
I had a similar problem with my application and the app doing things before the user has time to accept or decline the location permission dialogue. Here is what I ended up doing.
-(BOOL)locationAuthorizationStatus {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
// user has not authorized us to use location
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Location Denied", #"Location Denied")
message:NSLocalizedString(#"This app does not have permission to access your location. Please enable location access in device settings.", #"Message stating this app does not have permission to access your location and to enable location permission in settings")
delegate:self
cancelButtonTitle:NSLocalizedString(#"Ok", #"Ok")
otherButtonTitles: nil];
[alert show];
return NO;
}
// Check if region monitoring is available for this device
if (![CLLocationManager regionMonitoringAvailable]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Geofencing Unavailable", #"Geofencing Unavailable")
message:NSLocalizedString(#"This device is not able to monitor regions", #"Message stating this device is not able to monitor regions")
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:NSLocalizedString(#"Ok", #"Ok"), nil];
[alert show];
return NO;
} else {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
// trigger a location check to prompt user for authorization
LocationManagerController *locationController = [LocationManagerController sharedManager];
[locationController.locationManager startUpdatingLocation];
// the dialogue box is triggered here
[locationController.locationManager stopUpdatingLocation];
_waitingOnAuthorization = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkForAuthorizationStatusChange) name:#"WaitingOnAuthorizationStatus" object:nil];
return NO;
}
}
return YES;
}
-(void)checkForAuthorizationStatusChange {
if (_waitingOnAuthorization) {
// this should only catch location change on first time
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
// user approved location services
} else {
// user declined authorization
}
// set flag back to NO
_waitingOnAuthorization = NO;
}
// remove our notification observer
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
You will have to add the variables that apply to your use case. But here is the gist of it.
Check for authorization status via BOOL method (locationAuthorizationStatus)
If ok to use location, returns YES do whatever you want
If not, returns NO and alerts user
If first time, will fire up the location manager to trigger location dialogue, then stop it to save battery, sets the flag and sets a notification so you will know when the user has hit yes or no.
The notification fires the method checkAuthorizationChangeStatus and rechecks permissions to see what the user did. From there, you can call any methods you need based on the users choice.
Apple does not have any delegate methods to catch this selection, so the only way around it is kinda of hacky. This method has worked well for me. Slightly hacky, but works. Hope this helps.
//Start up motion manager, not sure if you need this for location manager
motionManager = [[CMMotionManager alloc] init];
if (motionManager.accelerometerAvailable) {
motionManager.accelerometerUpdateInterval = 1.0/2.0;
[motionManager startAccelerometerUpdates];
}
locationManager = [[CLLocationManager alloc] init];
//We will be the location manager delegate
locationManager.delegate = self;
//Track position at the 100m accuracy
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
//We want to see all location updates, regardless of distance change
locationManager.distanceFilter = 0.0;
[locationManager startUpdatingLocation];
Put the above somewhere in your "viewDidLoad" or appDelegate "didFinishLaunchingWithOption"
The following handles the updates and stuff. bestLocation is a CLLocation. You'll need to implement CLLocationManagerDelegate. TWO_MINUTES = 120
- (BOOL)isBetterLocation:(CLLocation *)location {
if (bestLocation == nil){
//best location not set yet, so it's a better location by default
return YES;
}
// Figure out how long it's been since we got a better location
NSTimeInterval timeDelta = [location.timestamp timeIntervalSinceDate:bestLocation.timestamp];
BOOL isSignificantlyNewer = timeDelta > TWO_MINUTES;
BOOL isSignificantlyOlder = timeDelta < -TWO_MINUTES;
BOOL isNewer = timeDelta > 0;
if (isSignificantlyNewer) {
return YES;
}else if (isSignificantlyOlder) {
return NO;
}
CLLocationAccuracy accuracyDelta = location.horizontalAccuracy - bestLocation.horizontalAccuracy;
//You want accuracy to be low
BOOL isLessAccurate = accuracyDelta > 0;
BOOL isMoreAccurate = accuracyDelta < 0;
BOOL isDifferent = location.coordinate.latitude != bestLocation.coordinate.latitude ||
location.coordinate.longitude != bestLocation.coordinate.longitude;
if (isMoreAccurate) {
return YES;
} else if (isNewer && !isLessAccurate && isDifferent) {
return YES;
}
return NO;
}
#pragma mark - Location manager delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if ([self isBetterLocation:newLocation]){
self.bestLocation = newLocation;
} else {
[locationManager stopUpdatingLocation];
isLocating = NO;
NSLog(#"AppDelegate: Turning off location manager >>>>>>>>>>>>>>>>>>>>>");
}
}
Note: You don't need the "isBetterLocation" method. That will just check the location and turn off the location manager after like 2 minutes so you don't wear down the user's battery. Sometimes you don't want the manager off though, so you can leave it out if you want.
I don't know how much of this you've done already. This is the way I did it.

Resources