Skip to content

DeepLink Kit Integration Guide

Wes Smith edited this page Dec 22, 2015 · 3 revisions

Overview

DeepLink Kit is a splendid route-handling block-based way to handle deep links. Rather than decide how to format your URLs, parse them, pass data, and navigate to specific content or perform actions, this library and a few lines of code will get you on your way.

Check it out

pod try DeepLinkKit

Note: Want to play with the source? Check it out

Getting DeepLink Kit

The best way to get the DeepLink Kit is to use CocoaPods. If you don't already use CocoaPods, the CocoaPods Getting Started Guide will have you managing dependencies in no time.

Add the following line to your Podfile.

pod "DeepLinkKit"

Import DeepLink Kit as follows:

#import <DeepLinkKit/DeepLinkKit.h>

The Router

To get started, you'll create an instance of DPLDeepLinkRouter. Since the URLs are received in your AppDelegate, create a property for your router and initialize it in -application:didFinishLaunchingWithOptions:.

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  self.router = [[DPLDeepLinkRouter alloc] init];

  return YES;
}

Receiving DeepLinks

To handle incoming URLs in your app, you'll implement application:openURL:sourceApplication:annotation: in your app delegate and pass the URL to the router.

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {

  return [self.router handleURL:url withCompletion:NULL];
}

If your application supports universal links introduced in iOS 9, implement the following in your app delegate:

- (BOOL)application:(UIApplication *)application
        continueUserActivity:(NSUserActivity *)userActivity
        restorationHandler:(void (^)(NSArray *))restorationHandler {

    return [self.router handleUserActivity:userActivity
                            withCompletion:NULL];
}

Registering Routes

Registering a route is easy. Registering your first route can be done in just one line of code:

Note: As of 0.2.0, in all registered routes, paths are considered to begin at the first forward slash. A route component before the first forward slash will be considered the host.

self.router[@"/say/hello"] = ^{
  NSLog(@"Hello World!");
};

Now if your app receives a URL with a path of /say/hello, it would log “Hello World!” to the console.

You can register dynamic routes just as easily:

self.router[@"/alert/:title/:message"] = ^(DPLDeepLink *link){
  [[[UIAlertView alloc] initWithTitle:link.routeParameters[@"title"]
                              message:link.routeParameters[@"message"]
                             delegate:nil
                    cancelButtonTitle:NSLocalizedString(@"OK", nil)
                    otherButtonTitles:nil] show];
};

Now if your app receives a URL with a path of /alert/Hello/World, your app would display the following alert:

Route Example

Routes are matched in the order they were registered. The first route that matches the incoming URL is the one that gets handled. For example, given an incoming URL path of /say/hello, if you had registered the following two routes, the second route would not get matched:

self.router[@"/say/hello"] = ^{
  NSLog(@"Hello World!");
};

self.router[@"/say/:hello"] = ^{
  // This block is never called.
};

Matching URL Host

URLs coming into your app will be in a similar format to the following: <scheme>://<host>/<path-component>/<path-component>

When registering routes, it's important to note that the first forward slash in your registered route determines the start of the path to be matched. A route component before the first forward slash will be considered to be the host.

Say you have an incoming URL of twitter://timeline

// Matches the URL.
router[@"timeline"] = ^{ … }

// Does not match the URL.
router[@"/timeline"] = ^{ … }

In another example, a URL of twitter://dpl.com/timeline

// Matches the URL.
router[@"/timeline"] = ^{ … }

// Does not match the URL.
router[@"timeline"] = ^{ … }

Route Handlers

When you register routes, you also specify the route handler to be used when handling a matching route. There are two type of route handlers.

Block-based handlers

Block handlers are great for simple cases such as performing an action in your app without any UI, or displaying a simple modal. Basically, block handlers leave all the work up to you. When an incoming URL is matched against a route, we'll call your block, pass you the parsed` deep link, and you do the rest.

The block type to use when specifying your route handler is DPLRouteHandlerBlock. The block takes one parameter, an instance of DPLDeepLink, the parsed object representation of the incoming deep link. You may omit the block parameter if you don't need it. For example, the following are valid ways to register a block-based handler for your routes:

router[@"/say/hello"] = ^{ };

router[@"/say/hello"] = ^(DPLDeepLink *deepLink) { };

Class-based handlers

Class-based handlers help simplify more complex situations where you want to map a route to a specific view controller. To create a class-based handler, create a subclass of DPLRouteHandler. In your subclass, at a minimum, you must override -targetViewController and return a view controller conforming to the DPLTargetViewController protocol.

@implementation ProductRouteHandler

- (UIViewController <DPLTargetViewController> *)targetViewController {
  return [[ProductDetailViewController alloc] init];
}

@end

Once you have your route handler subclass, you'll register it just as easily as a block-based handler.

self.router[@"/product/:product_id"] = [ProductRouteHandler class];

If your app receives a URL with a path of /product/abc123, the router will instantiate your route handler subclass which will automatically display your target view controller.

Your target view controller conforms to the DPLTargetViewController protocol, so you'll implement configureWithDeepLink: where you'll receive the DPLDeepLink instance and configure whatever content, action, etc is necessary to fulfill the use case.

@implementation ProductDetailViewController

- (void)configureWithDeepLink:(DPLDeepLink *)deepLink {

    // Get the data you need from the deep link to configure your view controller.
    NSString *productId = deepLink.routeParameters[@"product_id"];
    self.product = [Product productWithId:productId];
}

@end

Sample Code

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  // Setup your DeepLink router.
  self.router = [[DPLDeepLinkRouter alloc] init];

  // Register a route handler.
  self.router[@"/say/:title/:message"] = ^(DPLDeepLink *link) {
    [[[UIAlertView alloc] initWithTitle:link.routeParameters[@"title"]
                                message:link.routeParameters[@"message"]
                               delegate:nil
                      cancelButtonTitle:NSLocalizedString(@"OK", nil)
                      otherButtonTitles:nil] show];
  };

  // Present a view controller from a route handler.
  self.router[@"/product/:product_id"] = ^(DPLDeepLink *link) {

    // Configure your view controller with the incoming product id.
    ProductDetailViewController *controller = [[ProductDetailViewController alloc] init];
    controller.productId = link.routeParameters[@"product_id"];

    // Present the view controller.
    [self.window.rootViewController presentViewController:controller
                                                 animated:NO  
                                               completion:NULL];
  };

  // Register a class-based route handler.
  self.router[@"/event/:event_id"] = [EventRouteHandler class];

  return YES;
}


- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {

  [self.router handleURL:url withCompletion:^(BOOL handled, NSError *error) {
    NSLog(@"Handled URL");
  }];

  return YES;
}