Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Link directly to custom protocol handlers for mobile, without worrying if the user has your App installed.Licensed under the WFTPL http://www.wtfpl.net/txt/copying/
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>App Redirection</title>
</head>
<body>
<!--
NOTE: This was a great hack in days gone by, but now both Apple and Google have improved their support for custom
protocol handlers.
# References
* Handle Open URL: http://handleopenurl.com/
* iOS Smart App Banners: https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/PromotingAppswithAppBanners/PromotingAppswithAppBanners.html
* Android Intents: https://developer.android.com/reference/android/content/Intent.html
* On the desktop you still need to make a custom plugin to do the right thing, or save some sort of user prefernce.
-->
<!-- iframe used for attempting to load a custom protocol -->
<iframe style="display:none" height="0" width="0" id="loader"></iframe>
<script>(function(){
// For desktop browser, remember to pass though any metadata on the link for deep linking
var fallbackLink = 'http://example.com/my-web-app/'+window.location.search+window.location.hash;
// Simple device detection
var isiOS = navigator.userAgent.match('iPad') || navigator.userAgent.match('iPhone') || navigator.userAgent.match('iPod'),
isAndroid = navigator.userAgent.match('Android');
// Mobile
if (isiOS || isAndroid) {
// Load our custom protocol in the iframe, for Chrome and Opera this burys the error dialog (which is actually HTML)
// for iOS we will get a popup error if this protocol is not supported, but it won't block javascript
document.getElementById('loader').src = 'custom-protocol://my-app'+window.location.search+window.location.hash;
// The fallback link for Android needs to be https:// rather than market:// or the device will try to
// load both URLs and only the last one will win. (Especially FireFox, where an "Are You Sure" dialog will appear)
// on iOS we can link directly to the App Store as our app switch will fire prior to the switch
// If you have a mobile web app, your fallback could be that instead.
fallbackLink = isAndroid ? 'https://play.google.com/store/apps/details?id=com.mycompany.myapp' :
'itms-apps://itunes.apple.com/app/my-app/idxxxxxxxx?mt=8' ;
}
// Now we just wait for everything to execute, if the user is redirected to your custom app
// the timeout below will never fire, if a custom app is not present (or the user is on the Desktop)
// we will replace the current URL with the fallbackLink (store URL or desktop URL as appropriate)
window.setTimeout(function (){ window.location.replace(fallbackLink); }, 1);
/*
Q&A
I have a native desktop app as well, how do I link to a custom protocol handler on the desktop?
IE Only: http://msdn.microsoft.com/en-us/library/ms537512.aspx#Version_Vectors
All Other Browsers: Use a custom plugin like iTunes does: http://ax.itunes.apple.com/detection/itmsCheck.js
*/
})();</script>
</body>
</html>
@sivicencio

This comment has been minimized.

Copy link

commented Apr 29, 2013

This script really helped me to get a job done. An iframe was the key. Thanks!

@jcaveman

This comment has been minimized.

Copy link

commented Jul 1, 2013

So far I can only get this to work in the non-Chrome default Android browser (pre Jelly Bean). In Chrome, the custom protocol is not picked up by the app when I set it as the source of the iframe. Any thoughts?

@cjse

This comment has been minimized.

Copy link

commented Oct 24, 2013

This looks great, any chance you could add some license information to the gist?

@jimadamsss

This comment has been minimized.

Copy link

commented Jan 16, 2014

This method is similar to others I have seen but it has a major flaw in that the timer will still run when you come back to the page later. Thus you always end up on your fallback page which is, in the example, the app store. Not quite the best user experience. It would be better if we could figure out how to kill the timer totally once it complete's once.

@martin-sweeny

This comment has been minimized.

Copy link

commented Jul 7, 2014

This answer on SO describes one way to avoid the problem @jimadamsss describes

@bsurtz

This comment has been minimized.

Copy link

commented Jul 22, 2014

Did anyone find a fix for the =Chrome issue? thanks

@aaron-schneider

This comment has been minimized.

Copy link

commented Aug 7, 2014

@bsurtz - Check out the Chrome page. You have to use something different for Chrome 25 and later.

@colaboradores

This comment has been minimized.

Copy link

commented Sep 17, 2014

thanks for your comment @aaronschneiderbelkin. I'm now using two separated links:

  • procotol://host/ (for iOS)
  • intent://host/#Intent;scheme=protocol;package=com.domain.apppackage;end (for Android browsers)

On the end, I have something like this:

function openApp() {
            var isiOS = navigator.userAgent.match('iPhone') || navigator.userAgent.match('iPod');
            var isAndroid = navigator.userAgent.match('Android');

            if (isiOS) {
                document.getElementById('loader').src = "{{ mobileURI }}";
            }
            else if (isAndroid) {
                window.location = "{{ androidIntentURI }}";
            }
        }

        (function(){
            openApp();
        })();
@rahuljodhani

This comment has been minimized.

Copy link

commented Oct 18, 2014

Hello colaboradores,
Iphone is working fine, but in android i want to check only the application is installed or not, in your code if not installed then redirect to play store. Is there any way to stay there if application is not installed

@fengyuanyang

This comment has been minimized.

Copy link

commented Nov 3, 2014

I wonder same solution as rahuljodhani!!
any possibility to use intent and not redirect to google play if application is not installed???

@andresmafra

This comment has been minimized.

Copy link

commented Jan 23, 2015

As our friends rahuljodhani and fengyuanyang asked, can we checked if the app is instaled on Android and iOS?

@delconis

This comment has been minimized.

Copy link

commented Apr 17, 2015

For Chrome I have only found the following 2 ways to open the app

window.location = "customprotocol://"
window.location.assign('customprotocol://')

If the app is not installed it will land you on a About:Blank or a page that says the protocol isn't registered / does not exist.

What I found to work is setting the window.location to my protocol and then doing a settimeout for a very short time 5ms or less that will attempt the fallback url such as the apps Google Play listing. The issue I still encounter with this is the app will launch and the Default App launcher is started 50% of the time behind the app that just opened and the other 50% in front of the app asking what I want to do with the google play URL as in Open in the Google Play app or use the webbrowser.

While that works it disrupts UX which isn't good.

@delconis

This comment has been minimized.

Copy link

commented Apr 17, 2015

@andresmafra You can via native code but via javascript I have not found a way.

@anil1712

This comment has been minimized.

Copy link

commented May 5, 2015

I am getting the same issue @delconis in android (till KitKat) but in Andoird Lollipop version its not working any more. Its not opening our application first, its was asking to open the play store url and there is no way to skip this step. Please give me some solution for this.

FYI: I have tested it in Moto G 2nd generation(Lollipop)

@cedriclombardot

This comment has been minimized.

Copy link

commented Jun 10, 2015

Hi, I have an issue with safari, when my app is not installed, i got an alert saying can't open page before redirecting to store url

@YogendraChauhan

This comment has been minimized.

Copy link

commented Jul 10, 2015

Hi Cedriclombardot,
I have modified the above code, which works fine in all mobile and desktop browsers including chrome & it won't give you any alert msg, kindly use the below piece of code 😄

<!doctype html>

<title>App Redirection</title> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script> <script> function openApp() { //see if our window is active window.isActive = true; $(window).focus(function() { this.isActive = true; }); $(window).blur(function() { this.isActive = false; });
    // For desktop browser, remember to pass though any metadata on the link for deep linking
    var fallbackLink = 'your website url';
    // Simple device detection
    var isiOS = navigator.userAgent.match('iPad') || navigator.userAgent.match('iPhone') || navigator.userAgent.match('iPod'),
        isAndroid = navigator.userAgent.match('Android'), isWindow = navigator.userAgent.match('Windows Phone');
        // Mobile
        if (isiOS || isAndroid || isWindow) {
            // Load our custom protocol in the iframe, for Chrome and Opera this burys the error dialog (which is actually HTML)
            // for iOS we will get a popup error if this protocol is not supported, but it won't block javascript

            var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
            if (isMobile) {
                var isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
                var isSafari = /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
                if (isChrome){
                    window.location  = 'custom-protocol://my-app';  
                }else
                {
                    document.getElementById('loader').src = 'custom-protocol://my-app';
                }
            }

            // The fallback link for Android needs to be https:// rather than market:// or the device will try to 
            // load both URLs and only the last one will win. (Especially FireFox, where an "Are You Sure" dialog will appear)
            // on iOS we can link directly to the App Store as our app switch will fire prior to the switch
            // If you have a mobile web app, your fallback could be that instead. 
                  fallbackLink = isAndroid ? 'https://play.google.com/store/apps/details?id=com.mycompany.myapp' :
                             'itms-apps://itunes.apple.com/app/my-app/idxxxxxxxx?mt=8' ;
        }

        // Now we just wait for everything to execute, if the user is redirected to your custom app
        // the timeout below will never fire, if a custom app is not present (or the user is on the Desktop)
        // we will replace the current URL with the fallbackLink (store URL or desktop URL as appropriate)
        window.setTimeout(function (){
            if (window.isActive) { 
                window.location.replace(fallbackLink);
            } 
        }, 1000);
        /*
          Q&A
        I have a native desktop app as well, how do I link to a custom protocol handler on the desktop?
        IE Only: http://msdn.microsoft.com/en-us/library/ms537512.aspx#Version_Vectors
        All Other Browsers: Use a custom plugin like iTunes does: http://ax.itunes.apple.com/detection/itmsCheck.js
        */
    };

    window.onload = function()
    {
        var button = document.getElementById("button");
        button.onclick = function()
        {
            openApp();
        };
    };
</script> <style type="text/css"> #button { border:1px solid #7c5b2b; -webkit-border-radius: 3px; -moz-border-radius: 3px;border-radius: 3px;font-size:12px;font-family:arial, helvetica, sans-serif; padding: 10px 10px 10px 10px; text-decoration:none; display:inline-block;text-shadow: -1px -1px 0 rgba(0,0,0,0.3);font-weight:bold; color: #FFFFFF; background-color: #a67939; background-image: -webkit-gradient(linear, left top, left bottom, from(#a67939), to(#845108)); background-image: -webkit-linear-gradient(top, #a67939, #845108); background-image: -moz-linear-gradient(top, #a67939, #845108); background-image: -ms-linear-gradient(top, #a67939, #845108); background-image: -o-linear-gradient(top, #a67939, #845108); background-image: linear-gradient(to bottom, #a67939, #845108);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#a67939, endColorstr=#845108); } </style> <iframe style="display:none" height="0" width="0" id="loader"></iframe>
Open App
@mdeora

This comment has been minimized.

Copy link

commented Sep 19, 2015

doesn't work with iOS9

@luigisaggese

This comment has been minimized.

Copy link

commented Sep 22, 2015

iOS 9 fail 👎

@curbsaleem

This comment has been minimized.

Copy link

commented Sep 28, 2015

doesn't work with iOS9

@ebsaral

This comment has been minimized.

Copy link

commented Oct 27, 2015

document.getElementById('loader').src = '{{ url }}';

this does not work in iOS 9

@jonesmac

This comment has been minimized.

Copy link

commented Jun 9, 2017

This mostly worked for me...major gotcha for iOS is that you cannot start your custom protocol with a number. Evidently that get's url encoded and the redirect will fail since it would find that registered with an app. Specifically, I noticed that 9 gets reencoded as %39

@maquannene

This comment has been minimized.

Copy link

commented Mar 12, 2018

Safair has already not support call up app by iframe after iOS9.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.