iPhone browser: Checking if iPhone app is installed from browser - iphone

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
}

Related

Access Blocked: authorisation error. flutter

I just released my first app and It has a button in it that takes you to a website.
A user just sent me this:.
I tried googling Google's secure browsers policy but not much info is coming up.
how can I make my app comply with this policy? I think the button opens a browser in app (I use duckduckgo as my default browser and haven't had an issue)
is it just a case of opening a browser and then heading to the website when the button is pressed?
my code to open the website is:
_launchURL() async {
const url = 'https://www.thiswebsite.com';
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
throw 'Could not launch $url';
}
}
thanks so much and any help would be greatly appreciated
Google is trying to make sure, you open this window in an actual new browser window, not in a webview still under the control of your application.
Your code should open an external browser.
Maybe the user has no browser installed on their device? Maybe their default browser is some exotic thing not recognized by Google?
If you are using the latest version of url_launcher (currently 6.1.8) there is not a lot more you can do.
You could force the app to take the external browser, not the in-app webview:
await launchUrl(_url,mode: LaunchMode.externalApplication);
But that should be what happens anyway. If your version is up to date, ask your user, what browser they use. Be prepared to tell them that they need to use another one.

Facebook redirect after login

I have read a number of questions and answers, including this one on StackOverflow and none works. My question to all responses I have seen are 'does it work in Safari?'.
I am trying to get this to work with Safari. It works on Chrome and Firefox fine. But in Safari the login screen just freezes and I get the "Unsafe JavaScript attempt to access frame with URL" log message.
I have a canvas app. I want to log the user in and redirect them to a page once they have logged in. I am trying to redirect directly after login. I have tried
setting window.top.location to a facebook url (with some data passed to a signed request as the all_data argument)
setting window.location to a URL with the same domain as my app.
subscribing to the auth.login event and putting the redirect there
putting the redirect in the callback to login
None of these works for Safari. I'm starting to think that there's no way to do it.
function doSomething()
{
FB.login(
function(loginResponse)
{
if (loginResponse.authResponse)
{
window.top.location = "my url"
}
},
{
scope:"some,scope"
}
);
}
}
In response to Nitzan Tomer, here is the equivalent code which doesn't work with Safari but does work with others:
function myThing()
{
FB.login(function(loginResponse)
{
if (loginResponse.authResponse)
{
FB.api('/me', function(response)
{
window.location = "http://my_app.com?x=" + response.xyz;
});
}
},
{
scope:"scopes"
}
);
}
This is not much of a solution to your problem, but more of a "workaround", though it still uses window.top.location but not from a callback so maybe it will work (I'm just not sure where the callback is executed, since it's the fb sdk that executes it, maybe they call it from the fb iframe inside your iframe).
Instead of using client side authentication, you can use the server side flow.
The entire process is happening on the top window, since it starts by you redirecting the user to the oauth dialog using window.top.location.
When the process ends facebook redirects the user to your redirect_uri, and there you can simply redirect the user where ever you want.
I hope this will help.
Also, you should be aware that in the Facebook Platform Policies it states:
13 . The primary purpose of your Canvas or Page Tab app on Facebook must not be to simply redirect users out of the Facebook experience
and onto an external site.
It turns out the problem was occurring slightly earlier in the login process and the callback never got called. I did set a breakpoint in the callback (which wasn't called) but I thought the breakpoint might not work for some reason (perhaps because it was being executed in the context of another window).
Anyway, the problem was that the channel file (which the docs seem to suggest are optional) wasn't working properly, and this caused a problem with Safari but not with other browsers.

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.

Ajax call to check if native iPhone application exists on the device?

For our iPhone native application we have a URL : example://
On the iPhone if I type this URL (example://) in safari it automatically opens up my application.
From my "regular" website I have a link which when the user clicks it opens the application. The problem is that if the application is not installed on the iPhone it throws "Unable to open" error.
So before rendering the link on my "regular" site I need to check if the app is installed, one solution is to make an Ajax call and check for status code:
$.ajax({
type: 'POST',
url: 'example://',
complete: function (transport) {
if (transport.status == 200) {
alert('Success');
} else {
alert(transport.status);
alert('Failed');
}
}
});
But this code is always returning a status code "0".
Is there a way to find out from the web if the native iPhone app is installed?
If u are referring to Mobile Safari, you're out of luck. There's no documented public API that I know of that can do this. Mobile Safari is sandboxed away from the OS.
If it's in a webview within an app, u can request the URL and let the webview delegate talk to the native app / query the handling of example://. Otherwise, no way the browser can know existence of any installed app.

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

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