234

I'd like to have iOS to open URLs from my domain (e.g. http://martijnthe.nl) with my app whenever the app is installed on the phone, and with Mobile Safari in case it is not.

I read it is possible to create a unique protocol suffix for this and register it in the Info.plist, but Mobile Safari will give an error in case the app is not installed.

What would be a workaround?

One idea:

1) Use http:// URLs that open in any desktop browser and render the service through the browser

2) Check the User-Agent and in case it's Mobile Safari, open a myprotocol:// URL to (attempt) to open the iPhone app and have it open Mobile iTunes to the download of the app in case the attempt fails

Not sure if this will work... suggestions? Thanks!

User97693321
  • 3,336
  • 7
  • 45
  • 69
Martijn Thé
  • 4,674
  • 3
  • 29
  • 42
  • 4
    In the NYC subway, there is wifi by Boingo which provides you free access to WiFi if you download an application that they recommend. Once you download it, you go back to Safari and the browser detects if it was installed and then grant you access. Any idea how that's being done? – TommyG Oct 03 '13 at 00:23
  • 2
    Universal Links would now support this use case without any error message. Here's how to configure your domain and app: https://blog.branch.io/how-to-setup-universal-links-to-deep-link-on-apple-ios-9 – Alex Austin Nov 16 '15 at 05:14

13 Answers13

253

I think the least intrusive way of doing this is as follows:

  1. Check if the user-agent is that of an iPhone/iPod Touch
  2. Check for an appInstalled cookie
  3. If the cookie exists and is set to true, set window.location to your-uri:// (or do the redirect server side)
  4. If the cookie doesn't exist, open a "Did you know Your Site Name has an iPhone application?" modal with a "Yep, I've already got it", "Nope, but I'd love to try it", and "Leave me alone" button.
    1. The "Yep" button sets the cookie to true and redirects to your-uri://
    2. The "Nope" button redirects to "http://itunes.com/apps/yourappname" which will open the App Store on the device
    3. The "Leave me alone" button sets the cookie to false and closes the modal

The other option I've played with but found a little clunky was to do the following in Javascript:

setTimeout(function() {
  window.location = "http://itunes.com/apps/yourappname";
}, 25);

// If "custom-uri://" is registered the app will launch immediately and your
// timer won't fire. If it's not set, you'll get an ugly "Cannot Open Page"
// dialogue prior to the App Store application launching
window.location = "custom-uri://";
Nathan de Vries
  • 15,481
  • 4
  • 49
  • 55
  • 1
    I tried both ideas both obviously work, and the best solution would be to combine them. The dialogue / cookie for first-timers and the timer to catch the case where the app has been deinstalled. (Perhaps trigger a new dialogue: "I see Your App Name has disappeared from your iPhone. Would you like to download it again?") On my own 3GS the alert doesn't show when the timer is shorter than 100msec. I also tried loading the AppStore URL after the app:// URK with two meta refresh tags, but that didn't work. – Martijn Thé Jul 24 '09 at 09:39
  • 16
    Great solution. If your fallback is to another application, it will load _IMMEDIATELY_ without displaying the error. So instead of falling back to http://itunes.com... use itms://phobos.apple.com/... to avoid the popup error! – jb. Mar 06 '10 at 00:20
  • I ran this code on my iphone 3g (ios 3.1.3) and the error still pops up. I think safari must be pausing javascript execution while it tries to load the new page. Once I click the ok from the alert that pops up the next page loads. – Anony372 Aug 16 '11 at 17:38
  • 51
    **Problem**: When `window.location="custom-uri://` succeeds, the fallback timeout is not killed. When the user returns to the browser from your app, the timer is still there and will launch the app store link. This is poor user experience. – JoJo Jan 06 '12 at 20:19
  • 5
    It seems the Cookies are sandboxed in #ios6 so you cannot access cookie setted by an application from another one. (App WebUI and Safari Mobile for example) – Olivier Amblet Jan 25 '13 at 14:15
  • 2
    Anyone find a way to prevent the original timeout from firing when the user comes back to the browser? (the upvoted problem JoJo mentions) – cobolstinks Apr 29 '13 at 15:28
  • 2
    To solve the problem that JoJo mentions, only run the code in the timeout if not "a lot of time" has passed since the user clicked the link. See this solution: http://stackoverflow.com/a/14751543/533420 – kkara Jun 06 '13 at 09:33
  • Timeout will still fire on iOS 7 with a so small time (25 ms)... You should change the 25 with 700 because the code execution stops only after the app switching animation is fully played (it takes about 700 ms). Not working anymore in iOS 7 unless you'll accept an huge timeout – Marco Marsala Apr 18 '14 at 11:04
  • @MarcoMarsala The workaround also doesn't work for iOS Chrome. – Alex B May 06 '14 at 06:39
  • 1
    @JoJo take a look at my solution to avoid the experience you mentioned. http://stackoverflow.com/a/24133372/278629 – cnotethegr8 Jun 10 '14 at 05:20
  • It still opens the mobile safari browser instead of going straight to the app if it is installed (which youtube does right). How do you solve that? – Rodrigo Ruiz Jun 19 '14 at 19:49
  • twitter app show and alert and always prevent to call your app and instead call AppStore. – Gabriel Goncalves Oct 09 '14 at 16:48
  • @Rodrigo It seems to me like apple provides this sort of seamless transitioning specifically for youtube videos (Not exactly sure why though) the same way it does for apple maps and iTunes links. Check this out: https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html – hshepherd Oct 15 '14 at 14:38
  • do you know solution for android? – someUser Jan 19 '15 at 16:15
  • What if user accidently clicks 'Yes' and the app isn't installed on device? How should we clear the cookie? – Nirav Gandhi Sep 30 '15 at 05:38
95

It's quite possible to do this in JavaScript as long as your fallback is another applink. Building on Nathan's suggestion:

<html>
  <head>
    <meta name="viewport" content="width=device-width" />
  </head>
  <body>

    <h2><a id="applink1" href="fb://profile/116201417">open facebook with fallback to appstore</a></h2>
    <h2><a id="applink2" href="unknown://nowhere">open unknown with fallback to appstore</a></h2>
    <p><i>Only works on iPhone!</i></p>    

  <script type="text/javascript">

// To avoid the "protocol not supported" alert, fail must open another app.
var appstorefail = "itms://itunes.apple.com/us/app/facebook/id284882215?mt=8&uo=6";

function applink(fail){
    return function(){
        var clickedAt = +new Date;
        // During tests on 3g/3gs this timeout fires immediately if less than 500ms.
        setTimeout(function(){
            // To avoid failing on return to MobileSafari, ensure freshness!
            if (+new Date - clickedAt < 2000){
                window.location = fail;
            }
        }, 500);    
    };
}

document.getElementById("applink1").onclick = applink(appstorefail);
document.getElementById("applink2").onclick = applink(appstorefail);

</script>
</body>
</html>

Check out a live demo here.

Community
  • 1
  • 1
jb.
  • 9,987
  • 12
  • 39
  • 38
  • 7
    Agree with Lee, this seems like a more straightforward solution - though i still do get the error message from safari if the app does not exist and it redirects to the app store. – Jon Apr 29 '11 at 19:50
  • 1
    **Problem**: If you don't have the app installed, trying to open the app will pop up an alert saying "cannot open page". Although there is nothing technically wrong, this is a weird message to show to an average user. – JoJo Jan 06 '12 at 21:30
  • 2
    I used this solution for both Android and iOS. I found if I change the timeout value from 500 to 100 I don't get the "Cannot open page" popup dialog in iOS. I also found that the timeout needs to be 50 for Android – Rossini Jan 18 '12 at 14:56
  • 14
    Using "itms-apps:" instead of "itms:" saves 1 redirect and directly opens app page in appstore. – German Latorre Jun 20 '12 at 12:21
  • 6
    @Rossini this is built into Android by setting up an [intent filter for your action that responds to a particular host](http://developer.android.com/guide/topics/manifest/data-element.html) – jwadsack Jun 28 '12 at 19:46
  • I had to change 2000 to about 500 in "+new Date - clickedAt < 2000" or I would get a popup saying "Would you like to open this in the App store?". Changing this made this work as said above. – Benjamin Oman Aug 23 '12 at 01:20
  • 9
    Does anyone know how to avoid the "cannot open page" error message from safari if the app is not installed and before redirecting to the app store? – davidk Sep 07 '12 at 20:42
  • I had to go to a timeout of 1000 for iOS 5. With a timeout of 500 it would still trigger the fail URL even on success. – Tod Cunningham Jun 09 '13 at 23:24
  • 1
    So, what happens when the cookie is set to true and the app gets uninstalled? – gitaarik Nov 05 '13 at 09:49
  • If you don't want to build and manage this client side Javascript for all browsers, you should check out Branch links at https://branch.io. Once you tell them what the URI scheme is and the fallback url (defaulted to app stores), they generate and host this code automatically for you. – Alex Austin Jul 13 '15 at 06:10
  • @jb. Any suggestion how to do it on Mac ? – CoDe Feb 28 '17 at 10:39
26

For iOS 6 devices, there is an option: Promoting Apps with Smart App Banners

ohho
  • 50,879
  • 75
  • 256
  • 383
  • 12
    Sadly Smart App Banner are only supported in Mobile Safari, not in UIWebviewComponent. So it wont be display if your site is displayed inside a Twitter client for exemple. – Olivier Amblet Jan 25 '13 at 14:17
24

I found that the selected answer works for the browser apps but I was having issues with the code working in non browser apps that implement a UIWebView.

The problem for me was a user on the Twitter app would click a link that would take them to my site through a UIWebView in the Twitter app. Then when they clicked a button from my site Twitter tries to be fancy and only complete the window.location if the site is reachable. So what happens is a UIAlertView pops up saying are you sure you want to continue and then immediately redirects to the App Store without a second popup.

My solution involves iframes. This avoids the UIAlertView being presented allowing for a simple and elegant user experience.

jQuery

var redirect = function (location) {
    $('body').append($('<iframe></iframe>').attr('src', location).css({
        width: 1,
        height: 1,
        position: 'absolute',
        top: 0,
        left: 0
    }));
};

setTimeout(function () {
    redirect('http://itunes.apple.com/app/id');
}, 25);

redirect('custom-uri://');

Javascript

var redirect = function (location) {
    var iframe = document.createElement('iframe');
    iframe.setAttribute('src', location);
    iframe.setAttribute('width', '1px');
    iframe.setAttribute('height', '1px');
    iframe.setAttribute('position', 'absolute');
    iframe.setAttribute('top', '0');
    iframe.setAttribute('left', '0');
    document.documentElement.appendChild(iframe);
    iframe.parentNode.removeChild(iframe);
    iframe = null;
};

setTimeout(function () {
    redirect('http://itunes.apple.com/app/id');
}, 25);

redirect('custom-uri://');

EDIT:

Add position absolute to the iframe so when inserted there isn't a random bit of whitespace at the bottom of the page.

Also it's important to note that I have not found a need for this approach with Android. Using window.location.href should work fine.

cnotethegr8
  • 7,342
  • 8
  • 68
  • 104
  • 1
    It works!! Thanks I finally found a solution that works on every browser. – koleror Aug 21 '14 at 15:26
  • 2
    This is the best solution if found. Thank you. There is no error popup anymore. – Michael Sep 02 '14 at 13:49
  • Trying to put the Javascript in a href so it only gets called when a link on my page is clicked but can't seem to get it to work. Any suggestions? – Tim Oct 10 '14 at 03:17
  • 1
    @Tim if you want this code fired when a link is clicked, than wrap this code in a function which is called once the link gets clicked. – cnotethegr8 Oct 10 '14 at 07:06
  • 1
    @cnotethegr8 I've put it in a function and redirect to custom url works great but the fall back to iTunes does not. Here's my [code](http://www.filedropper.com/redirecttest_1). What am I missing? – Tim Oct 12 '14 at 22:08
  • @Tim currently this code only works for mobile devices and for loading app url schemes. So `my-app://` would work along with `https://itunes.apple.com/app/my-app-id` because this redirects to `appstore://`. I'll see if I can debug the code a little to give you a solution for other urls. – cnotethegr8 Oct 24 '14 at 14:12
  • That answer also deals with the issue of the "cannot open page" message that shows up with the solution http://stackoverflow.com/a/1109200/1014300 – thibaultP Jan 08 '15 at 00:54
  • @cnotethegr8 Thanks so much ! This code works so good! You can change code inside the Timeout function form redirect to window.location will get it works for regular links. – formatjam Mar 25 '15 at 21:24
  • 1
    Has anyone else had trouble with this in iOS 9. It no longer works for me in Safari. – bgolson Nov 09 '15 at 17:46
  • 1
    @bgolson it appears Apple made the change to force the use of 'Universal Links'. http://stackoverflow.com/a/32774701/278629 – cnotethegr8 Nov 10 '15 at 05:23
21

In iOS9 Apple finally introduced the possibility to register your app to handle certain http:// URLs: Universal Links.

A very rough explanation of how it works:

  • You declare interest in opening http:// URLs for certain domains (web urls) in your app.
  • On the server of the specified domains you have to indicate which URLs to open in which app that has declared interest in opening URLs from the server's domain.
  • The iOS URL loading service checks all attempts to open http:// URLs for a setup as explained above and opens the correct app automatically if installed; without going through Safari first...

This is the cleanest way to do deep linking on iOS, unfortunately it works only in iOS9 and newer...

severin
  • 10,148
  • 1
  • 39
  • 40
9

BUILDING Again on Nathan and JB's Answer:

How To Launch App From url w/o Extra Click If you prefer a solution that does not include the interim step of clicking a link, the following can be used. With this javascript, I was able to return a Httpresponse object from Django/Python that successfully launches an app if it is installed or alternatively launches the app store in the case of a time out. Note I also needed to adjust the timeout period from 500 to 100 in order for this to work on an iPhone 4S. Test and tweak to get it right for your situation.

<html>
<head>
   <meta name="viewport" content="width=device-width" />
</head>
<body>

<script type="text/javascript">

// To avoid the "protocol not supported" alert, fail must open another app.
var appstorefail = "itms://itunes.apple.com/us/app/facebook/id284882215?mt=8&uo=6";

var loadedAt = +new Date;
setTimeout(
  function(){
    if (+new Date - loadedAt < 2000){
      window.location = appstorefail;
    }
  }
,100);

function LaunchApp(){
  window.open("unknown://nowhere","_self");
};
LaunchApp()
</script>
</body>
</html>
BFar
  • 2,447
  • 22
  • 23
9
window.location = appurl;// fb://method/call..
!window.document.webkitHidden && setTimeout(function () {
    setTimeout(function () {
    window.location = weburl; // http://itunes.apple.com/..
    }, 100);
}, 600);

document.webkitHidden is to detect if your app is already invoked and current safari tab to going to the background, this code is from www.baidu.com

zyanlu
  • 181
  • 1
  • 3
  • I tested this solution and found that, while it delivers events properly, the "Safari cannot open this page because the address is invalid" error dialog appears, momentarily. (It auto-dismisses after a fraction of a second). – michaelhanson Jan 31 '13 at 19:12
  • use an iframe to load `appurl` and `weburl` could solve your problem – zyanlu Feb 26 '13 at 06:01
  • 1
    @zyanlu : i have tried with iFrame. bt still safari is showing the same error. – AloSwift May 27 '14 at 13:14
6

If you add an iframe on your web page with the src set to custom scheme for your App, iOS will automatically redirect to that location in the App. If the app is not installed, nothing will happen. This allows you to deep link into the App if it is installed, or redirect to the App Store if it is not installed.

For example, if you have the twitter app installed, and navigate to a webpage containing the following markup, you would be immediately directed to the app.

<!DOCTYPE html>
<html>
    <head>
    <title>iOS Automatic Deep Linking</title>
    </head>
    <body>
        <iframe src="twitter://" width="0" height="0"></iframe>
        <p>Website content.</p>
    </body>
</html>

Here is a more thorough example that redirects to the App store if the App is not installed:

<!DOCTYPE html>
<html>
    <head>
    <title>iOS Automatic Deep Linking</title>
    <script src='//code.jquery.com/jquery-1.11.2.min.js'></script>
    <script src='//mobileesp.googlecode.com/svn/JavaScript/mdetect.js'></script>
    <script>
      (function ($, MobileEsp) {
        // On document ready, redirect to the App on the App store.
        $(function () {
          if (typeof MobileEsp.DetectIos !== 'undefined' && MobileEsp.DetectIos()) {
            // Add an iframe to twitter://, and then an iframe for the app store
            // link. If the first fails to redirect to the Twitter app, the
            // second will redirect to the app on the App Store. We use jQuery
            // to add this after the document is fully loaded, so if the user
            // comes back to the browser, they see the content they expect.
            $('body').append('<iframe class="twitter-detect" src="twitter://" />')
              .append('<iframe class="twitter-detect" src="itms-apps://itunes.com/apps/twitter" />');
          }
        });
      })(jQuery, MobileEsp);
    </script>
    <style type="text/css">
      .twitter-detect {
        display: none;
      }
    </style>
    </head>
    <body>
    <p>Website content.</p>
    </body>
</html>
q0rban
  • 915
  • 11
  • 9
  • The problem with your first example is that if you go back to Mobile Safari, it will display "The Twitter App is not installed" even though Twitter was launched. Same with the second example, displaying "Website content". There needs to be code that does something different (loads another URL, or displays one of two messages) if the app is installed. – mahboudz Jan 08 '15 at 02:01
  • 1
    Yes, @mahboudz, if you read the text, it is just a simple example to show that it is possible to automatically redirect to the app. – q0rban Jan 09 '15 at 10:34
  • I then follow up with a more thorough example that would show the actual website content. I can delete the "The Twitter App is not installed" text if it will make it clearer. – q0rban Jan 09 '15 at 10:51
  • ios 6 still displayes popup That page cannot be opened because of the invalid url – Andrei Shender Feb 02 '15 at 10:56
  • @AndreiShender, here are iOS usage statistics at the time of this writing: http://monosnap.com/image/8eXUcpEUi8fm94DiMZIdiIp4xUNaln.png iOS 8: 72%, iOS 7: 25%, Earlier versions: 3% – q0rban Feb 05 '15 at 15:55
  • ios 7 does the same - popup. – Andrei Shender Feb 06 '15 at 08:19
4

Heres a solution.

Setup a boolean sitiation using blur and focus

//see if our window is active
window.isActive = true;
$(window).focus(function() { this.isActive = true; });
$(window).blur(function() { this.isActive = false; });

Bind your link with a jquery click handler that calls something like this.

function startMyApp(){
  document.location = 'fb://';

  setTimeout( function(){
    if (window.isActive) {
        document.location = 'http://facebook.com';
    }
  }, 1000);
}

if the app opens, we'll lose focus on the window and the timer ends. otherwise we get nothing and we load the usual facebook url.

Dane Macaulay
  • 814
  • 8
  • 11
  • 1
    Thanks a lot for the suggestion. I am facing the problem that the "launch external application" dialogue seems to be enough for the blur to inactivate the flag. This happens even if the app is not installed (e.g., when clicking a link meant to launch an iPhone App on a desktop). Ideas? – fluxon Sep 04 '12 at 12:13
2

You can't, as far as I know, make the entire OS understand an http:+domain URL. You can only register new schemes (I use x-darkslide: in my app). If the app is installed, Mobile Safari will launch the app correctly.

However, you would have to handle the case where the app isn't installed with a "Still here? Click this link to download the app from iTunes." in your web page.

Fraser Speirs
  • 4,642
  • 3
  • 21
  • 15
  • 2
    This no longer is correct: with iOS9 and recent Android versions you can register your app to listen for certain `http` URLs – severin Sep 25 '15 at 08:20
0

Check the User-Agent and in case it's Mobile Safari, open a myprotocol:// URL to (attempt) to open the iPhone app and have it open Mobile iTunes to the download of the app in case the attempt fails

This sounds a reasonable approach to me, but I don't think you'll be able to get it to open mobile itunes as a second resort. I think you'll have to pick one or the other - either redirect to your app or to itunes.

i.e. if you redirect to myprotocol://, and the app isn't on the phone, you won't get a second chance to redirect to itunes.

You could perhaps first redirect to an (iphone optimised) landing page and give the user the option to click through to your app, or to itunes to get the app if they don't have it? But, you'll be relying on the user to do the right thing there. (Edit: though you could set a cookie so that is a first-time thing only?)

frankodwyer
  • 13,948
  • 9
  • 50
  • 70
  • 1
    Thats wrong. If an error is shown that the page cannot be opened (App not installed), JS is still executed. Thats why you can can redirect to another fallback solution then. – Erik Apr 21 '10 at 07:26
0

It also possible to check tab activity by document.hidden property

Possible solution

document.location = 'app://deep-link';

setInterval( function(){
  if (!document.hidden) {
    document.location = 'https://app.store.link';
  }
}, 1000);

But seems like this not works in Safari

Den Kison
  • 1,074
  • 2
  • 13
  • 28
0

In seeking to fix the problem of pop-up, I discovered that Apple had a way around this concern.

Indeed, when you click on this link, if you installed the application, it is rerouted to it; otherwise, you will be redirected to the webpage, without any pop-up.

Titignes
  • 1
  • 2
  • 3
    I dug in pretty deeply into how that link was working, and the best I can come up with is that it is not a JavaScript solution at all. Apple seems to have registered a special URL handler for their app that doesn't require a custom protocol and is instead some matching on a URL string. The link you send redirects immediately with a 303 to [here](http://store.apple.com/fr/xc/holiday/shopping_event/guide?cid=CDM-EU-34581&cp=em-P0011136-180935&sr=em). If you send that link in an email to yourself you can observe that clicking on it will directly bring up the AppStore app if installed – Casey Dec 07 '11 at 14:18
  • Very interesting. You're right : if i click on it, it brings up the AppStore app if installed. But if you delete some parameters until "holydays", it brings up within Safari. Apple can register special URL scheme... – Titignes Dec 07 '11 at 17:59
  • @Titignes, could you please elaborate on this way to open app or web page. What is the pattern for building such an url? – Andrei Shender Feb 02 '15 at 11:03