Deep linking

To allow an application to be deep linkable, aside from registering a URL Scheme, as seen on quickstart and setting up the SDK, it needs to map routes. These routes should follow the convention static/path/:variable/:another_variable.

Route mapping

To map routes with HOKO you should call the deep linking module’s mapRoute:toTarget method after setting up the SDK in your application’s AppDelegate subclass on the application:didFinishLaunchingWithOptions: method.

If an application which has a product detail view controller it should somehow map a product route by calling mapRoute:toTarget: to the deep linking module.

[[Hoko deeplinking] mapRoute:@"products/:product_id"
                    toTarget:^(HOKDeeplink *deeplink) {
  // Do something when deeplink is opened
}];
Hoko.deeplinking().mapRoute("products/:product_id", toTarget: {
  (deeplink: HOKDeeplink!) -> Void in
    // Do something when deep link is opened
})

This will map a products/:product_id route to an executable target block. This target will always be executed when a deep link matching the route is opened in the user’s device. (e.g. opening hoko://products/42?referrer=hokolinks.com).

When a target block is executed, a HOKDeeplink object is passed as an argument.

This HOKDeeplink object will contain 3 parameters. The route to which the deep linking was matched against (in this case products/:product_id).

The routeParameters, a dictionary containing all the route format variables which were mapped to the incoming deep link (in this case {"product_id": 42}).

And finally the queryParameters, a dictionary which contains all the optional parameters which might passed through the query string in the deep link (in this example {"referrer": "hokolinks.com"}).

Target

The main purpose of a target execution block is for the app to create the navigation path and instantiate the proper view controllers to create the designed user experience. If a hoko://products/42?referrer=hokolinks.com deep linking is opened the target should provide the user with a product detail view controller representing the product opened.

[[Hoko deeplinking] mapRoute:@"products/:product_id"
                    toTarget:^(HOKDeeplink *deeplink) {
  BLKProductViewController *productViewController = [[BLKProductViewController alloc] initWithProductId:deeplink.routeParameters[@"product_id"]]; // always exists
  productViewController.referrer = deeplink.queryParameters[@"referrer"]; // might not exist
  [HOKNavigation pushViewController:productViewController animated:YES];
}];
Hoko.deeplinking().mapRoute("products/:product_id", toTarget: {
  (deeplink: HOKDeeplink!) -> Void in
    let productViewController = BLKPRoductViewController(productId: deeplink.routeParameters["product_id"]) // always exists
    productViewController.referrer = deeplink.queryParameters["referrer"] // might not exist
    HOKNavigation.pushViewController(productViewController, animated: true)
})

Default route

In case you want to provide a given behavior for when an unmapped deep linking opens the app you can do so by using the mapDefaultRouteToTarget: method. Only deep links that do not match any existing routes will trigger the default route.

[[Hoko deeplinking] mapDefaultRouteToTarget:^(HOKDeeplink *deeplink) {
  BLKLandingViewController *landingViewController = [BLKProductViewController new];
  [HOKNavigation setRootViewController:landingViewController];
}];
Hoko.deeplinking().mapDefaultRouteToTarget { (deeplink: HOKDeeplink!) -> Void in
  let landingViewController = BLKLandingViewController()
  HOKNavigation.setRootViewController(landingViewController)
}

Handlers

In order to execute code that is common to any incoming deep link (e.g. analytics tracking, referrer tracking, etc), the deeplinking module allows delegating and block execution of handlers.

If a class object implements the HOKHandlerProtocol interface and that object is added to the handler list, its handleDeeplink: method will be executed.

// Analytics.h
@interface Analytics: NSObject <HOKHandlerProtocol>
...
@end
// Analytics.m
@implementation Analytics
...
- (void)handleDeeplink:(HOKDeeplink *)deeplink
{
  [self track:"deeplink" parameters:@{@"route": deeplink.route}];
}
// AppDelegate.m
...
[[Hoko deeplinking] addHandler:[Analytics sharedInstance]];
// Analytics.swift
class Analytics: HOKHandlerProtocol {
...
func handleDeeplink(deeplink: HOKDeeplink!) {
  track("deeplink", parameters: ["route": deeplink.route])
}
// AppDelegate.swift
...
Hoko.deeplinking().addHandler(Analytics.sharedInstance())

Aside from interface implementation, HOKO also allows adding handler blocks to be executed.

[[Hoko deeplinking] addHandlerBlock:^(HOKDeeplink *deeplink) {
  [[Analytics sharedInstance] track:"deeplink" parameters:@{@"route": deeplink.route}];
}];
Hoko.deeplinking().addHandlerBlock { (deeplink: HOKDeeplink!) -> Void in
  Analytics.sharedInstance().track("deeplink", parameters: ["route": deeplink.route])
}

To save time integrating HOKO in an application, HOKO does not require delegation of the application:openURL:sourceApplication:annotation: (or its iOS < 4.2 counterpart application:handleOpenURL:) from the AppDelegate, this is automatically done through method swizzling. Should you choose to delegate manually, you must make sure to return true in case the deep link was handled, by either HOKO or other deep link frameworks and false otherwise.

// AppDelegate.m
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  if ([[FBSDKApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:sourceApplication annotation:annotation])
    return YES;
  return [[Hoko deeplinking] openURL:url sourceApplication:sourceApplication annotation:annotation];
}
// AppDelegate.swift
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
  if FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication annotation: annotation) {
    return true
  }
  return Hoko.deeplinking().openURL(url, sourceApplication: sourceApplication, annotation: annotation)
}

HOKO is all about saving your development time and, with that in mind, our SDK also does not require you to delegate the application:continueUserActivity:restorationHandler: method from the AppDelegate in order to work with Apple’s Universal Links (HOKO handles all of this automatically via method swizzling). Although that delegate method is used for Universal Links, keep in mind that it is also used for Handoff and iOS 9's search.
When this method is called, HOKO verifies that the link associated with the NSUserActivity, generated and passed by iOS, is a proper HOKO link. If it’s not, the SDK will redirect the method’s call to your AppDelegate, if implemented.
Should you choose to delegate manually, you must make sure to return true in case the NSUserActivity was handled, by either HOKO or by any framework/method and false otherwise.

// AppDelegate.m
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
{
    // 'processUserActivity:' would be one of your methods that
    // would check if the activity received is what you're expecting.
    if ([self processUserActivity:userActivity]) {
        return YES;
    }
    return [[Hoko deeplinking] continueUserActivity:userActivity restorationHandler:restorationHandler];
}
// AppDelegate.swift
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
    // 'processUserActivity()' would be one of your methods that
    // would check if the activity received is what you're expecting.
    if (processUserActivity(userActivity)) {
        return true
    }
    return Hoko.deeplinking().continueUserActivity(userActivity, restorationHandler:restorationHandler)
}

Smart links may be created on the dashboard or through the HOKO SDK, in order to allow users to share platform independent links directly to the relevant content.

To generate Smart links through templates, the application needs a route format, the corresponding route parameters and optional query parameters.

HOKDeeplink *deeplink = [HOKDeeplink deeplinkWithRoute:@"products/:product_id"
                                     routeParameters:@{@"product_id":@(self.product.identifier)}
                                     queryParameters:@{@"referrer": self.user.name}];
[[Hoko deeplinking] generateSmartlinkForDeeplink:deeplink success:^(NSString *smartlink) {
  [[Social sharedInstance] shareProduct:self.product link:smartlink];
} failure:^(NSError *error) {
  // Share web link instead
  [[Social sharedInstance] shareProduct:self.product link:self.product.webLink];
}];
let deeplink = HOKDeeplink("products/:product_id", routeParameters: ["product_id": product.identifier],
                                                 queryParameters:["referrer": user.name])
Hoko.deeplinking().generateSmartlinkForDeeplink(deeplink, success: { (smartlink: String!) -> Void in
  Social.sharedInstance().shareProduct(product, link: smartlink)
}) { (error: NSError!) -> Void in
  // Share web link instead
  Social.sharedInstance().shareProduct(product, link: self.product.webLink)
}

To allow applications to link to content without the use of templates, the HOKO SDK allows the creation of HOKDeeplink objects and the manual addition of platform dependent URLs.

HOKDeeplink *deeplink = [HOKDeeplink deeplinkWithRoute:@"products/:product_id"
                                     routeParameters:@{@"product_id":@(self.product.identifier)}
                                     queryParameters:@{@"referrer": self.user.name}];
[deeplink addURL:@"http://awesomeapp.com/the_perfect_product" forPlatform:HOKDeeplinkPlatformWeb];
[deeplink addURL:@"http://awesomeapp.com/no_android_app_yet" forPlatform:HOKDeeplinkPlatformAndroid];
[[Hoko deeplinking] generateSmartlinkForDeeplink:deeplink success:^(NSString *smartlink) {
  [[Social sharedInstance] shareProduct:self.product link:smartlink];
} failure:^(NSError *error) {
  // Share web link instead
  [[Social sharedInstance] shareProduct:self.product link:self.product.webLink];
}];
let deeplink = HOKDeeplink("products/:product_id", routeParameters: ["product_id": product.identifier],
                                                 queryParameters:["referrer": user.name])
deeplink.addURL("http://awesomeapp.com/the_perfect_product" forPlatform:.Web)
deeplink.addURL("http://awesomeapp.com/no_android_app_yet" forPlatform:.Android)
Hoko.deeplinking().generateSmartlinkForDeeplink(deeplink, success: { (smartlink: String!) -> Void in
  Social.sharedInstance().shareProduct(product, link: smartlink)
}) { (error: NSError!) -> Void in
  // Share web link instead
  Social.sharedInstance().shareProduct(product, link: self.product.webLink)
}

Should you want to open a Smart link (e.g. https://yourapp.hoko.link/example) and have it work as a deeplink that is processed by your route handler, you can call openSmartlink:completion:.

[[Hoko deeplinking] openSmartlink:slink completion:^(HOKDeeplink *deeplink) {
  if (!deeplink) {
    // deepling was opened
  } else {
    // could not resolve Smartlink
  }
}];
Hoko.deeplinking().openSmartlink(slink, completion: { (deeplink: HOKDeeplink?) -> Void in
  if let link = deeplink {
    // deepling was opened
  } else {
    // could not resolve Smart link
  }
}

If you choose not to have a completion block, you can use the openSmartlink: method without the 2nd parameter.

View Hoko Utilities →