- iOS 8.0 or greater
ARoute is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "ARoute"--
First thing to be done is registering the routes on appropriate place in app, e.g. AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let routes: [String: AnyObject] = [
"home": HomeViewController.self,
"user-profile/{userId}": UserViewController.self
]
ARoute.sharedRouter()
.registerRoutes(routes)
.execute()
return true
}Take a look at route:
user-profile/{userId}
Notice that userId is wrapped in { }.
This parameter and its value will be accesible in ARouteResponse's property routeParameters which is actually NSDictionary object. Read more about ARouteResponse.
Executing the route is trivial.
Pass the route pattern string you used in route registration, but remember to replace the wrapped value with the actual one.
func openUserProfileViewController(userId: String) -> Void {
let userProfileRoute = String(format: "user-profile/%@", userId)
ARoute.sharedRouter()
.route(userProfileRoute)
.execute();
}Simple and easy:
func openUserProfileViewController(userId: String) -> Void {
let userProfileRoute = String(format: "user-profile/%@", userId)
ARoute.sharedRouter()
.route(userProfileRoute)
.animated({ () -> Bool in
// return false if you don't want animations
return false
})
.execute();
}--
If you have different route parameter separator in mind, you can customise it:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let routes: [String: AnyObject] = [
"home": HomeViewController.self,
"user-profile/:userId:": UserViewController.self
]
ARoute.sharedRouter()
.registerRoutes(routes)
.separator({ () -> String in
return ":"
})
.execute()
return true
}That means that route parameters should be wrapped in chosen separator.
If you would like to wrap route parameters in opening and closing characters, return such string in callback.
Examples:
| Registration pattern | Separator | Execution pattern | Parameter object |
|---|---|---|---|
user/id-{userId} |
{} (default) |
user/id-123456 |
@{@"userId": @"123456"} |
user/id-!userId/profile |
! or !! |
user/id-123456/profile |
@{@"userId": @"123456"} |
user/name-!userName/profile |
! or !! |
user/name-my-name/profile |
@{@"userName": @"my-name"} |
user/:first:-:last:/profile |
: or :: |
user/aron-balog/profile |
@{@"first": @"aron", @"last": @"balog"} |
Passing parameters is possible using both registration end executing the route.
Route registration:
ARoute.sharedRouter()
.registerRoutes(routes)
.parameters({ () -> [NSObject : AnyObject]? in
return ["message":"some default message"]
})
.execute()Route execution:
ARoute.sharedRouter()
.route(route)
.parameters({ () -> [NSObject : AnyObject]? in
return ["message2":"Another message"]
})
.execute();Note: If you use same parameter keys in registration and execution, priority will be on execution parameter.ARouteResponse will receive combined values of both parameters.
E.g. this example will return following dictionary (routeResponse.parameters):
[
"message": "some default message",
"message2": "Another message"
]ARoute supports parameter casting. Examples:
let routes: [String: AnyObject] = [
"user-profile/{userId|number}": UserViewController.self
]
ARoute.sharedRouter()
.registerRoutes(routes)
.execute()You can also define a specific casting class:
let routes: [String: AnyObject] = [
"user-profile/{userId|NSDecimalNumber}": UserViewController.self
]
ARoute.sharedRouter()
.registerRoutes(routes)
.execute()It works with your custom objects.
let routes: [String: AnyObject] = [
"user-profile/{userId|MyObject}": UserViewController.self
]
ARoute.sharedRouter()
.registerRoutes(routes)
.execute()On your object you must implement method:
required init(routeParameterValue value: String) {
}from <ACastable> protocol.
| Casting pattern | Resolving class | Route example |
|---|---|---|
undefined |
String |
"user-profile/{userId}" |
string or NSString |
String |
`"user-profile/{userId |
number or NSNumber |
String |
`"user-profile/{userId |
decimal or NSDecimalNumber |
NSDecimalNumber |
`"user-profile/{userId |
MyCustomClass |
MyCustomClass |
`"user-profile/{userId |
#####There is more!
If parameters casting separator needs to be changed, you can do it this way:
let routes: [String: AnyObject] = [
"user-profile/{userId=number}": UserViewController.self
]
ARoute.sharedRouter()
.registerRoutes(routes)
.castingSeparator({ () -> String in
return "="
})
.execute()You can link a callback to a route:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *routes =
@{
@"home":[HomeViewController class],
@"user-profile/{userId}": [UserViewController class],
@"friends/{userId}/delete":^(ARouteResponse *routeResponse){
NSLog(@"Deleting user with ID %@", routeResponse.routeParameters[@"userId"]);
// e.g. call your deletion method here
}
};
[[[ARoute sharedRouter] registerRoutes:routes] execute];
return YES;
}... then call (execute) the route:
- (void)deleteFriendWithId:(NSString *)userId
{
NSString *route = [NSString stringWithFormat:@"friends/%@/delete", userId];
[[[ARoute sharedRouter] route:route] execute];
}It is possible to forward <UIViewControllerTransitioningDelegate> to destination view controller. Just call transitioningDelegate block and return an object conformed to <UIViewControllerTransitioningDelegate> protocol.
- (void)openUserProfileViewControllerWithUserId:(NSString *)userId
{
NSString *userProfileRoute = [NSString stringWithFormat:@"user-profile/%@", userId];
id <UIViewControllerTransitioningDelegate> animator = [Animator new];
[[[[ARoute sharedRouter] route:userProfileRoute] transitioningDelegate:^id<UIViewControllerTransitioningDelegate>{
return animator;
}] execute];
}You can embed your view controllers in UINavigationController, UITabBarController and your custom view controllers. Embedding view controller instance will be part of ARouteResponse object.
Take a look at following examples:
[[[[ARoute sharedRouter] route:route] embedInNavigationController] execute];You can prestack view controllers when embedding in navigation controller. An array of items must be return.
Allowed types in array:
NSString- a route (if defined)UIViewController- your view controller instanceClass- your view controller class conforming<ARoutable>protocol
The code in following example will embed view controllers in UINavigationController in following order:
FriendsListViewControllerUserProfileDetailsViewControllerUserPhotosViewControlleras current view controller
As you can see, it will immediately place UserPhotosViewController as UINavigationController's current view controller.
// e.g. registred to UserPhotosViewController
NSString *route = @"user/123456/photos";
[[[[ARoute sharedRouter] route:route] embedInNavigationController:^NSArray *(ARouteResponse *routeResponse) {
return @[[FriendsListViewController new], [UserProfileDetailsViewController class];
}] execute];[[[[ARoute sharedRouter] route:route] embedInTabBarController] execute];Embedding destination view controller in custom view controller is possible by returning an object conforming <AEmbeddable> protocol in embedIn: block.
- (void)openUserProfileViewControllerWithUserId:(NSString *)userId
{
EmbeddingViewController <AEmbeddable> *embeddingViewController = [EmbeddingViewController new];
NSString *userProfileRoute = [NSString stringWithFormat:@"user-profile/%@", userId];
[[[[ARoute sharedRouter] route:userProfileRoute] embedIn:^__kindof UIViewController<AEmbeddable> *{
return embeddingViewController;
}] execute];
}Custom initiation is a cool feature and pretty easy to accomplish:
- (void)openUserProfileViewControllerWithUserId:(NSString *)userId
{
NSString *title = @"User profile";
[[[[ARoute sharedRouter] route:route] initSelector:^SEL{
return @selector(initWithTitle:);
} objects:^NSArray *{
return @[title];
}] execute];
}When destination view controller is presented, completion block will be executed.
-
[[[[ARoute sharedRouter] route:route] completion:^(ARouteResponse *routeResponse) { // handle route response }] execute];
-
[[[[ARoute sharedRouter] route:route] failure:^(ARouteResponse *routeResponse, NSError *error) { // handle the error }] execute];
--
You can protect your route.
You can globally protect your route upon registration:
[[[[ARoute sharedRouter]
registerRoutes:routes] protect:^BOOL(ARouteResponse *routeResponse, NSError **errorPtr) {
*errorPtr = YOUR_CUSTOM_ERROR_OBJECT;
// return YES if you don't want to handle the route
return YES;
}] execute];... or you can provide protection callback on route execution:
[[[[ARoute sharedRouter]
route:route] protect:^BOOL(ARouteResponse *routeResponse, NSError **errorPtr) {
*errorPtr = YOUR_CUSTOM_ERROR_OBJECT;
// return YES if you don't want to handle the route
return YES;
}] execute];An error set in error pointer will be provided in failure block.
BE AWARE!
Protection callback on execution will override the callback called upon registration!
--
ARouteResponse object wraps various data you pass through route registrations and route executions.
--
You are now already familiar with [ARoute sharedRouter]. This is ARoute global instance and in most cases it will be enough.
But, sometimes you need to separate things! 😎
For example, your app has an admin and frontend parts. Ideally, you would separate routers:
[[[ARoute createRouterWithName:@"admin"] registerRoutes:adminRoutes] execute];
or
[[[ARoute createRouterWithName:@"front"] registerRoutes:frontRoutes] execute];... then you would call a route:
[[[ARoute routerNamed:@"admin"] route:@"user-profile/12345"] execute];or
[[[ARoute routerNamed:@"front"] route:@"user-profile/12345"] execute];Note: These routes maybe look the same, but they are in different namespaces so separation is satisfied.
--
Aron Balog
ARoute is available under the MIT license. See the LICENSE file for more info.