Skip to content

Instantly share code, notes, and snippets.

@benbahrenburg
Last active January 16, 2023 09:50
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save benbahrenburg/c4c992c8c61d197510ea to your computer and use it in GitHub Desktop.
Save benbahrenburg/c4c992c8c61d197510ea to your computer and use it in GitHub Desktop.
iOS 8: Geo Location Permissions Fix

Fixing Geo Location Permissions in iOS8

In iOS8, Apple has changed how geo location permissions work. This gist outlines an approach to fix this so Geo will continue to work in iOS8.

Before getting started with the code change, we need to update the tiapp.xml with a few keys.

If you wish to have Geo always run, you need to add the below key. The key NSLocationAlwaysUsageDescription is used when requesting permission to use location services whenever the app is running. To enable this, add the below to the tiapp.xml:

NSLocationAlwaysUsageDescription Reason that appears in the authorization prompt

If you wish to use Geo when your app is in the foreground, you need to add the below key. The key NSLocationWhenInUseUsageDescription is used when requesting permission to use location services while the app is in the foreground. To enable this, add teh below to the tiapp.xml

NSLocationWhenInUseUsageDescription Reason that appears in the authorization prompt

What happens if I add both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription keys to my tiapp.xml? In this case we assume you want the highest level access right and use the NSLocationAlwaysUsageDescription key.

Now to the code changes, first... we need to open GeolocationModule.m

Next we need to add some default behavior to the -(CLLocationManager*)locationManager method.

To do this scroll down to where you see the purpose logic in your method, and replace that block with the one below. This will kill two birds with one stone and remove the purpse section if you are running iOS8. You will notice we will automatically add the correct permission based on their tiapp.xml entries. This makes it easy for backwards compatability.

         if ([TiUtils isIOS8OrGreater]) {
             if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]){
                 [locationManager requestAlwaysAuthorization];
             }else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]){
                [locationManager  requestWhenInUseAuthorization];
             }else{
                  NSLog(@"[ERROR] The keys NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription are not defined in your tiapp.xml.  Starting with iOS8 this are required.");
             }
         }else{
             if (purpose==nil)
             {
                 DebugLog(@"[WARN] The Ti.Geolocation.purpose property must be set.");
             }
             else
             {
                 [locationManager setPurpose:purpose];
             }
         }

Next we need to add some properties. The developer will be able to use this when checking their permission status. In the GeolocationModule.m file scroll to the property section and add the below.

-(NSNumber*)AUTHORIZATION_ALWAYS
{
    if ([TiUtils isIOS8OrGreater]) {
        return NUMINT(kCLAuthorizationStatusAuthorizedAlways);
    }
    return NUMINT(0);
}

-(NSNumber*)AUTHORIZATION_WHEN_IN_USE
{
    if ([TiUtils isIOS8OrGreater]) {
        return NUMINT(kCLAuthorizationStatusAuthorizedWhenInUse);
    }
    return NUMINT(0);
}

Finally, we need to listen for the authorization change event. Scroll towards the end of the file and you will see all of the delegate methods. Add the below to this section of your code. This will fire an event off the Ti.Geolocation namespace called authorization.

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    NSDictionary *event = [NSDictionary dictionaryWithObjectsAndKeys:
                           NUMINT([CLLocationManager authorizationStatus]),@"authorizationStatus",nil];

    if ([self _hasListeners:@"authorization"])
    {
        [self fireEvent:@"authorization" withObject:event];
    }
}

Test Plan

How to test: ___________________

Testing Geo AlwaysUsage: ++++++++++++++++++++++++

  1. Using an Ti SDK with this patch applied create a test app ( in classic mode )
  2. Update the app.js with this sampel https://gist.github.com/benbahrenburg/c4c992c8c61d197510ea#how-to-test-this
  3. Open the tiapp.xml in the ios / plist / dict section add the below:
<key>NSLocationAlwaysUsageDescription</key>
<string>Test NSLocationAlwaysUsageDescription</string>
  1. Compile and push to your device or the simulator. Please note you must target iOS8.
  2. Press the btnAuthorization button, you should see a message that says Ti.Geolocation.AUTHORIZATION_UNKNOWN
  3. Press the btnGeoTest button, you should now see the permission box with the message Test NSLocationAlwaysUsageDescription
  4. Press the approve button
  5. You should now see the event below fire
Ti.Geolocation.addEventListener('authorization',function(e){
	Ti.API.info('authorization event:' + JSON.stringify(e));
});
  1. Press the btnAuthorization button, you should now see an alert with Ti.Geolocation.AUTHORIZATION_ALWAYS or Ti.Geolocation.AUTHORIZATION_AUTHORIZED

Testing Geo In Usage Permission: ++++++++++++++++++++++++

  1. Next reset your simulator or uninstall the app from your device
  2. Remove the NSLocationAlwaysUsageDescription settings from your tiapp.xml
  3. Open the tiapp.xml in the ios / plist / dict section add the below:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Test NSLocationWhenInUseUsageDescription</string>
  1. Compile and push to your device or the simulator. Please note you must target iOS8.
  2. Press the btnAuthorization button, you should see a message that says Ti.Geolocation.AUTHORIZATION_UNKNOWN
  3. Press the btnGeoTest button, you should now see the permission box with the message Test NSLocationWhenInUseUsageDescription
  4. Press the approve button
  5. You should now see the event below fire
Ti.Geolocation.addEventListener('authorization',function(e){
	Ti.API.info('authorization event:' + JSON.stringify(e));
});
  1. Press the btnAuthorization button, you should now see an alert with Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE
  2. Next reset your simulator or uninstall the app from your device

Testing what happens if you have both NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription ++++++++++++++++++++++++

  1. Next reset your simulator or uninstall the app from your device
  2. Open the tiapp.xml in the ios / plist / dict section add the below:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Test NSLocationWhenInUseUsageDescription</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Test NSLocationAlwaysUsageDescription</string>
  1. Compile and push to your device or the simulator. Please note you must target iOS8.
  2. Press the btnAuthorization button, you should see a message that says Ti.Geolocation.AUTHORIZATION_UNKNOWN
  3. Press the btnGeoTest button, you should now see the permission box with the message Test NSLocationAlwaysUsageDescription
  4. Press the approve button
  5. You should now see the event below fire
Ti.Geolocation.addEventListener('authorization',function(e){
	Ti.API.info('authorization event:' + JSON.stringify(e));
});
  1. Press the btnAuthorization button, you should now see an alert with Ti.Geolocation.AUTHORIZATION_ALWAYS or Ti.Geolocation.AUTHORIZATION_AUTHORIZED
  2. Next reset your simulator or uninstall the app from your device

Testing iOS7 Support ++++++++++++++++++++++++

  1. Next reset your simulator or uninstall the app from your device
  2. Open the tiapp.xml in the ios / plist / dict section add the below:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Test NSLocationWhenInUseUsageDescription</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Test NSLocationAlwaysUsageDescription</string>
  1. Compile and push to your device or the simulator. Please note you must target iOS7.
  2. Press the btnAuthorization button, you should see a message that says Ti.Geolocation.AUTHORIZATION_UNKNOWN
  3. Press the btnGeoTest button, you should now see the permission box with the text provided as part of the purpose
  4. Press the approve button
  5. You should now see the event below fire
Ti.Geolocation.addEventListener('authorization',function(e){
	Ti.API.info('authorization event:' + JSON.stringify(e));
});
  1. Press the btnAuthorization button, you should now see an alert with Ti.Geolocation.AUTHORIZATION_ALWAYS or Ti.Geolocation.AUTHORIZATION_AUTHORIZED

How to test this...

Use the below app.js file.

var win = Titanium.UI.createWindow({  
    backgroundColor:'#fff',layout:'vertical'
});

var btnAuthorization = Titanium.UI.createButton({
    title:'Authorization Check', left:25,right:25, top:80
});
win.add(btnAuthorization);

var btnGeoTest = Titanium.UI.createButton({
    title:'Test Geo', left:25,right:25, top:45
});
win.add(btnGeoTest);

Ti.Geolocation.addEventListener('authorization',function(e){
	Ti.API.info('authorization event:' + JSON.stringify(e));
});

function printAuthorizedResults(authorization){

	Ti.API.info('Authorization: '+authorization);

	if (authorization == Ti.Geolocation.AUTHORIZATION_UNKNOWN) {
		return "Authorization = Ti.Geolocation.AUTHORIZATION_UNKNOWN";
	}
	
	if (authorization == Ti.Geolocation.AUTHORIZATION_DENIED) {
		return "Authorization = Ti.Geolocation.AUTHORIZATION_DENIED";
	}	
	if (authorization == Ti.Geolocation.AUTHORIZATION_RESTRICTED) {
		return "Authorization = Ti.Geolocation.AUTHORIZATION_DENIED";
	}	
	if (authorization == Ti.Geolocation.AUTHORIZATION_AUTHORIZED) {
		return "Authorization = Ti.Geolocation.AUTHORIZATION_AUTHORIZED";
	}	
	if (authorization == Ti.Geolocation.AUTHORIZATION_ALWAYS) {
		return "Authorization = Ti.Geolocation.AUTHORIZATION_ALWAYS";
	}	
	if (authorization == Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE) {
		return "Authorization = Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE";
	}				
	
	return "Error: No authorization value found";
}

btnAuthorization.addEventListener('click',function(d){
	alert(printAuthorizedResults(Ti.Geolocation.locationServicesAuthorization));
});


btnGeoTest.addEventListener('click',function(d){
	Ti.Geolocation.getCurrentPosition(function(e)
	{
		if (!e.success || e.error){
			Ti.API.info("Code translation: "+translateErrorCode(e.code));
			alert('error ' + JSON.stringify(e.error));
			return;
		}

		var longitude = e.coords.longitude;
		var latitude = e.coords.latitude;
		var altitude = e.coords.altitude;
		var heading = e.coords.heading;
		var accuracy = e.coords.accuracy;
		var speed = e.coords.speed;
		var timestamp = e.coords.timestamp;
		var altitudeAccuracy = e.coords.altitudeAccuracy;
		Ti.API.info('speed ' + speed);

		Titanium.API.info('geo - current location: ' + new Date(timestamp) + ' long ' + longitude + ' lat ' + latitude + ' accuracy ' + accuracy);
	});	
});
win.open();
@jhaynie
Copy link

jhaynie commented Sep 10, 2014

this is awesome. thank you for writing up all this detail and submitting PR

@ayakix
Copy link

ayakix commented Sep 11, 2014

Great job! Thanks for sharing this information.

@yozef
Copy link

yozef commented Sep 13, 2014

Thanks Ben

@ricardoalcocer
Copy link

For those of you landing here, this fix is part of the Titanium SDK 3.4 Beta released in September 5 2014 - http://www.appcelerator.com/blog/2014/09/titanium-sdk-3-4-0-beta-for-ios-8-now-available/

@justinhernandez
Copy link

!!! IPHONE SIMULATOR !!!

Just wanted to add this comment in case anyone is using the iphone simulator. For some reason when using the simulator it will throw an error eventhough a location is set. Just select another location in Debug -> Location and the error will go away. It pops up sporadically. Hopefully that tip will save you an hour that it took me to figure it out. :)

@darkhorse2013
Copy link

@justinhernandez it seems like on a device it is buggy even if if (Ti.Geolocation.locationServicesEnabled) { returns true

@alvesl
Copy link

alvesl commented May 6, 2015

@justinhernandez That was my problem, Thank you! Couldnt find anywhere else.

PS: This seems to still be around with 3.5.1GA

@Gerst20051
Copy link

👍 this doesn't seem to work in 3.5.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment