Clay Allsopp

Routable

Apr 3, 2013

Find Routable on Github for Android and for iOS

Long ago, there was an iOS library known as Three20. Among Three20's many contributions was the TTNavigator, which allowed apps to be organized using URL-based routes. It was a pretty good idea, but got lost in the shuffle as Three20 fell by the wayside. Well, until now.

Routable is a set of libraries for both Android and iOS inspired by Three20's URL navigator. Here's how it works:

1. Define your routes.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // ...

    [[Routable sharedRouter] map:@"user/:id" toController:[UserController class]];
    [[Routable sharedRouter] map:@"messages" toController:[MessagesController class]];
}
public class PropellerApplication extends Application {
    @Override
    public void onCreate() {
        // ...

        Router.sharedRouter().map("users/:id", UserActivity.class);
        Router.sharedRouter().map("messsages", MessagesActivity.class);
    }
}

2. Implement the required Routable protocol in either your UIViewController or Activity subclass:

@implementation UserController

// This is a required method
- (id)initWithRouterParams:(NSDictionary *)params {
  if ((self = [self initWithNibName:nil bundle:nil])) {
    self.userId = [params objectForKey: @"id"];
  }
  return self;
}

@end
public class UserActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Parameters are passed in the extras
        Bundle intentExtras = getIntent().getExtras();
        String userId = intentExtras.get("id");
    }
}

3. Open a route, anywhere in your app!

[[Routable sharedRouter] open:@"users/16"];
Router.sharedRouter().open("users/16");

Why?

The benefit of structuring your app like this, as opposed to a loose collection of UIViewController and Activity objects, is that you can open any "screen" from any point in your code without wrangling Intents or controller allocations:

- (void)messagesTapped:(id)sender {
  [[Routable sharedRouter] open:@"messages"];
}
aView.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        Router.sharedRouter().open("messages");
    }
});

Or, imagine you're receiving push notifications from Apple or Google. Instead of hacking together a pseudo-protocol to determine what to show when a user opens a notification, just pass the appropriate Routable URL:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
  NSString *url = [userInfo objectForKey:@"url"];
  // => @"messages/123123"
  [[Routable sharedRouter] open: url];
}
public class GCMIntentService extends GCMBaseIntentService {
    @Override
    protected void onMessage(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        String route = extras.getString("url");
        // => "messages/123123"
        Router.sharedRouter().open(route);
    }
}

What else?

Routable has some other neat features, most notably the ability to route anonymous callbacks to URLs:

[[Routable sharedRouter] map:@"logout" toCallback:^(NSDictionary *params) {
  [User logout];
}];
Router.sharedRouter().map("logout", new Router.RouterCallback() {
    public void run(Map<String, String> extras) {
        User.logout();
    }
});

Contribute

We'd love to hear how you're using Routable and find out what else you think it can do. Don't hesitate to send pull-requests to the Android or iOS repos!