- Requirements
- Installation
- Simple usage
- Advanced usage
- ACL
ARouteResponse
- Routes separation
- And... There is more!
- 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
:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *routes = @{
@"home":[HomeViewController class],
@"user-profile/{userId}": [UserViewController class]
};
[[[ARoute sharedRouter] registerRoutes:routes] execute];
return YES;
}
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.
- (void)openUserProfileViewControllerWithUserId:(NSString *)userId
{
NSString *userProfileRoute = [NSString stringWithFormat:@"user-profile/%@", userId];
[[[ARoute sharedRouter] route:userProfileRoute] execute];
}
Simple and easy:
- (void)openUserProfileViewControllerWithUserId:(NSString *)userId
{
NSString *userProfileRoute = [NSString stringWithFormat:@"user-profile/%@", userId];
[[[[ARoute sharedRouter] route:userProfileRoute] animated:^BOOL{
return NO;
}] execute];
}
--
If you have different route parameter separator in mind, you can customise it:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *routes =
@{
@"home":[HomeViewController class],
@"user-profile/:userId:": [UserViewController class]
};
[[[[ARoute sharedRouter] registerRoutes:routes] separator:^NSString *{
return @":";
}] execute];
}
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:^NSDictionary<id,id> *{
return @{@"message":@"some default message"};
}] execute];
Route execution:
[[[[ARoute sharedRouter] route:route] parameters:^NSDictionary<id,id> *{
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:
NSDictionary *routes = @{
@"user-profile/{userId|number}": [UserViewController class]
};
[[[ARoute sharedRouter] registerRoutes:routes] execute];
You can also define a specific casting class:
NSDictionary *routes = @{
@"user-profile/{userId|NSDecimalNumber}": [UserViewController class]
};
[[[ARoute sharedRouter] registerRoutes:routes] execute];
It works with your custom objects.
NSDictionary *routes = @{
@"user-profile/{userId|MyObject}": [UserViewController class]
};
[[[ARoute sharedRouter] registerRoutes:routes] execute];
On your object you must implement method:
- (instancetype)initWithRouteParameterValue:(NSString *)value;
from <ACastable>
protocol.
Casting pattern | Resolving class | Route example |
---|---|---|
undefined |
NSString |
@"user-profile/{userId}" |
string or NSString |
NSString |
`@"user-profile/{userId |
number or NSNumber |
NSNumber |
`@"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:
NSDictionary *routes =
@{
@"user-profile/{userId=number}": [UserViewController class]
};
[[[[ARoute sharedRouter] registerRoutes:routes] castingSeparator:^NSString*{
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.