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

Aug 20, 2014

The Back Button and Inconsistent Tendencies

There have been a number of posts lately about Android’s hardware back button, but they have, so far, missed the key issue. Basically, user behavior around the back button is inconsistent.

At ‘Food on the Table’, I have watched nearly a hundred people use our product on Android and iPhone (both live in person, and through UserTesting.com). One of the things that I’m constantly surprised by is how inconsistent user behavior is around the hardware back button. Some users will immediately jump back whenever they get somewhere unfamiliar. Others will just continue going deeper and deeper, always looking for some onscreen indication of where to go next, until they finally “give up” and tap the back button (“Well, I don’t know what to do here, so I guess I’ll just go back now”). While still others will get deep into the app and simply give up (“No idea what I should do now”).

I think one user put it best, “I don’t like to use the back button. It makes me feel like I’m failing. I like going forward in life”.

That’s a pretty incredible statement given that the entire design of the Android operating system sort of depends on you understanding how the back button works. To have three subtly different ways of looking at the same control is certainly not a good thing.

This sentiment has been echoed in some form or another by about 50% of our Android users. Whats more, in A/B tests where we exposed additional on screen controls for getting “back” to previous screens, we saw significant improvements in users “getting around”. However, when we ran these very same experiments on the iPhone, there was little or no improvement. iPhone users already know how to get around, they don’t need additional cues.

Takeaway for me: design for the least common denominator. Always have onscreen buttons for navigation. You cannot assume that users will understand the back button. I can assure you, they do not.

Nov 02, 2011

My git aliases

I use the following aliases in git all the time.

Status Info

br = branch

Simple branch info. No muss, no fuss.

bra = branch -v

Branch info with just a little more muss & fuss.

st = status --ignore-submodules=dirty

Current status, excluding changes in those dirty little submodules.

Preparing for commit

unstage = reset HEAD --

Remove all, one or many files from staging.

co = checkout

Log Viewing

lm = log master..

Show me commits on this branch that differ from master.

mt = mergetool

Bring up visual merge.

lg = log --graph --pretty=format:'%Cred%h%Creset -%C(magenta)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

Show me the first line of each commit, with simple branch info & visualization.

lgm = log master.. --graph --pretty=format:'%Cred%h%Creset - %s' --abbrev-commit

Show me commits with first line of everything that’s changed since master.

Cleanup & Pushing

pom = push origin master

Horrible command, do not add this one. You’ll use it all the time. Even when you don’t intend to.

remas = rebase master

Rebase on master.

fixup = commit --amend --reuse-message HEAD

  1. add files to staging
  2. update the last commit with them

/via @jeffxl

Pausing a Branch

wip = commit -a -m "WIP"

Checkin all current files and denote that the work contained there is not complete. Usefull when you want to move to a new branch, but not lose track of exactly where you were on the current branch.

See go below for picking back up.

go = reset HEAD^

Pop off the latest commit on a branch and go back to working on it. Best when used in conjunction with wip

[Alias] from ~/.gitconfig

[alias]
  co = checkout
  br = branch
  bra = branch -v
  st = status --ignore-submodules=dirty
  unstage = reset HEAD --
  lm = log master..
  mt = mergetool
  lg = log --graph --pretty=format:'%Cred%h%Creset -%C(magenta)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
  lgm = log master.. --graph --pretty=format:'%Cred%h%Creset - %s' --abbrev-commit
  pom = push origin master
  remas = rebase master
  fixup = commit --amend --reuse-message HEAD
  wip = commit -a -m "WIP"
  go = reset HEAD^

Feb 09, 2011

Food on the Table

I’m happy to announce that on December 1, I’ll be joining Food on the Table as a senior developer. It’s been a great year here at Workstreamer, but I’ve decided to move on.

Food on the Table is still small, only a handful of people at this point, but they’ve already made great progress. On top of that, a small, but growing, community of people have noticed (check out the wall on thier Facebook page).

I could not be more excited! :)

Nov 17, 2010

Building a JS Library

Javascript is a beautiful language when you used correctly. More and more, entire applications are being built primarily in Javascript. Getting started, though, is often the hardest part. This article will attempt to provide a starting point for building reusable javascript libraries.

Wrap Your Code

Right off the bat, it’s important to wrap all of your code:

(function () {
  /* All of your code goes in here! */
})();

Wrapping code in an anonymous function ensures that nothing is “leaked” into the global context. This may not seem so important at first, but as your app grows, it really is nice to know that you’re not “stepping” on yourself (or other libraries that you may be using).

Expose Functionality

Now, what if we want to actually expose some code to the surrounding page. For our example, let’s assume we’re developing for spicyjs.com, and our code is in the file spicy.js. We’ll create a single global variable named “SPICY”.

(function () {
  var spicy = {}
  window.SPICY = {};
  
  /* ... */

})();

Here’s another alternative:

var SPICY = (function () {
  var spicy = {}
  
  /* ... */
  
  return spicy;
})();

Now, anyone that wants to use our code outside of this file can through the “SPICY” variable. Internal to our library, we can refer to to this object as “spicy”. This sort of naming helps when dealing with multiple libraries that talk to one another. If you see your variable name in all lower case, you’ll know where you are.

As a general rule, it’s best to name all global variables in all CAPS just so you’ll recognize them easily.

To expose a function to the outside world, explicitly add it to the “SPICY” object.

(function () {
  var spicy = {}
  window.SPICY = SPICY;

  function button(spec) {
    // build a button
  }
  spicy.button = button;
  
  /* ... */
})();

When using this library, you’ll simply call your method through the SPICY variable.

SPICY.button({
  title: 'Click Me',
  action: function (event) {
    alert('I just got clicked, yo');
  }
});

References

Mar 20, 2010

TEST