Deep linking
- - Route mapping
- - Deep link delegation
- - Universal Links Delegation (NSUserActivity)
- - Smart link Generation
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
).
HOKDeeplink
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])
}
Deep link delegation
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)
}
Universal Links Delegation (NSUserActivity)
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 link Generation
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.
Smart links from templates
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)
}
Smartlinks from URLs
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)
}
Resolving Smart links
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.