Open apps with hyperlinks on iOS
Important update: iOS 9 introduces the possibility for native apps to handle links to normal websites. This feature will make this hack unnecessary.
On some pages of my website I’ve added a button to follow me on twitter. It’s a
custom implementation that doesn’t use twitter’s official iframe
for
performance reasons and falls back to a simple link if JavaScript is disabled or
fails to load. On iOS I wanted this button to open the twitter app though.
The problem is that
Apple’s URL schemes provide no
fallback if an app is not installed. If you add a link to
twitter://user?screen_name=max_hoffmann
for example and the user hasn’t
installed the app, mobile Safari only shows an error message that the URL could
not be opened.
This is what my custom popup implementation looked like before adding iOS specific behavior:
function openPopup(event) {
// don’t open the link’s href
event.preventDefault();
// twitter’s popup size
var width = 550;
var height = 570;
// position popup in the center of the screen
var top = screen.height / 2 - height / 2;
var left = screen.width / 2 - width / 2;
// open popup
window.open(
"https://twitter.com/max_hoffmann",
"Follow @max_hoffmann on twitter",
"width=" + width + ",height=" + height + ",top=" + top + ",left=" + left
);
}
To workaround iOS’s error message if the URL scheme could not open an app I
changed the code to load twitter’s URL scheme in an iframe
instead. This opens
the app if it’s installed and doesn’t show the error message otherwise.
The last problem to solve was that there is no way to detect if the iframe
threw an error. It should still open my profile on twitter’s website if opening
the app failed. First I tried using the page visibility API to detect if the
page was pushed to background after the user clicked the button, which would
indicate that it opened the app. Unfortunately this didn’t work so I had to use
another workaround:
- get a timestamp before inserting the
iframe
- add a function add the end of the current event loop via
setTimeout
- insert the
iframe
- at some point in time the scheduled function will be called
- calculate if less than
30ms
passed
- if more than
30ms
passed iOS could open the app - otherwise it’s not installed and the fallback URL is used
#Final Code
function openPopup(event) {
// don’t open the link’s href
event.preventDefault();
var username = 'max_hoffmann'; // use your username here
// twitter’s popup size
var width = 550;
var height = 570;
// position popup in the center of the screen
var top = screen.height/2-height/2;
var left = screen.width/2-width/2;
// detect iOS
if (['iPhone', 'iPad'].indexOf(navigator.platform) > -1) {
// create iframe with twitter:// url scheme
var iframe = document.createElement('iframe');
iframe.src = 'twitter://user?screen_name='+username;
// hide iframe visually
iframe.width = 0;
iframe.height = 0;
iframe.frameBorder = 0;
// get timestamp before trying to open the app
var beforeSwitch = Date.now();
// schedule check if app was opened
setTimeout(function() {
// if this is called after less than 30ms
if (Date.now() - beforeSwitch < 30) {
// open fallback url in browser
window.location = 'https://twitter.com/'+username;
}
});
// add iframe to trigger opening the app
document.body.appendChild(iframe);
// directly remove it again
iframe.parentNode.removeChild(iframe);
return;
}
// open popup if user is not on iOS
window.open('https://twitter.com/'+username, 'Follow @'+username+' on twitter', 'width='+width+',height='+height+',top='+top+',left='+left);
},
This workaround can be used for other apps on iOS as well. Take a look at this list of URL schemes for more schemes of popular apps. Let me know if this code could be improved. I’m thankful for feedback.