The Official Ionic Blog

Build amazing native and progressive web apps with HTML5

deeplinks-header

I was doing some research on common challenges developers face while building their Ionic apps, and one of the things that kept coming up was Deeplinking.

For those not familiar, Deeplinking makes it possible to direct a user to content hidden deep in a native app, whether that’s from another app or a web browser. As web developers we live and breathe deep-linking because that was the major innovation of the web. Native apps are only just catching up, and so it hasn’t always been obvious how to link into an app the way we link into a webpage.

Deeplinking as a concept has evolved heavily over the last few years, with mobile devices going from supporting custom URL schemes (like instagram://) to now opening native apps in response to navigation to URLs (like amazon.com). Additionally, OS’s now support powerful ways to index and search data inside of native apps. Each evolution in the deeplinking feature set has caused churn in both what mobile devices support and what they no longer support, making it a tall order to keep up.

To help Ionic developers deeplink more easily, we are excited to announce a new, official way to deeplink into both Ionic 1 and Ionic 2 apps (and non-ionic Cordova apps): the Ionic Deeplinks Plugin along with Ionic Native 1.3.0. Let’s take a look at how it works:

Choosing a Deeplink

The first thing we need to do is figure out what kind of deeplink we want our app to respond to. Let’s say we run a Hat Shop and we have a website version of our store where we display our many fancy Hats. A URL to one of those Hats might look like https://ionic-hats.com/hats/very-nice-hat.

We can actually launch our app when someone navigates to this URL on Android or iOS and display the app version of the Hat product page. Additionally, let’s say we want to enable a custom URL scheme of the form ionichats://app/hats/very-nice-hat.

Now that we have our URL scheme, website, and deeplinking path decided, let’s install the Deeplinks Plugin:

Installing Ionic Deeplinks

The Ionic Deeplinks plugin requires some variables in order to get set up properly:

cordova plugin add ionic-plugin-deeplinks --variable URL_SCHEME=ionichats --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOST=ionic-hats.com

In the install command, we provide the custom URL scheme we want to handle (ionichats), the host domain we will respond to (ionic-hats.com) and the host protocol we will listen for, which 99% of the time will be https as it’s required on iOS and Android.

We’re almost ready to handle deeplinks, we just need to configure Universal Links on iOS and App Links on Android 6.0 so our app can open when navigating to ionic-hats.com.

Configuring Universal Links (iOS) and App Links (Android)

To configure iOS and Android, we need to enable Universal Links for iOS, and App Links for Android (6.0+). This process is primarily done on the server side of your website. You’ll publish a specific json file for iOS and one for Android, ensure your site is using HTTPS, and then configure your app to open in response to links to that domain.

For Android, it pretty much Just Works from the plugin install above. However, for iOS, you’ll then enable the domain in the Associated Domains section of your entitlements, with the form applinks:yourdomain.com:

Screenshot 2016-06-10 11.38.33

You may also need to enable the entitlement from the Developer center for your app.

Testing Deeplinks

Assuming we have everything set up correctly (or at least we think we do), it’s time to start testing Deeplinks.

On Android, this process is a snap. We can boot up our emulator or device, and send a deeplink intent directly to the app from the command line:

adb shell am start -a android.intent.action.VIEW -d "ionichats://app/hats/very-nice-hat" io.ionic.hats

Replacing the custom URL scheme and package name with your respective values. If everything was configured properly, our app will open, regardless of whether it was running or not!

On iOS, I find it’s easier to test on the simulator. Start your app from X Code, go into the Contacts app and add a URL for one of the fake contacts so it looks like this:

Screenshot 2016-06-10 12.02.20

Tap on the link and our app should open!

To test Universal Links on iOS, we can’t use the simulator (as far as I know, I couldn’t get it to work). Instead, we run our app on our iOS device, open safari and navigate to our URL. In Safari, when open on a page that has Universal Links enabled, we can swipe down to expose this bar:

IMG_8549

Universal Links on iOS are finicky. If you adjust the manifest, you’ll need to uninstall and reinstall the app in order for iOS to fetch it again, otherwise it’ll cache for 24 hours.

If you are sure you’ve got everything configured properly but it’s still not working, double check your bundle identifer matches up with the manifest, you have the proper entitlements, you’re using HTTPS, you are sending application/pkcs7-mime as the content type for the manifest file, and try removing/installing the app again. (Here’s an example express.js route for serving the manifest for iOS)

Responding to Deeplinks

Getting the app configured is by far hardest part, and Universal Links is particularly finicky.

Now that we (hopefully) have everything configured, it’s time to actually respond to our deeplinks!

I’m going to assume you’re using Ionic 2 with Ionic Native which comes with convenient wrappers around many Cordova plugins to add Observable, Promise, and TypeScript support. Ionic Native works in any Cordova project regardless of whether it’s using Ionic 1/2, Angular 1/2, or TypeScript. See the Deeplinks README for examples for Ionic 1 and non-Ionic projects.

To start, we define a set of deeplink routes we want to listen for. If one matches against an incoming deeplink, we can automatically navigate in our app to display the appropriate content.

In Ionic 2, we can conveniently navigate with a specific instance of a Nav Controller, though we can also use the plain route method to handle the navigation ourselves (for example, if we want to do a very custom deeplink navigation).

import {Component, ViewChild} from [email protected]/core';
import {Platform, Nav, ionicBootstrap} from 'ionic-angular';
import {Deeplinks} from 'ionic-native';

import {AboutPage} from './pages/about/about';
import {HatDetailPage} from './pages/hat/hat';

@Component({
  template: '<ion-nav [root]="rootPage"></ion-nav>',
})
class MyApp {
  @ViewChild(Nav) nav:Nav;

  constructor(private _platform: Platform) {}

  ngAfterViewInit() {
    this._platform.ready().then(() => {
      Deeplinks.routeWithNavController(this.nav, {
        '/about-us': AboutPage,
        '/hats/:hatId': HatDetailPage
      });
    });
  }
});

ionicBootstrap(MyApp);

Inside of our HatDetailPage, we can grab the hatId from the route:

export class HatDetailPage {
  hatId: string;

  constructor(public nav: NavController, private _params: NavParams) {
    this.hatId = _params.get('hatId');
  }
}

Take a look at a simple demo for Ionic 2 and one for Ionic 1 to see how the two differ.

Conclusion

That’s pretty much it! A lot of the work happens behind the scenes to make sure our app opens from both custom URL schemes and Universal Links, and that it functions from a cold boot (a deeplink received while the app is not running) as well as while running.

Many people often confuse deeplinking with routing. The two are similar but subtly different. Routing helps an app navigate within itself while it’s running, as well as possibly from external links (especially in a traditional web app). Deeplinking, by contrast, is not used within the app for its own navigation, it’s only used to display a specific piece of content triggered from an external request. In that sense, you would not use the Deeplinks class for your own routing, only to enable the app to be launched from elsewhere on a user’s device.

If you give the new plugin a try, let us know what you think. We are looking for feedback on how to make this easier, and plan to make Deeplinking a major feature in Ionic apps going forward.

  • http://www.raymondcamdencom/ Raymond Camden

    I think maybe you skipped over something important in the beginning. You say:

    “A URL to one of those Hats might look like https://ionic-hats.com/hats/very-nice-hat.

    We can actually launch our app when someone navigates to this URL on Android or iOS and display the app version of the Hat product page. ”

    Ok, so given that I’ve built my hat site, how exactly do I launch the custom URL when you’ve requested this URL? And even if there is some magical way of doing that (maybe a META tag?), isn’t the “do you want to open this in our app” thing pretty annoying?

    I’m assuming I’m just misreading you though. 😉

    • yesimahuman

      Hey Ray, if I understand the question, this is on you to decide how to handle this. You can either automatically redirect (assuming the user hasn’t disabled that functionality for your app, by default it’s on), or you can have a button to “open in app.”

      For the first example, Amazon does this if you hit a product link from mobile (for example, in a google search). They don’t give you the option of opening in the app, they just do it. This is configured (on iOS) through this file: http://amazon.com/apple-app-site-association. You don’t actually “do” anything, iOS does by matching paths in this file to paths navigated to in the browser.

      Imgur, by contrast, gives you the option to open in the app instead of doing it automatically: http://m.imgur.com/gallery/FaBrG, though I believe that was a recent change as they used to automatically open it. It might also be the case that this was a local setting that I forgot I set (universal links is incredibly finicky and if you click the upper-right label in the status bar after a redirect it will go to safari always instead).

      If you look at the imgur site, they redirect to the app through a custom URL scheme link: imgur://imgur.com/gallery/FaBrG

      Did that help? I believe Smart App Banners through meta tags are redundant with universal links, but I might be wrong (would love someone else to chime in!) I’m noticing fewer of them in the Universal App Links era.

      Also, you never trigger the user to the app of your own accord (except through browser exploits). It’s always either a passive operation, or giving the user a link to click.

      • http://www.raymondcamdencom/ Raymond Camden

        Yep – it does. Thank you for the examples!

  • Young Park

    wow. awesome resource!

  • imakkie

    Great work guys 🙂

  • http://julienrenaux.fr/ Julien Renaux

    What about the apple-app-site-association file for UL?

    • yesimahuman

      The link to the apple docs explain it all, but perhaps worth us doing our own tutorial…

  • Mike M

    Great article! thanks Max for sharing! I know I’ve spent time on this, and knowing it is all from the ionic stack will make it that much easier, especially on integrations with other ionic services like ionic push! We all love simplicity!

  • Jeremy S

    Great tutorial… FYI, the Ionic1 DeepLinks Demo LINK (https://github.com/driftyco/ionic1-deeplinks-demo) doesn’t work

  • Keith D. Moore

    Is deferred deep linking going to be supported through Ionic Services? I’m talking about the ability for a user to click a link that will take them to the appropriate app store and allow the user to download the app. Then when the app any request params are retrieved from a service. This is what https://branch.io/ does. They do a ton of other things but this is one service they provide.

    • yesimahuman

      Yes, it’s coming!

      • Steven Sanders

        How far off do you think that is?

      • Jet Balagtas ✈

        do you happen to already have a rough ETA on this?

  • http://bcliks.blogspot.in/ bCliks

    ionic 1 demo link not working. Please check and update. https://github.com/driftyco/ionic1-deeplinks-demo)

  • Steve Melnikoff

    Hi Max, echoing the other comments: great article! Quick question about the apple-app-site-association manifest file. I was reading Apple’s docs which say that manifest name should be simply: ‘apple-app-site-association’ without a file extension, and (for iOS 9+) to be sent as ‘application/json’. Do I have something confused when I studied your express.js example gist?

    • yesimahuman

      Hey Steve. You are correct that there is no file extension. However, on the mimetype, I got conflicting info on this, and struggled to get iOS to detect the universal link when using the JSON mimetype. From other posts online, I read to try the application/pkcs7-mime mimetype, which could be a hold over from when the file required a certificate to be attached. Let me know if you have any additional info or learning on this.

  • Davo

    Thx for the awesome plugin and tutorial! Just gonna leave two comments here, in case someone gonna struggle as much time as I did on following remarks. 1. If you target iOS9 and use https, you don’t need to sign your apple-app-site-association. Furthermore the file should not, in that case, returned with a header content “application/pkcs7-mime” but “application/json”. 2. If you define for example an URL_SCHEME “mytest” and you face the same problem as I did, that the application didn’t opened, when you perform the simulator “Contacts” test described above, try to modify your link from “mytest://” to “mytestapp://”, that solved my issue

  • http://www.codingandclimbing.co.uk Dave

    Just tried this on Android and it doesn’t pass through the parameter at all, just undefined. Must be something missing from your walkthrough.

  • Daniel C

    As seen in the screenshot above, the “open in app” toolbar that appears in safari lacks the application name. I have the exact same problem. Do you know where the app name is pulled from? I’ve retraced all the steps and don’t think I’ve missed anything.

  • uzer_t1

    Android was pretty easy to setup, iOS was painful.
    I would recommend adding a backend example in these tutorials so that ionic users can easily set up the entire process.

    I also did not use ‘application/pkcs7-mime’, but instead used ‘application/json’

    For any node / express users this is what worked for me.

    var assetlinks = fs.readFileSync(__dirname + ‘/public/assetlinks.json’);
    var applesite = fs.readFileSync(__dirname + ‘/public/apple-app-site-association’);

    app.get(‘/.well-known/assetlinks.json’, function(req, res, next) {
    res.set(‘Content-Type’, ‘application/json’);
    res.status(200).send(assetlinks);
    });

    app.get(‘/.well-known/apple-app-site-association’, function(req, res, next) {
    res.set(‘Content-Type’, ‘application/json’);
    res.status(200).send(applesite);
    });

    • Ari S

      Is this your code on the server side?

      • mark candaras

        yes

  • jasonwastaken

    Thanks for the great article, Max! Question: what will this experience be like for users on pre-6.0 Android?

  • Tiago

    Thank you for the tutorial! That was awesome. I have only one question. I have my login logic inside my app.component platform.ready().then().. so I need to know if the user is using a Deeplink to open the app or not. Because I need to decide which logic to execute, the deep link routing or the normal logic. For the life of me I can’t seem to find a simple solution for this.

    Thank you so much

  • vishnu

    After Install the plugin . I’m getting the error Unknown provider: $cordovaDeeplinksProvider

    • Andres

      were you able to resolve this? I’m getting the same thing.

      • Ladna Meke

        I’m guessing this plugin shouldn’t work in the browser and you’re testing in browser. Wrap the plugin initialization in function that checks if we’re in cordova or not

    • Aleksander Kostov

      Any luck?

    • Jacques De Villiers

      I also have the same thing. Any help on this would be much appreciated

  • queejie

    There are a number of unanswered questions about getting this to work for a web based app that are not addressed here, in the docs, or in the forum. (In the forum, questions simply go unanswered.) It would be great if there were more documentation or a complete example app that allowed for true URL based navigation. Or, if it isn’t supported, it should be stated clearly.

    I know that one problem is that the evolution of the Angular 2 router has resulted in constantly changing conditions, so there are a number of information pages on the net that are now incorrect. These decoys can really be a time sink, both in reading and in trying to follow obsolete advice.

    Following the example in this blog post, I’ve been able to see URLs generated in the location bar, but entering those same URLs manually results in 404’s.

    Here are some of the unanswered questions:

    1.) Is installing the plugin necessary for apps that are to be used on the web platform only?

    2.) Does one need to “add browser” platform to get this to work properly, or does the www directory that one uses with ionic serve work as well?

    3.) If one does need to add the plugin to get it to work fully for the browser platform, how can the dev server be distinguished from that used for production? The command line appears to hard code a domain name.

    4.) The DeepLinking document page states that it isn’t a router, that it provides some sort of mysterious breadcrumb navigation. I have read and re-read it multiple times without understanding what it is. If it isn’t a router, what mechanism does support the ability to go to a specific page in an app?

    Thanks for reading. I have given myself until the end of the day to make a yea-or-nay decision on using Ionic, or switching back to Angular alone. It would be great to know there is some support, somewhere, for these kinds of questions.

    • jpradeep

      I have exact same questions that queejie asked 4 months back here. Web-based app and deep-linking are both essential for a critical requirement in the app. I use Ionic 2 for other applications and hope that it is possible to use it for this as well. Kindly share if anybody has figured this out.

  • Janelle Thorn Contreras

    For Android, I was able to lunch my app through the emulator when I type into terminal the command you have in the tutorial:

    adb shell am start -a android.intent.action.VIEW -d theurlIsetup://

    However, if I open up chrome in the emulator and type theurlsetup:// into it, I get ‘This site can’t be reached in the browser instead of the app opening up. Same if I download the app to my device and try it. Ideas on where to look? I feel like I am missing something really simple.

  • Philip Chen

    Hello,I have download the ionic 1 example and restore the plugins. After I type ‘ionic serve’ in terminal, the demo app is running on the web. My question is. What should I do in order to execute the Deeplinking functions. Thank you….

  • Brad

    can anyone explain to me where the “routes.js” plays its role? I feel like I have done everything but still no success… or is it not needed if I have a real server only needed for a Node server?

  • Oscar Manuel Aguillon

    hi, the command add sell am … wokrs well but when we put te last parametter (adb shell am start) doesnt work beacuse we dont know what is it or where can we get it.