Deep App linking and changes to Chrome on Android

I am fascinated by deep app linking. On the web, we've never had the problem of identifying a piece of content and loading the resource behind it. I say we have never had a problem, there have been many different ways to link to content, each with some trade-off, linking however has never been a problem.

Native on the other-hand hasn't got a consistent way across platforms to link from the web to an app or even app to app: iOS uses protocol schemes, Android uses Intents, Windows phone uses protocol schemes as well, and we've seen a number of start-ups appear to try and smooth cross platform linking system (I'll go into why I think this is shaky ground to base a business on in a future post).

A perennial problem that both web and native have is that you don't actually know if that target exists on other end of the link. On the non-HTTPS web it's painfully common for ISP's to intercept lack of target on a link for their own needs, whilst this interception doesn't happen for apps, instead there has been a gap left open for Deep App Linking companies to step in and try and solve the problem.

A question came up recently, all centered around a change we made to Chrome on Android that frustrated some developers, but, I believe will make users happy. You can see a quick summary on Tapstream:

TL;DR — sites were using a piece of functionality that we believe was broken in Chrome whereby when a user types in a URL in to the address bar and navigates to the web site, the site author would add a specially encoded link to the page and simulate a click on the link as follows:

<a href="intent:some-intent-string" id="clickTarget">Open in our app</a>
<script>
  window.onload = function() {
    var clickTarget = document.getElementById("clickTarget");
    var fakeMouseEvent = document.createEvent('MouseEvents');
    fakeMouseEvent.initMouseEvent("click", true, true, window,
        0, 0, 0, 20, 10, false, false, false, false, 0, null);

    clickTarget.dispatchEvent(fakeMouseEvent);
  };
</script>

The reason why sites do this in most cases is that they want to push all users to the native app. Personal philosophical issues aside, our position is clear:

We believe it was a bug that this could be circumvented, so we have fixed it.

So what works and what doesn't (or shouldn't work)?

Deep App Linking is an important concept and it hasn't changed much.

That's clear. But there are a lot of other scenarios including when:

This is probably the most common scenario, but it has a couple of logic paths in it.

I have created a quick chart that shows the intent: resolution (mostly because I wanted to test out).

Chrome will try to resolve the app, however if there is no app that can handle the Intent then Chrome will no-op and stay on the same page.

There is a way to mitigate this issue, use the intent: syntax to mitigate the issue. For example replace your custom-scheme: anchor with an intent:/#Intent;scheme=custom-scheme;... anchor.

When a user clicks a link with an HTTP or HTTPS protocol and is subsequently redirected to an intent: based URL, Chrome will consider that a user triggered gesture and will allow the request to take place. This includes at least the two following scenarios:

Is there actually a problem?

Many Deep App Linking providers provide a service takes an HTTP URL, looks up what deep-link path the user should be sent to and then redirects the user to it. So for iOS it might turn the link into a protocol based URL and for Android it would turn the URL into a intent: based URL.

All that has changed is that if the user types in the URL then it will load the page and not allow the redirect to a non-HTTP and non-HTTPS URL to occur. Any other link navigation should be kept the same (i.e, click a link Android's intent picker should appear if an app can handle it).

Side Note: I am investigating if encoding &referrer=xyz into the package id is a bug or intended behavior.

I saw mention of S.browser_fallback_url. What is it and how can it help?

One huge issue for Deep App Linking on all platforms is that if there is no App installed then the user has nothing to go to. On Android, if you do it correctly, the user will be directed to the Play Store. This certainly helps users as at least they have something to do on the end of a link, however if you wanted the user to read some content or perform an actions it is highly likely that you have lost that conversion.

Chrome will now look for an Intent EXTRA named browser_fallback_url which when specified will be used as the URL that Chrome will load when it can't find the app that the developer wanted to launch.

A while ago I created some detailed guidance of the Intent syntax for web developers, we are yet to update that but here is a run down of different solutions.

Originally an anchor with the following href would try to launch the app com.google.zxing.client.android

intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end

If the app wasn't installed, the presence of the package=com.google.zxing.client.android would direct the user to the Play Store to install the app.

The recent addition is called "Browser Fallback URL" and has been made to allow you to instead direct the user to a URL, this is important if you want to ensure that you have a high conversion rate (remember huge numbers of users drop off at the app install phase) and ensure that the user has some content to read.

It is simple to implement. If you are using the intent: URL syntax, encode a S.browser_fallback_url= parameter in to the URL. The following example demonstrates a browser fall-back URL and shows you the referrer that will be shown if the App is not installed (hint it's the page that was the originator of the link click).

intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=http%3A%2F%2Fwww.whatismyreferer.com%2F;end

Demo

A good example of falling back to a real Web App with similar functionality:

intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=https%3A%2F%2Fqrsnapper.appspot.com%2F;end

Demo

If you rely on the referrer being sent with your package ID, you can use the S.browser_fallback_url as a workaround.

intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.google.zxing.client.android&%26referrer%3Dkinlan;end

Please note that the above is a workaround. The fallback URL should be used for falling back to content, not the Play Store.

TL;DR - What are the best practices for deep app linking?

This is my best practice and you have to understand that I am a webby at heart :)

Accuracy and Veracity

This information is "accurate" as of March 2015. As always this could change again in the future. Please leave a comment if you find an issue.

If you want to understand exactly how the URL handling works visit check out the shouldOverrideUrlLoading methodd

I lead the Chrome Developer Relations team at Google.

We want people to have the best experience possible on the web without having to install a native app or produce content in a walled garden.

Our team tries to make it easier for developers to build on the web by supporting every Chrome release, creating great content to support developers on web.dev, contributing to MDN, helping to improve browser compatibility, and some of the best developer tools like Lighthouse, Workbox, Squoosh to name just a few.

I love to learn about what you are building, and how I can help with Chrome or Web development in general, so if you want to chat with me directly, please feel free to book a consultation.

I'm trialing a newsletter, you can subscribe below (thank you!)