Is it possible to register a http+domain-based URL Scheme for iPhone apps, like YouTube and Maps? - iphone

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!

I think the least intrusive way of doing this is as follows:
Check if the user-agent is that of an iPhone/iPod Touch
Check for an appInstalled cookie
If the cookie exists and is set to true, set window.location to your-uri:// (or do the redirect server side)
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.
The "Yep" button sets the cookie to true and redirects to your-uri://
The "Nope" button redirects to "http://itunes.com/apps/yourappname" which will open the App Store on the device
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://";

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.

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

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.

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...

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>

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

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>

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.

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.

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?)

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.

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

Related

Chrome packaged application - downloading file from Webview

I am working on getting an existing Ajax style web application functional as a Chrome packaged app. The Ajax app is running in a Webview inside the packaged app and is mostly working great.
The Ajax app allows users to upload documents using standard HTML 5 upload and drag/drop. Uploads work fine but downloads are not working.
This is a simplified sample of the logic in the Ajax app. It uses Javascript to handle the click event, performs some logic, and then eventually triggers the download by setting the location of a hidden IFrame.
<html>
<head>
<script type="text/javascript">
function downloadFile(url) {
window.frames['dataTransfer'].location = url;
}
</script>
</head>
<body>
Google<br/>
Download PDF<br/>
Download PDF JS<br/>
<iframe name="dataTransfer" style="width: 0; height: 0; border: none;"></iframe><br/>
</body>
</html>
If you run this in a standard Chrome tab all 3 links work fine. However in a Chrome App only the first 2 links work - clicking the 3rd link does nothing. In the network section of the Developer tools it actually appears to start the download but then is quickly cancelled.
The manifest of the Chrome app allows Webview and the relevant domains involved.
{
"manifest_version": 2,
"name": "Test Download",
"version": "0.1.0",
"permissions": [
"webview",
"<DOMAIN OF THE SAMPLE PAGE ABOVE>",
"https://s3.amazonaws.com/"
],
"app": {
"background": {
"scripts": ["background.js"]
}
},
"icons": {},
"minimum_chrome_version": "28"
}
The Chrome App has some simple newwindow handling in it as well.
window.onload = function() {
var webview = document.querySelector('#app-webview');
webview.src = '<URL TO SAMPLE PAGE ABOVE>';
webview.addEventListener('newwindow', function(e) {
e.preventDefault();
e.stopPropagation();
window.open(e.targetUrl);
});
};
Any suggestions on how to get downloads working (hopefully without requiring significant changes to the Ajax app)?
File downloads from the webview guest are gated by a permissionrequest event per download attempt. The default response is to deny download permission.
Note: The download permissionrequest will not land until Chrome 30 now, and it appears that documentation has not yet been made available. It is generally a stable API though, and it is unlikely to change between now and then.
In order to override this behavior, you need to handle the event and explicitly allow the download to happen. As an example:
var webview = document.querySelector('#app-webview');
webview.addEventListener('permissionrequest', function(e) {
if (e.permission === 'download') {
e.request.allow();
}
});
The event includes additional information (such as the download URL in e.url) in case you want to further filter your grants.
Be aware that this will only permit the download to happen using the regular Chrome file download experience, which isn't necessarily what you'll want from within a packaged app. Your options are limited until the chrome.downloads API is made available to apps.
One possibility is to deny the download request, grab the URL from the event, and manually manage the download process with XHR, the fileSystem API, and whatever UX you want to build.

Redirect To my App if the app hosting site is visited

i want the codes which will redirect the user to my app if he/she tries to open my app hosting site.
for ex:
my app link: https://apps.facebook.com/gaming-zone/
app hosting link: http://gaming-zone.herokuapp.com/
i have found some codes already but those codes redirects the user continously and if he is using my app on facebook and he is playing any game then also it redirects the user to app home page.
i want the exact codes that redirect the user only when he visits my hosting site.
These are the codes that i found already:
<script type='text/javascript'>
if (window.top.location == window.location)
window.top.location = 'https://apps.facebook.com/gaming-zone/';
</script>
Here is the code that I use:
<script type="text/javascript">
function NotInFacebookFrame() {
return top === self;
}
function ReferrerIsFacebookApp() {
if(document.referrer) {
return document.referrer.indexOf("apps.facebook.com") != -1;
}
return false;
}
if (NotInFacebookFrame()) {
top.location.replace("https://apps.facebook.com/YOUR_APP_NAMESPACE");
}
</script>
This checks if they are currently in the Facebook frame and ONLY if they aren't it will redirect them to "https://apps.facebook.com/YOUR_APP_NAMESPACE"
Note: you can use this on any page in you app just change the URL accordingly.
For example: If you are using this in http://YourDomain.com/anotherpage.php you can change the URL in the code above to https://apps.facebook.com/YOUR_APP_NAMESPACE/anotherpage.php
The problem is that you cannot read the window.top.location value. You can only write it. Strange but thats it.
Option 1: Mount your app to a page tab and create a like-gate. Like gate will return false outside facebook iframe ALWAYS.
Option 2: Check signed request data when loading the page at backend.
Option 3: Other option is to examinate the top url as you do but at backend.

How to redirect from Mobile Safari to Native iOS app (like Quora)?

On my iPhone, I just noticed that if I do a Google Search (in Mobile Safari) and select a result on quora.com, the result page launches the native Quora app on my phone.
How is this done? Specifically, is it a detection of the user agent and the use of an iOS URL scheme? Can it tell if the native app is installed and/or redirect to the app store?
I'm reposting an answer to my own related (but was originally Ruby-on-Rails-specific) question from here: Rails: redirect_to 'myapp://' to call iOS app from mobile safari
You can redirect using javascript window.location.
Sample code:
<html><head>
<script type="text/javascript">
var userAgent = window.navigator.userAgent;
if (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i)) {
window.location = "myiosapp://"
}
</script>
</head>
<body>
Some html page
</body>
</html>
Just a small improvement of the JS code, if the app is not installed, it will send the user to itunes store ;)
<script type="text/javascript">
// detect if safari mobile
function isMobileSafari() {
return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/)
}
//Launch the element in your app if it's already installed on the phone
function LaunchApp(){
window.open("Myapp://TheElementThatIWantToSend","_self");
};
if (isMobileSafari()){
// To avoid the "protocol not supported" alert, fail must open itunes store to dl the app, add a link to your app on the store
var appstorefail = "https://itunes.apple.com/app/Myapp";
var loadedAt = +new Date;
setTimeout(
function(){
if (+new Date - loadedAt < 2000){
window.location = appstorefail;
}
}
,100);
LaunchApp()
}
</script>
You can do trigger your application to be launched using custom URL scheme, registered by your application with the iOS runtime. Then on your website, write code to detect the incoming User-Agent and if iOS is detected generate your custom URL's instead of regular http ones.

iPhone browser: Checking if iPhone app is installed from browser

I have web page where I have Button that either opens app (if it installed) or directs to App store if app isn't installed.
It all works if App is installed (I call into "MYAPP://"). However, if app is not installed Safari shows error message "Can not open URL" and that's it. Is there way to disable that message from JScript or is there another way to find out from JScript if app installed (instead of hitting app URL)?
To MODERATOR: I saw someone asked similar question and Moderator wrongly marked it as duplicate. Please understand that question was specifically about doing it from Browser.
Found somewhat suitable solution here
BTW if someone interested in how to do same thing for Android, here is code. We are using Dojo library:
dojo.io.iframe.send({
url: "yourApp://foo/bar",
load: function(resp) {
// nothing to do since it will automagically open App
},
error: function () {
window.location = "go to Android market";
}
});
At Branch we use a form of the code below--note that the iframe works on more browsers. Simply substitute in your app's URI and your App Store link. By the way, using the iframe silences the error if they don't have the app installed. It's great!
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
window.onload = function() {
// Deep link to your app goes here
document.getElementById("l").src = "my_app://";
setTimeout(function() {
// Link to the App Store should go here -- only fires if deep link fails
window.location = "https://itunes.apple.com/us/app/my.app/id123456789?ls=1&mt=8";
}, 500);
};
</script>
<iframe id="l" width="1" height="1" style="visibility:hidden"></iframe>
</body>
</html>
If others have better solutions to detect whether the URI scheme call actually failed, please post! I haven't seen one, and I've spent a ton of time looking. All existing solutions just rely on the user still being on the page and the setTimeout firing.
here is a code that works on iOs, even if the "Can not open URL" still show.
window.location = "yourApp://foo/bar";
clickedAt = +new Date;
setTimeout(function() {
if (+new Date - clickedAt < 2000) {
window.location = "go to Android market";
}
}, 500);
Thanks for the android solution.
I've combined a few things and used the following code to check if it's an iOS device before using the try/catch method from chazbot. Unfortunately, the device still throws a pop-up box to the user saying the address is invalid...anyone know if this is expected behavior for trying to open an invalid URL within a "try" block?
var i = 0,
iOS = false,
iDevice = ['iPad', 'iPhone', 'iPod'];
for ( ; i < iDevice.length ; i++ ) {
if( navigator.platform === iDevice[i] ){ iOS = true; break; }
}
try {
//run code that normally breaks the script or throws error
if (iOS) { window.location = "myApp://open";}
}
catch(e) {
//do nothing
}
There are a few things you can do to improve on other answers. Since iOS 9, a link can be opened in a UIWebView or in a SFSafariViewController. You might want to handle them differently.
The SFSafariViewController shares cookies across apps, and with the built in Safari. So in your app you can make a request through a SFSafariViewController that will set a cookie that says "my app was installed". For instance you open your website asking your server to set such cookie. Then anytime you get a request from a SFSafariViewController you can check for that cookie and redirect to MYAPP:// if you find it, or to the app store if you don't. No need to open a webpage and do a javascript redirection, you can do a 301 from your server. Apps like Messages or Safari share those cookies.
The UIWebView is very tricky since it is totally sandboxed and shared no cookies with anything else. So you'll have to fallback to what has been described in other answers:
window.onload = function() {
var iframe = document.createElement("iframe");
var uri = 'MYAPP://';
var interval = setInterval(function() {
// Link to the App Store should go here -- only fires if deep link fails
window.location = "https://itunes.apple.com/us/app/my.app/id123456789?ls=1&mt=8";
}, 500);
iframe.onload = function() {
clearInterval(interval);
iframe.parentNode.removeChild(iframe);
window.location.href = uri;
};
iframe.src = uri;
iframe.setAttribute("style", "display:none;");
document.body.appendChild(iframe);
};
I've found it annoying that this will prompt the user if they want to leave the current app (to go to your app) even when your app is not installed. (empirically that seems only true from a UIWebView, if you do that from the normal Safari for instance that wont happen) but that's all we got!
You can differentiate the UIWebView from the SFSafariViewController from your server since they have different user agent header: the SFSafariViewController contains Safari while the UIWebView doesn't. For instance:
Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E269
-> UIWebView
Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E269 Safari/602.1
-> SFSafariViewController
Other considerations:
in the first approach, you might want to handle uninstalls: if the user uninstalls your app, you still have a cookie that says that the app is there but it's not, so you might end up with the "Can not open URL" message. I've handled it by removing the cookie after a few tries that didn't end up opening the app (which I know because at every app open I'm resetting this failed tries cookie)
In the second case, it's unclear if you're better off using a setInterval or setTimeout. The issue with the timeout is that if it triggers while a prompt is on, it will be ignored. For instance if you open the link from Messenger, the os will ask you "Leave Messenger? You're about to open another app" when the iframe tries to load your app. If you don't respond either way within the 500ms of the timeout, the redirection in the timeout will be ignored.
Finally even though the UIWebView is sandboxed, you can give it a cookie to identify it, pass it in your deeplink, and save this id as corresponding to device with your app in your server when your app opens. Next time, if you see such a cookie in the request coming from the UIWebView, you can check if it matches a known device with the app and redirect directly with a 301 as before.
I think you can still use the app url as a test. Try wrapping it in a try...catch block,
try {
//run code that normally breaks the script or throws error
}
catch(e) {
//do nothing
}

Connect to Facebook with Javascript Client Library using Adobe AIR

I'm having an issue connecting to Facebook through the Javascript Client Library in Adobe AIR. It works fine in the browser but in Adobe AIR it seems to not be able to find the Facebook functions. Here is some code I copied from the Facebook website: (Oh and I have the xd_receiver.htm file in the correct path too)
<textarea style="width: 1000px; height: 300px;" id="_traceTextBox">
</textarea>
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script>
//
var api = FB.Facebook.apiClient;
// require user to login
api.requireLogin(function(exception){
FB.FBDebug.logLevel=1;
FB.FBDebug.dump("Current user id is " + api.get_session().uid);
// Get friends list
//5-14-09: this code below is broken, correct code follows
//api.friends_get(null, function(result){
// Debug.dump(result, 'friendsResult from non-batch execution ');
// });
api.friends_get(new Array(), function(result, exception){
FB.FBDebug.dump(result, 'friendsResult from non-batch execution ');
});
});
});
//]]>
</script>
It's not that simple actually man, the problem with loading external script files from the application sandbox is that it's not supported. Technically, by using iframes, you should be able to do this and it works but it's still impossible to use XFBML in the root document after from my experience. After two days of trial and error, the easiest way to do use Facebook Connect in an html/ajax adobe air application is to download the actionscript library and include it as such:
<script type="application/x-shockwave-flash" src="lib/facebook.swf"></script>
<script type="text/javascript">
var FB = window.runtime.com.facebook;
var fb = new FB.Facebook();
var session = new FB.utils.DesktopSessionHelper();
//etc...
</script>
then just refer to the documentation. my only problem now it that I still haven't found an efficient enough way of using XFBML in adobe air, simply adding the facebook namespace to the html tag doesn't do the trick unfortunately. If any knows, please do share thanks
you also need to change xml namespace (main html tag) to this:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml" xml:lang="en-gb" lang="en-gb">