shadr.us

Hooking into TiApp

One of the pains with using Titanium is that they make it really quite difficult to hook into methods within the Application Delegate (TiApp). This is not a file that you want to change, as it is modified with each version of Titanium.

Normally, this is not a problem, but sometimes you need to integrate some 3rd party SDK, and all the instructions advise you to simply drop a line of code into applicationDidBecomeActive:. Sometimes, you can jump through hoops and find the right JS event to listen for, but often (as was the case when iOS7 was first released) there is simply no way to extend TiApp.

Or so I thought.

I recently stumbled across github.com/rubenfonseca/titanium-dropboxsync and realized that this module was actually extending TiApp in a way I didn’t realize was possible. So, after some reading up on “Swizzling” in Objective-C, I landed with the following solution. Perhaps this will be useful to someone else.

  1. Create your new module using ti create
  2. Clone JRSwizzle from github.com/rentzsch and add JRSwizzle.m/h to your project.
  3. Add methods to TiApp with a category, name your override methods with a prefix.
  4. Add a load method to your module that will “Swizzle” your implementation into place.

YourModule.m

#import "YourModule.h"
#import "TiBase.h"
#import "TiHost.h"
#import "TiUtils.h"
#import "TiApp.h" //added
#import "JRSwizzle.h" //added

//added - Create a category which adds new methods to TiApp
@implementation TiApp (YourModule)
- (void)yourmoduleApplicationDidBecomeActive:(UIApplication *)application
{
    // If you're successful, you should see the following output from titanium
    NSLog(@"[INFO] -- YourModule#applicationDidBecomeActive --");

    // be sure to call the original method
    // note: swizzle will 'swap' implementations, so this is calling the original method,
    // not the current method... so this will not infinitely recurse. promise.
    [self yourmoduleApplicationDidBecomeActive:application];
    
    // Add your custom code here...
}

@implementation ComFoodExtModule

// added - This is the magic bit... Method Swizzling
// important that this happens in the 'load' method, otherwise the methods
// don't get swizzled early enough to actually hook into app startup.
+ (void)load {
    NSError *error = nil;
    [TiApp jr_swizzleMethod:@selector(applicationDidBecomeActive:)
                 withMethod:@selector(foodextApplicationDidBecomeActive:)
                      error:&error];
    if(error)
        NSLog(@"[WARN] Cannot swizzle applicationDidBecomeActive: %@", error);
}


#pragma mark Internal
// Rest of your module implementation is down here...
// ...

References