- 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:
FriendsListViewController
UserProfileDetailsViewController
UserPhotosViewController
as 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.