Skip to content

Instantly share code, notes, and snippets.

@benbahrenburg
Last active August 29, 2015 14:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benbahrenburg/7b65d074c728cba0de4c to your computer and use it in GitHub Desktop.
Save benbahrenburg/7b65d074c728cba0de4c to your computer and use it in GitHub Desktop.
iOS 8 : Local Notification PR Description

Adding iOS8 Location Notification Permission Support to Titanium

Before you start, you must make the below changes to Ti SDK 3.4.0 or greater

This gist discusses the approach used to provide a PR for https://jira.appcelerator.org/browse/TC-4671 PR submitted here tidev/titanium-sdk#6025

In this gist we will provide step by step instructions and reasons for adding features to Titanium to allow developers to check iOS 8 the current UIUserNotificationSettings for the app. These are needed so the developer can determine if the proper UIUserNotificationType has been granted to their app.

To accomplish this, we first add an event listener to Ti.App.iOS called usernotificationsetting. This will fire when the app's [UIApplication sharedApplication] currentUserNotificationSettings is updated.

Second we create a method named getCurrentUserNotificationSettings which returnes a dictionary with an array of UIUserNotificationType which are current granted to the app.

Both of these features are needed in order to properly check if your Titanium application currently has registered the proper rights to be able to create local notifications.

Add the usernotificationsetting listener to Ti.App.iOS

To add the usernotificationsetting to Ti.App.iOS we have to update the UIApplicationDelegate located in TiApp.m.

Open TiApp.m and go to the iOS local notification section and add the below.

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
	[[NSNotificationCenter defaultCenter] postNotificationName:kTiUserNotificationSettingsNotification object:notificationSettings userInfo:nil];
}

By adding the didRegisterUserNotificationSettings method, your app will now call this delegate method whenever an UIUserNotificationSettings registeration event happens. This is typically after the user answers the iOS security prompt allowing you to register local notifications or disallowing you app to do so.

The didRegisterUserNotificationSettings method simply creates a notification which will be handled in the TiAppiOSProxy.

Next you need to add the const used by the notification. Open TiBase.h and add the below to the const section.

extern NSString * const kTiUserNotificationSettingsNotification;

Then open TiBase.m and add the following to the const section.

NSString * const kTiUserNotificationSettingsNotification = @"TiUserNotificationSettingsNotification";

Now you have the delegate method ready to start sending notifications.

Next open TiAppiOSProxy and update the _listenerAdded method. At the end of the method add the below code:

    if ([TiUtils isIOS8OrGreater]){
        if ((count == 1) && [type isEqual:@"usernotificationsetting"]) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector
             (didRegisterUserNotificationSettingsNotification:) name:kTiUserNotificationSettingsNotification object:nil];
        }
    }

This will add our listener and fire the didRegisterUserNotificationSettingsNotification method when the UIUserNotificationSettings registeration event is fired on the app delegate.

Next open update the _listenerRemoved method. At the end of this method, add the below code:

    if ([TiUtils isIOS8OrGreater]){
        if ((count == 1) && [type isEqual:@"usernotificationsetting"]) {
            [[NSNotificationCenter defaultCenter] removeObserver:self name:kTiUserNotificationSettingsNotification object:nil];
        }
    }

This will allow the developer to remove the Titanium event.

Next scroll to the end of the TiAppiOSProxy file and add the below code. This is the code that is triggered by the UIUserNotificationSettings notification.

-(void)didRegisterUserNotificationSettingsNotification:(NSNotification*)notificationSettings
{
    [self fireEvent:@"usernotificationsetting"
         withObject:[self formatUserNotificationSettings:(UIUserNotificationSettings*)[notificationSettings object]]];
}

Next we will add our formatter. The formatter is used in a few places to format the UIUserNotificationSettings object into a nicer object for JavaScript. Above your didRegisterUserNotificationSettingsNotification method, add the below code:

-(NSDictionary*)formatUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings
{
     NSMutableArray *notifSettings = [[NSMutableArray alloc] init];
    if(notificationSettings == nil)
    {
        return [NSDictionary dictionaryWithObjectsAndKeys: notifSettings,@"types",nil];
    }

    if((notificationSettings.types & UIUserNotificationTypeNone) != 0){
        [notifSettings addObject:NUMINT(UIUserNotificationTypeNone)];
    }

    if((notificationSettings.types & UIUserNotificationTypeBadge) != 0){
        [notifSettings addObject:NUMINT(UIUserNotificationTypeBadge)];
    }

    if((notificationSettings.types & UIUserNotificationTypeSound) != 0){
        [notifSettings addObject:NUMINT(UIUserNotificationTypeSound)];
    }

    if((notificationSettings.types & UIUserNotificationTypeAlert) != 0){
        [notifSettings addObject:NUMINT(UIUserNotificationTypeAlert)];
    }

    return [NSDictionary dictionaryWithObjectsAndKeys: notifSettings,@"types",nil];
}

Add the getCurrentUserNotificationSettings method to Ti.App.iOS

Next we will create the getCurrentUserNotificationSettings method. This method returns a dictionary with all of the UIUserNotificationSettings which your application has registered and allowed by the user.

At the end of your TiAppiOSProxy.m file add the below code:

-(NSDictionary*)getCurrentUserNotificationSettings:(id)unused
{

    if (![TiUtils isIOS8OrGreater]){
        return nil;
    }

    return [self formatUserNotificationSettings:[[UIApplication sharedApplication] currentUserNotificationSettings]];
}

You can now use this to query the [UIApplication sharedApplication] currentUserNotificationSettings for registered and allowed notification types.

var _versionNum = null; //Cache for later
var indexOf = function(searchValue) {
if(typeof Array.prototype.indexOf === 'function') {
indexOf = Array.prototype.indexOf;
} else {
indexOf = function(searchValue) {
var i = -1, index = -1;
for(i = 0; i < this.length; i++) {
if(this[i] === searchValue) {
index = i;
break;
}
}
return index;
};
}
return indexOf.call(this, searchValue);
};
var helpers = {
formatTypes : function(types){
if((types==undefined)||(types==null)){
return [];
}
var userSettings = [];
for (var iLoop=0;iLoop<types.length;iLoop++){
if(types[iLoop] == Ti.App.iOS.NOTIFICATION_TYPE_BADGE ){
userSettings.push({
name:"Ti.App.iOS.NOTIFICATION_TYPE_BADGE", id:types[iLoop]
});
}
if(types[iLoop] == Ti.App.iOS.NOTIFICATION_TYPE_SOUND ){
userSettings.push({
name:"Ti.App.iOS.NOTIFICATION_TYPE_SOUND", id:types[iLoop]
});
}
if(types[iLoop] == Ti.App.iOS.NOTIFICATION_TYPE_ALERT ){
userSettings.push({
name:"Ti.App.iOS.NOTIFICATION_TYPE_ALERT", id:types[iLoop]
});
}
}
return userSettings;
}
};
var notify = {
permissionCheckRequired : function(){
if (Ti.Platform.name == 'iPhone OS'){
if(_versionNum == null){
var version = Ti.Platform.version.split(".");
var major = parseInt(version[0],10);
_versionNum = major;
}
if (_versionNum >= 8){
return true;
}
}
return false;
},
permissionsGranted : function(){
var currentNotifications = Ti.App.iOS.getCurrentUserNotificationSettings();
return helpers.formatTypes(currentNotifications.types);
},
checkIfPermissionGranted :function(permissions){
var currentNotifications = Ti.App.iOS.getCurrentUserNotificationSettings();
var results = [];
for (var iLoop=0;iLoop<permissions.length;iLoop++){
results.push({
type:permissions[iLoop],
hasPermission: (indexOf.call(currentNotifications, permissions[iLoop]) !==-1)
});
}
return results;
},
hasAsked : function(){
return Ti.App.Properties.getBool("NOTIFICATION_QUESTION_ASKED",false);
},
resetAsked : function(){
Ti.App.Properties.removeProperty("NOTIFICATION_QUESTION_ASKED");
},
register : function(notification,callback){
function wrapper(e){
Ti.App.iOS.removeEventListener('usernotificationsetting',wrapper);
Ti.App.Properties.setBool("NOTIFICATION_QUESTION_ASKED",true);
callback(helpers.formatTypes(e.types));
};
Ti.App.iOS.addEventListener('usernotificationsetting',wrapper);
Ti.App.iOS.registerForLocalNotifications(notification);
}
};
module.exports = notify;

Taking it to the next level

Now that we have the ability to access [UIApplication sharedApplication] currentUserNotificationSettings and the UIUserNotificationSettings events we can now start to make things easier for JavaScript by creating a CommonJS module such as the localnotify_permission_helper.js included in this gist.

Here is an app.js using this approach.

var permissionHelper = require('localnotify_permission_helper');

var win = Ti.UI.createWindow({  
    backgroundColor:'#fff'
});

var bntTest = Ti.UI.createButton({
	title:'Test', width:'auto'
});
win.add(bntTest);

bntTest.addEventListener('click',function(d){

	 Ti.API.info("Has already Asked for Permission? " + permissionHelper.hasAsked());
	 
	 var request = {
                    types: Ti.App.iOS.NOTIFICATION_TYPE_BADGE |
                           Ti.App.iOS.NOTIFICATION_TYPE_SOUND |
                           Ti.App.iOS.NOTIFICATION_TYPE_ALERT,
                    categories: [Ti.App.iOS.NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
                    			Ti.App.iOS.NOTIFICATION_ACTIVATION_MODE_FOREGROUND]
      };
      
	 permissionHelper.register(request,function(e){
		Ti.API.info('request = ' + JSON.stringify(e));
		Ti.API.info("Did I get all of my permissions? " + 
					 JSON.stringify(permissionHelper.checkIfPermissionGranted([
					 		Ti.App.iOS.NOTIFICATION_TYPE_BADGE,
                            Ti.App.iOS.NOTIFICATION_TYPE_SOUND,
                            Ti.App.iOS.NOTIFICATION_TYPE_ALERT])));	 

		var myPermissions = permissionHelper.permissionsGranted();
		if(myPermissions.length > 0){
			Ti.API.info("What permissions do I have? " + JSON.stringify(myPermissions));		
		}else{
			Ti.API.info("What permissions do I have? None...");	
		}                             
        
	 });
	
});

win.open();

Testing the updates

Below is a simple app.js which demonstrates how to use both the usernotificationsetting listener and the getCurrentUserNotificationSettings method.

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

var bntTest1 = Titanium.UI.createButton({
    title:'Test 1', width:'auto', top:80
});
win.add(bntTest1);

var bntTest2 = Titanium.UI.createButton({
    title:'Test 2', width:'auto', top:60
});
win.add(bntTest2);

Ti.App.iOS.addEventListener('usernotificationsetting',function(e){
    Ti.API.info('usernotificationsetting:' + JSON.stringify(e));        
});

bntTest1.addEventListener('click',function(d){

     Ti.App.iOS.registerForLocalNotifications({
                    types: Ti.App.iOS.NOTIFICATION_TYPE_BADGE |
                           Ti.App.iOS.NOTIFICATION_TYPE_SOUND |
                           Ti.App.iOS.NOTIFICATION_TYPE_ALERT,
                    categories: [Ti.App.iOS.NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
                                Ti.App.iOS.NOTIFICATION_ACTIVATION_MODE_FOREGROUND]
       });

});

bntTest2.addEventListener('click',function(d){

    var currentNotifications = Ti.App.iOS.getCurrentUserNotificationSettings();
    var userSettings = [];
    for (var iLoop=0;iLoop<currentNotifications.types.length;iLoop++){
        if(currentNotifications.types[iLoop] == Ti.App.iOS.NOTIFICATION_TYPE_BADGE ){
            userSettings.push("Ti.App.iOS.NOTIFICATION_TYPE_BADGE");
        }
        if(currentNotifications.types[iLoop] == Ti.App.iOS.NOTIFICATION_TYPE_SOUND ){
            userSettings.push("Ti.App.iOS.NOTIFICATION_TYPE_SOUND");
        }
        if(currentNotifications.types[iLoop] == Ti.App.iOS.NOTIFICATION_TYPE_ALERT ){
            userSettings.push("Ti.App.iOS.NOTIFICATION_TYPE_ALERT");
        }               
    }
    Ti.API.info('displayNotifications:' + JSON.stringify(userSettings));

});
win.open();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment