Skip to content

Latest commit

 

History

History
466 lines (335 loc) · 14.3 KB

OBJECTIVE-C.md

File metadata and controls

466 lines (335 loc) · 14.3 KB

ARoute

Build Status Version License Platform

Table of contents

  1. Requirements
  2. Installation
  3. Simple usage
    1. Route registration
    2. Route execution
    3. Animations
  4. Advanced usage
    1. Parameters
    2. Custom separator
    3. Parameter casting
    4. Callbacks
    5. Custom animations
    6. Embedding
      1. UINavigation controller
      2. UITabBarController
      3. Custom view controllers
    7. Custom initiation
    8. Catching completion & failure
      1. Completion
      2. Failure
  5. ACL
  6. ARouteResponse
  7. Routes separation
  8. And... There is more!



Requirements

  • iOS 8.0 or greater

Installation

ARoute is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "ARoute"

--



🍼 Simple usage

Route registration

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.

Route execution

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];
}

Animations

Simple and easy:

- (void)openUserProfileViewControllerWithUserId:(NSString *)userId
{
	NSString *userProfileRoute = [NSString stringWithFormat:@"user-profile/%@", userId];

	[[[[ARoute sharedRouter] route:userProfileRoute] animated:^BOOL{
        return NO;
    }] execute];
}

--



🚀 Advanced usage

Custom separator

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"}

Parameters

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"
}

Parameter casting

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];

Callbacks

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];
}

Custom animations

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];
}

Embedding

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:

UINavigationController

[[[[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 instance
  • Class - your view controller class conforming <ARoutable> protocol

The code in following example will embed view controllers in UINavigationController in following order:

  1. FriendsListViewController
  2. UserProfileDetailsViewController
  3. 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];

UITabBarController

[[[[ARoute sharedRouter] route:route] embedInTabBarController] execute];

Custom view controllers

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

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];
}

Catching completion & failure

When destination view controller is presented, completion block will be executed.

  • Catching completion

     [[[[ARoute sharedRouter] route:route] completion:^(ARouteResponse *routeResponse) {
         // handle route response
     }] execute];
  • Catching failure

     [[[[ARoute sharedRouter] route:route] failure:^(ARouteResponse *routeResponse, NSError *error) {
         // handle the error
     }] execute];

--



👮 ACL (route protector)

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

ARouteResponse object wraps various data you pass through route registrations and route executions.

--



✂️ Routes separation

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.

--



Author

Aron Balog

License

ARoute is available under the MIT license. See the LICENSE file for more info.