{"_id":"5bd9129e92cbc0001f913e5c","project":"55e67aaa9cc7c62b00c4a1ea","version":{"_id":"5b720760c44b7600034b7a08","project":"55e67aaa9cc7c62b00c4a1ea","__v":0,"forked_from":"5b1f2cbdfd653400031d8d9f","createdAt":"2015-09-02T04:27:23.612Z","releaseDate":"2015-09-02T04:27:23.612Z","categories":["5b720760c44b7600034b79a7","5b720760c44b7600034b79a8","5b720760c44b7600034b79a9","5b720760c44b7600034b79aa","5b720760c44b7600034b79ab","561c61b4ad272c0d00a892df","586c014c0abf1d0f000d04d4","58991d2ad207df0f0002186b","5b720760c44b7600034b79ac","5b720760c44b7600034b79ad","5af0fe494ca2730003cbc98a","5af0fe55ec80af0003804ca2"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"API V6","version_clean":"1.4.0","version":"1.4"},"category":{"_id":"5b720760c44b7600034b79ac","project":"55e67aaa9cc7c62b00c4a1ea","__v":0,"version":"5b720760c44b7600034b7a08","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2017-02-08T21:31:11.878Z","from_sync":false,"order":6,"slug":"advanced-techniques","title":"Advanced Techniques"},"user":"587fed3d9efedf3b00200366","__v":0,"parentDoc":null,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2018-10-31T02:25:34.982Z","link_external":false,"link_url":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":3,"body":"When email is sent from Sailthru Email Manager, we go through the links in the templates, and then replace them with redirects, with domains of your choosing, generally something in the form of `http://link.yourdomain.com`. This allows us to track tap-through metrics on emails and measure your success. For example, Sailthru maintains a fake online clothing store, Varick and Vandam, as a demonstrator platform. Links in emails from V+V will look something like this: `https://link.varickandvandam.com/click/13071627.1/aHR0cHM.....`, but will redirect to: `https://varickandvandam.com/products/...`, referred to here as the \"canonical url\".\n\nSailthru Mobile SDK can be configured to handle these links and to open your app whenever a certain type of link is clicked. The SDK methods - called `handleSailthruLink` on both platforms - will do two things: \n1. Take the encoded `link.yourdomain.com/click/...` link and returns its canonical destination - the link that was entered into the template on Sailthru.\n2. Ping the link domain, meaning that tapthrough analytics will be preserved on the Sailthru platform..\n\nMake sure you've followed the steps in the [Sailthru Docs](https://getstarted.sailthru.com/account/management/apple-ios-app-universal-links/) before starting this guide - it corresponds to the \"Link Parsing and Tracking\" section.\n\n# Handling Universal Links From a Sailthru Email\n## Android\nAndroid can be configured to handle links from Sailthru emails without any further configuration on the Sailthru platform, using Android's deep-linking functionality. This means that when clicking on links in emails, users will be prompted which app to open the link with - example below.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/842bda5-app-disambiguation_2x.png\",\n        \"app-disambiguation_2x.png\",\n        500,\n        1010,\n        \"#5b5251\"\n      ],\n      \"caption\": \"Source: https://developer.android.com/training/app-links/verify-site-associations\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"We're currently investigating the best way to integrate App Links - which would remove the need for this disambiguation screen - into the Sailthru platform.\",\n  \"title\": \"What about App Links?\"\n}\n[/block]\nWe're going to continue to use Varick and Vandam as our example - it's link domain is `https://link.varickandvandam.com`. To route these links to our V+V app, we'll need to define an intent filter on our `MainActivity` in our `AndroidManifest.xml`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \" <activity\\n      \\tandroid:name=\\\".MainActivity\\\">\\n        <intent-filter>\\n            <action android:name=\\\"android.intent.action.VIEW\\\" />\\n\\n            <category android:name=\\\"android.intent.category.DEFAULT\\\" />\\n            <category android:name=\\\"android.intent.category.BROWSABLE\\\" />\\n\\n            <data android:scheme=\\\"https\\\" />\\n            <data android:host=\\\"link.varickandvandam.com\\\" /> <!-- Change this one to your link domain, leaving the others as they are -->\\n            <data android:pathPrefix=\\\"/click/\\\" />\\n        </intent-filter>\\n</activity>\",\n      \"language\": \"xml\"\n    }\n  ]\n}\n[/block]\nThis intent filter will listen on links being clicked that:\n- Have the scheme https\n- Are hosted on `link.varickandvandam.com`, and\n- Are prefixed by the string `/click`. This is important, as all Sailthru links are prefixed by `click`, and we don't want our intent filter to capture the wrong link by accident.\n\nAfter that, we'll need to configure our receiving activity - in this case it's `MainActivity` -  to handle these links, using the `Carnival.handleSailthruLink` method. We recommend calling this from the `Activity.onCreate()` method:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// In class MainActivity\\n:::at:::Override\\npublic void onCreate(Bundle savedInstanceState) {\\n    super.onCreate(savedInstanceState);\\n    Intent intent = getActivity().getIntent();\\n    if (isSailthruLinkIntent(intent)) {\\n        Uri sailthruLinkUri = intent.getData();\\n        Uri canonicalUri = Carnival.handleSailthruLink(sailthruLinkUri, null);\\n        doStuffWithUri(canonicalUri);\\n    } else {\\n      // Handle other sorts of intents\\n    }\\n}\\n\\nprivate Boolean isSailthruLinkIntent(Intent intent) {\\n  return intent.getAction().equals(Intent.ACTION_VIEW) &&\\n    intent.getScheme() != null && intent.getScheme().equals(\\\"https\\\") && // It's a web link\\n    intent.getData().getHost().equals(\\\"link.varickandvandam.com\\\");\\n}\\n\\nprivate void doStuffWithUri(Uri uri) {\\n  // ... start the right activity for this sort of link, or display the right fragment, etc\\n}\\n\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\nNote that though `Carnival.handleSailthruLink` can take a completion handler, we've just set it as null, as it's mostly only useful for debugging.\n\n## iOS\nTo properly handle Universal Links, your app will need to implement the `UIApplicationDelegate:application:continueUserActivity:restorationHandler:` method in its `AppDelegate`. When an app is opened with a Universal Link, this method is called with an instance of `NSUserActivity`, which will have a the URL the user clicked on as its `webpageURL` property. With this in mind, we can implement this method:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {\\n    let canonicalUrl;\\n    if let incomingURL = userActivity.webpageURL {\\n        if incomingURL.host! == \\\"link.yourdomain.com\\\" { // It's a Sailthru link, so we'll have to decode it\\n            canonicalUrl = Carnival.handleSailthruLink(incomingURL)\\n        } else { // We can handle this one without calling Carnival\\n            canonicalUrl = incomingURL\\n        }\\n\\n        routeToViewForURL(canonicalUrl) // Go to the correct view in the app for the content tapped on\\n        return true\\n    }\\n    return false;\\n}\",\n      \"language\": \"swift\",\n      \"name\": \"AppDelegate.swift\"\n    },\n    {\n      \"code\": \"- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {\\n    NSURL *routingURL;\\n    if ([continueUserActivity webpageURL] != nil) {\\n        NSURL *incomingURL = [continueUserActivity webpageURL];\\n        if ([[incomingURL host] isEqualToString:@\\\"link.yourdomain.com\\\"]) {\\n            routingURL = [Carnival handleSailthruLink:incomingURL];\\n        } else {\\n            routingURL = incomingURL;\\n        }\\n        [self routeToViewForURL:routingURL];\\n        return true;\\n    }\\n    return false;\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"AppDelegate.m\"\n    }\n  ]\n}\n[/block]\nNote that the method `routeToViewForURL` is just a placeholder, as different apps will have different ways of parsing web URLs into App views.","excerpt":"","slug":"handling-sailthru-links","type":"basic","title":"Handling Sailthru Links"}

Handling Sailthru Links


When email is sent from Sailthru Email Manager, we go through the links in the templates, and then replace them with redirects, with domains of your choosing, generally something in the form of `http://link.yourdomain.com`. This allows us to track tap-through metrics on emails and measure your success. For example, Sailthru maintains a fake online clothing store, Varick and Vandam, as a demonstrator platform. Links in emails from V+V will look something like this: `https://link.varickandvandam.com/click/13071627.1/aHR0cHM.....`, but will redirect to: `https://varickandvandam.com/products/...`, referred to here as the "canonical url". Sailthru Mobile SDK can be configured to handle these links and to open your app whenever a certain type of link is clicked. The SDK methods - called `handleSailthruLink` on both platforms - will do two things: 1. Take the encoded `link.yourdomain.com/click/...` link and returns its canonical destination - the link that was entered into the template on Sailthru. 2. Ping the link domain, meaning that tapthrough analytics will be preserved on the Sailthru platform.. Make sure you've followed the steps in the [Sailthru Docs](https://getstarted.sailthru.com/account/management/apple-ios-app-universal-links/) before starting this guide - it corresponds to the "Link Parsing and Tracking" section. # Handling Universal Links From a Sailthru Email ## Android Android can be configured to handle links from Sailthru emails without any further configuration on the Sailthru platform, using Android's deep-linking functionality. This means that when clicking on links in emails, users will be prompted which app to open the link with - example below. [block:image] { "images": [ { "image": [ "https://files.readme.io/842bda5-app-disambiguation_2x.png", "app-disambiguation_2x.png", 500, 1010, "#5b5251" ], "caption": "Source: https://developer.android.com/training/app-links/verify-site-associations" } ] } [/block] [block:callout] { "type": "info", "body": "We're currently investigating the best way to integrate App Links - which would remove the need for this disambiguation screen - into the Sailthru platform.", "title": "What about App Links?" } [/block] We're going to continue to use Varick and Vandam as our example - it's link domain is `https://link.varickandvandam.com`. To route these links to our V+V app, we'll need to define an intent filter on our `MainActivity` in our `AndroidManifest.xml`. [block:code] { "codes": [ { "code": " <activity\n \tandroid:name=\".MainActivity\">\n <intent-filter>\n <action android:name=\"android.intent.action.VIEW\" />\n\n <category android:name=\"android.intent.category.DEFAULT\" />\n <category android:name=\"android.intent.category.BROWSABLE\" />\n\n <data android:scheme=\"https\" />\n <data android:host=\"link.varickandvandam.com\" /> <!-- Change this one to your link domain, leaving the others as they are -->\n <data android:pathPrefix=\"/click/\" />\n </intent-filter>\n</activity>", "language": "xml" } ] } [/block] This intent filter will listen on links being clicked that: - Have the scheme https - Are hosted on `link.varickandvandam.com`, and - Are prefixed by the string `/click`. This is important, as all Sailthru links are prefixed by `click`, and we don't want our intent filter to capture the wrong link by accident. After that, we'll need to configure our receiving activity - in this case it's `MainActivity` - to handle these links, using the `Carnival.handleSailthruLink` method. We recommend calling this from the `Activity.onCreate()` method: [block:code] { "codes": [ { "code": "// In class MainActivity\n@Override\npublic void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n Intent intent = getActivity().getIntent();\n if (isSailthruLinkIntent(intent)) {\n Uri sailthruLinkUri = intent.getData();\n Uri canonicalUri = Carnival.handleSailthruLink(sailthruLinkUri, null);\n doStuffWithUri(canonicalUri);\n } else {\n // Handle other sorts of intents\n }\n}\n\nprivate Boolean isSailthruLinkIntent(Intent intent) {\n return intent.getAction().equals(Intent.ACTION_VIEW) &&\n intent.getScheme() != null && intent.getScheme().equals(\"https\") && // It's a web link\n intent.getData().getHost().equals(\"link.varickandvandam.com\");\n}\n\nprivate void doStuffWithUri(Uri uri) {\n // ... start the right activity for this sort of link, or display the right fragment, etc\n}\n", "language": "java" } ] } [/block] Note that though `Carnival.handleSailthruLink` can take a completion handler, we've just set it as null, as it's mostly only useful for debugging. ## iOS To properly handle Universal Links, your app will need to implement the `UIApplicationDelegate:application:continueUserActivity:restorationHandler:` method in its `AppDelegate`. When an app is opened with a Universal Link, this method is called with an instance of `NSUserActivity`, which will have a the URL the user clicked on as its `webpageURL` property. With this in mind, we can implement this method: [block:code] { "codes": [ { "code": "func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {\n let canonicalUrl;\n if let incomingURL = userActivity.webpageURL {\n if incomingURL.host! == \"link.yourdomain.com\" { // It's a Sailthru link, so we'll have to decode it\n canonicalUrl = Carnival.handleSailthruLink(incomingURL)\n } else { // We can handle this one without calling Carnival\n canonicalUrl = incomingURL\n }\n\n routeToViewForURL(canonicalUrl) // Go to the correct view in the app for the content tapped on\n return true\n }\n return false;\n}", "language": "swift", "name": "AppDelegate.swift" }, { "code": "- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {\n NSURL *routingURL;\n if ([continueUserActivity webpageURL] != nil) {\n NSURL *incomingURL = [continueUserActivity webpageURL];\n if ([[incomingURL host] isEqualToString:@\"link.yourdomain.com\"]) {\n routingURL = [Carnival handleSailthruLink:incomingURL];\n } else {\n routingURL = incomingURL;\n }\n [self routeToViewForURL:routingURL];\n return true;\n }\n return false;\n}", "language": "objectivec", "name": "AppDelegate.m" } ] } [/block] Note that the method `routeToViewForURL` is just a placeholder, as different apps will have different ways of parsing web URLs into App views.