Titanium Tips

Fix: Invalid Binary (Push Notifications Issue)

Fix for Push Notifications Issue - Invalid Binary
If you’ve tried to submit an app to the App Store recently (using any version of Titanium Mobile prior to 1.6.2), you may have unfortunately had your app rejected for the following reason...

Missing Push Notification Entitlement
Your app registers with the Apple Push Notifications Service, but the application signature’s entitlements do not include the required “aps-environment” entitlement. Make sure you have enabled Push Notification Services for this app, and that you have downloaded a Distribution provisioning profile that includes the “aps-environment” entitlement.

What does this mean? Why is this a problem? How do you fix it?

What does this mean?

Somewhere in the code of Titanium Mobile (prior to version 1.6.2) is a reference to ‘Push Notifications’. This reference is included in every Titanium app - even apps that don’t use ‘Push Notifications’.

Why is this a problem?

Up until recently, it wasn’t. However, Apple has become a bit more picky about ‘Push Notifications’ and is now checking each app to ensure that ‘Push Notifications’ are handled how they want them to be handled.

Why is this a particularly annoying problem?

When submitting an app to iTunes Connect for distribution on the App Store, you have the option to validate your app binary prior to submission. This validation process checks for lots of Apple requirements and tells you if you have any issues that need to be resolved before going through the often quite time-consuming submission process. This validation process detects a lot of potential issues - but not this ‘Push Notifications’ issue. So even if your app passes the validation process, it will probably be rejected a short time later.

InvalidBinaryMessage

You’ll probably get an ‘Invalid Binary’ message and an email explaining the problem, then you’ll have go back to square one and start preparing your app again.

How do I fix this problem?

Appcelerator has released version 1.6.2 of Titanium Mobile to fix this issue and you can find all the official details here.

However, this is only good if you are using Titanium Mobile 1.6.2 or greater. If, for any reason, you prefer to stick with an older version of the Titanium Mobile SDK, you’ll have to patch the version you are using. Unless, of course, your app DOES use ‘Push Notifications’ - in that case you should be fine as is.

Here’s how to patch an older version of Titanium Mobile...
1. Navigate to /Library/Application Support/Titanium/mobilesdk/osx/[version of Titanium Mobile that you want to patch]/iphone/Classes/

2. Find the TiApp.mm and NetworkModule.m files. Back up these files somewhere before you start playing around with them.

Path to TiApp.mm
Path to NetworkModule.m

3. Open NetworkModule.m in a text editor and locate the following block of code (it starts on line 290 in Titanium Mobile 1.5.1, but other versions might be different)...
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
        // called by TiApp
        if (pushNotificationSuccess!=nil)
        {
                NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] 
                                                        stringByReplacingOccurrencesOfString:@">" withString:@""] 
                                                   stringByReplacingOccurrencesOfString: @" " withString: @""];
                NSDictionary *event = [NSDictionary dictionaryWithObject:token forKey:@"deviceToken"];
                [self _fireEventToListener:@"remote" withObject:event listener:pushNotificationSuccess thisObject:nil];
        }
}


4. Add double-slashes to the start of each line, so as to ‘comment out’ the code.
You could just delete the code, but commenting it out allows you to easily activate the code again (by removing those double-slashes) if you ever do create an app that uses Push Notifications and therefore needs that code.
//-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
//{
//      // called by TiApp
//      if (pushNotificationSuccess!=nil)
//      {
//              NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] 
//                                                      stringByReplacingOccurrencesOfString:@">" withString:@""] 
//                                                 stringByReplacingOccurrencesOfString: @" " withString: @""];
//              NSDictionary *event = [NSDictionary dictionaryWithObject:token forKey:@"deviceToken"];
//              [self _fireEventToListener:@"remote" withObject:event listener:pushNotificationSuccess thisObject:nil];
//      }
//}


5. Open up TiApp.mm in a plain text editor and locate the following block of code (it starts on line 479 in Titanium Mobile 1.5.1, but other versions might be different)...
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{ 
        NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] 
                                                stringByReplacingOccurrencesOfString:@">" withString:@""] 
                                           stringByReplacingOccurrencesOfString: @" " withString: @""];
        
        RELEASE_TO_NIL(remoteDeviceUUID);
        remoteDeviceUUID = [token copy];
        
        NSString *curKey = [[NSUserDefaults standardUserDefaults] stringForKey:@"APNSRemoteDeviceUUID"];
        if (curKey==nil || ![curKey isEqualToString:remoteDeviceUUID])
        {
                // this is the first time being registered, we need to indicate to our backend that we have a 
                // new registered device to enable this device to receive notifications from the cloud
                [[NSUserDefaults standardUserDefaults] setObject:remoteDeviceUUID forKey:@"APNSRemoteDeviceUUID"];
                NSDictionary *userInfo = [NSDictionary dictionaryWithObject:remoteDeviceUUID forKey:@"deviceid"];
                [[NSNotificationCenter defaultCenter] postNotificationName:kTiRemoteDeviceUUIDNotification object:self userInfo:userInfo];
                NSLog(@"[DEBUG] registered new device ready for remote push notifications: %@",remoteDeviceUUID);
        }
        
        if (remoteNotificationDelegate!=nil)
        {
                [remoteNotificationDelegate performSelector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:) withObject:application withObject:deviceToken];
        }
}


6. Add double-slashes to the start of each line, so as to ‘comment out’ this lot of code too.
//- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
//{ 
//      NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] 
//                                              stringByReplacingOccurrencesOfString:@">" withString:@""] 
//                                         stringByReplacingOccurrencesOfString: @" " withString: @""];
        
//      RELEASE_TO_NIL(remoteDeviceUUID);
//      remoteDeviceUUID = [token copy];
        
//      NSString *curKey = [[NSUserDefaults standardUserDefaults] stringForKey:@"APNSRemoteDeviceUUID"];
//      if (curKey==nil || ![curKey isEqualToString:remoteDeviceUUID])
//      {
//              // this is the first time being registered, we need to indicate to our backend that we have a 
//              // new registered device to enable this device to receive notifications from the cloud
//              [[NSUserDefaults standardUserDefaults] setObject:remoteDeviceUUID forKey:@"APNSRemoteDeviceUUID"];
//              NSDictionary *userInfo = [NSDictionary dictionaryWithObject:remoteDeviceUUID forKey:@"deviceid"];
//              [[NSNotificationCenter defaultCenter] postNotificationName:kTiRemoteDeviceUUIDNotification object:self userInfo:userInfo];
//              NSLog(@"[DEBUG] registered new device ready for remote push notifications: %@",remoteDeviceUUID);
//      }
        
//      if (remoteNotificationDelegate!=nil)
//      {
//              [remoteNotificationDelegate performSelector:@selector(application:didRegisterForRemoteNotificationsWithDeviceToken:) withObject:application withObject:deviceToken];
//      }
//}


7. The underlying Titanium Mobile code is now patched to disable Push Notifications and therefore prevent the App Store rejection problem, but you’ll need to empty your build folder and launch your app in the iPhone Simulator again to ensure that your project is rebuilt using the patched code.

Thanks to all the clever members of the Appcelerator community that worked out how to solve this problem. One of my apps got rejected due to this issue, but your comments at this Q&A discussion helped me to quickly resolve the problem. The instructions above have been based on your great work.