WAAppRouting
iOS routing done right. Handles both URL recognition and controller displaying with parsed parameters. All in one line, controller stack preserved automatically!
Install / Use
/learn @Wasappli/WAAppRoutingREADME
Developed and Maintained by Ipodishima Founder & CTO at Wasappli Inc. (If you need to develop an app, get in touch with our team!)
So what is this library useful for? Good question. Let's answer by asking an other question. Have you been struggled at some point with the following issues?
- Well, I need to add some shortcuts to some parts of my apps and it seems crappy to manually allocate the path and select the controllers I need.
- I'm tired of using the push view controller method.
- I wish I had some kind of url handling to get to specific parts of my app just as easily as snapping a finger.
- If it could even handle a controller stack this would just be awesome.
- I found a library but it's not working with my custom container...
- I found a great library but the route matching is not working with my requirements...
All this points are answered by WAAppRouting (and more)
Which Uses?
- For all iOS: enable linking in your app. It is always useful to tell your app to go to
home/events/3/registerwith some magic involved. - For iOS 9: supports deeplinks (like Twitter app). Opening this URL Me on twitter would opened directly the app instead of the website.
- For iOS 9: respond to a search event. By using CoreSpotlight, users can go to your app by opening from a search result like a booking. At this point, you need to consider to
goto://bookings/bookingFromSearchID. Please take a look at an implementation of routing using a library which automatically index yourCoreDatastack WACoreDataSpotlight. - For iOS 9 and more specifically new iPhones 6S and 6+S: respond to 3D Touch
Table of Contents
#The story
What motivated me
Let's be honest, there are several routing libraries on Github to handle some of the behaviors described. But none of them fit all my requirements. So I wrote this library with some things in mind:
- Handle a stack of controllers.
This is not ok to open the app on a hotel detail if there is not even a back button, or if the back button sends me back to where I was before opening the app. I just want the app to be opened so that when I hit back, I'm on the hotels list for the hotel city...
- Do not force you to get this working with my route matcher, or my route handler.
If you want to provide your own, you should be able to do it. This last point is very important to me. I used (and use) too many libraries which are tightly tied to their technologies. Plus, the more they are dependant on their implementation, the less it is testable. This is why you'll see many protocols with a default implementation provided.
- iOS 9 is coming (or here already when you are reading this). And with iOS 9 comes this great feature called universal links. Well, I wanted something clean to address this new feature.
Inspiration
Historically, I first used HHRouter and implemented my own stack controller management. Then, by rewriting code to support iOS 9, I saw that it was just a bunch of lines with no error management, tightly tied to the controller hierarchy, not very readable, etc.
I decided to drop it and get something more fun. I found DeepLinkKit and used it until I realized it wasn't fitting my stack requirement. So I rewrote a custom route handler to deal with it and finally arrived to the conclusion that 80% of DeepLinkKit was not used anymore. This is when I decided to drop it and write my own.
So you might recognize some concepts of the two libraries, especially in the router handler, even if the implementation has nothing to do with DeepLinkKit.
Install and use
Requirements alongs with the default implementation
- The implementation I provide uses
UINavigationControllerto handle the stack and can be used onUITabBarControlleras well. - The route matching works on
:itemIDand uses*as the wildcard character.
Installation
CocoaPods
Use CocoaPods, this is the easiest way to install the router.
pod 'WAAppRouting'
If you want to link WAAppRouting into an iOS app extension (or a shared framework that is linked to an app extension), you'll need to ensure that the WA_APP_EXTENSION flag is set when you compile the framework. To do so using CocoaPods, add this to your Podfile:
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name =~ /WAAppRouting/
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= []
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'WA_APP_EXTENSION=1'
end
end
end
end
Older versions of cocoapods may require you to use installer.project instead of installer.pods_project, so if you get an error complaining that pods_project does not exist, update your cocoapods gem.
Setup the router: easiest method
Import #import <WAAppRouting/WAAppRouting.h> and you are good to start.
You also need to configure a URL scheme (I won't go into detail about this - there is plenty of documentation out there)
A navigation controller is a good start:
UINavigationController *navigationController = [[UINavigationController alloc] init];
You'll need first to allocate a route matcher. You can use the default I wrote or create your own:
// Create the default router
self.router = [WAAppRouter defaultRouter];
Register you path using the syntax:
url_path_component{ClassName}url_path_component1{ClassName1}/url_path_component2{ClassName2}
Optionaly, you can trigger the modal presentation using ! character.
For example: url_path_component{ClassName}/modal{ModalClass}! would result when calling appscheme://url_path_component/modal to ModalClass instance presented modally after placing ClassName in the navigation controller stack.
// Register the path
[self.router.registrar
registerAppRoutePath:
@"list{WAListViewController}/:itemID{WAListDetailViewController}/extra{WAListDetailExtraViewController}"
presentingController:navigationController];
Add a block handler if needed
// Register some blocks
[self.router.registrar registerBlockRouteHandler:^(WAAppLink *appLink) {
// Do something every time we are in list/something
}
forRoute:@"list/*"];
Finally set the navigation controller as the root controller:
self.window.rootViewController = navigationController;
You can now use it and open the app with:
[self application:(UIApplication *)self openURL:[NSURL URLWithString:@"appscheme://list"] sourceApplication:nil annotation:nil];
Don't forget to use the router!
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [self.router handleURL:url];
}
Each controller you use should implement WAAppRouterTargetControllerProtocol (it is a good idea to have a base view controller)
So implement this method and voilà:
- (void)reloadFromAppLinkRefresh {
// You can do something with self.appLink
// But more important: with self.appLinkRoutingParameters which has merged route|query|default parameters
NSString *articleTitle = self.appLinkRoutingParameters[@"article_title"];
}
Setup the router: more control methods
Start with the easiest method but replace the "create paths" by creating entities entities
// Create the entities
WAAppRouteEntity *list1Entity =
[WAAppRouteEntity routeEntityWithName:@"list"
path:@"list"
sourceControllerClass:nil
targetControllerClass:[WAListViewController class]
presentingController:navigationController
prefersModalPresentation:NO
defaultParametersBuilder:nil
allowedParameters:nil];
WAAppRouteEntity *list1DetailEntity =
[WAAppRouteEntity routeEntityWithName:@"listDetail"
path:@"list/:itemID"
sourceControllerClass:[WAListViewController class]
targetControllerClass:[WAListDetailViewController class]
presentingController:navigationController
prefersModalPresentation:NO
defaultParametersBuilder:^id<WAAppRouterParametersProtocol> {
NSMutableDictionary *defaultParameters = [NSMutableDictionary new];
defaultParameters[@"defaultParam"] = @1;
defaultParameters[@"defaultParam2"] = @"Default parameter 2";
return defaultParameters;
}
allowedParameters:nil];
Add the entities to the registrar
// Register the entities
[self.router.registrar registerAppRo

