Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

External Links in Cordova for iOS


:P
On this page:

Hybrid app development with Cordova can be challenging at times. While it makes so much sense to build Web applications that can run as an app in a WebView container that can run on most device platforms, it's good to remember that these apps are not 'just' Web apps. There are all these little gotchas that you run into with seemingly simple things that work in a normal Web application but have wonky behavior or work very differently when running inside of a WebView container. Here's one of these fun ones: External links that should not open inside of the current application but in a full web browser instance. It sure is a lot harder than it should be.

What's the problem? External Links stay in the WebView

Cordova applications run in a WebView container in iOS and one of the gotchas that you are likely to run into if you have any external links is that external links will try to display in the current WebView of your application. So lets say in my AlbumViewer application I'm viewing an album loaded from the application which displays content from an internal HTML link (or a re-rendered view in this case from Angular).

Then there are a few links on the page that link to external content – in this case external links to buy an album on Amazon or play it on Spotify. Here's what this looks like in my sample app:

AlbumViewerButtons

On a Web page you might do something very simple to link these external URLs to Amazon or Spotify - in this case by simply having HREF links like this:

<a ng-href="{{view.album.AmazonUrl}}" class="btn btn-sm btn-default" target="_blank"><i class="fa fa-dollar" ></i> Buy</a>

and that works fine in the browser. Because of the target="blank" the window opens in a new tab and you can easily get back to the original tab. Even in the same window without the target="blank" you can always use the back button to get back.

However, in a Hybrid app running in a WebView you don't have tabs or a back button. There's no browser chrome and you can't use a backspace key or swipe right to go back since those gestures are not supported:

NoNavigation

You're stuck on this page.

This behavior is actually what you want most of the time. Since hybrid apps are supposed to be 'apps' and not just wrapped Web pages, apps should provide for their own navigation features. You shouldn't be able to arbitrarily jump around the application by moving back and forth unless you explicitly expose that functionality as part of your UI.

That's all well and good, but in the code above this obviously not the behavior we want – we want to navigate to external content and then somehow actually get back. Target links don't do it and neither does the following script code calling window.open():

vm.openAmazonUrl = function(album) {
    window.open(album.AmazonUrl,"_system");
}

Even the explicit window.open operation forces the window to open in the WebView rather than in a new browser window. Note that the behavior varies. Android for example, does the right thing with window.open() and opens a new window. iOS… not so much.

Low Level Fixes

iOS requires a low level workaround to this problem and the workaround for this problem is – you guessed it – a plug-in. It seems really sad that something so simple requires an actual plug-in to work. The solution on iOS lies with a very low level fix – which is o create a custom Objective-C handler for the Web View control that detects external link requests and then opens them externally? Are you serious? Here's an old Stackoverflow Question that goes over a few solutions that no longer work with the exception of the Objective-C hack. Crazy huh?

The InAppBrowser Plug-in

Luckily there's a cordova.inAppBrowser plug-in that encapsulates this hack in an easy to add plug-in. This is a much simpler solution that doesn't require hacking the generated cordova WebView wrapper code that can be overwritten by updates. The plug-in basically provides the ability for window.open() to open a new window in the external browser.

You can add this plug in with:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git

in your Cordova project or – if you're using Visual Studio's Cordova Tools by adding it from the Visual Studio add-in Configuration page.

The plugin basically replaces the window.open() function inside of the WebView control and so causes a new instance of the device browser to open – on iOS that'd be Safari. So rather than using a direct link in my Angular app I had to change the code a bit to either using an onclick handler or an Angular call to a controller method:

<a ng-click="vm.openAmazonUrl(view.album)" class="btn btn-sm btn-default"><i class="fa fa-dollar"></i> Buy</a>

and then adding this function to the controller:

vm.openAmazonUrl = function(album) {
    window.open(album.AmazonUrl);
}

And here's what you get:

safari

It's a full instance of Safari opened in a separate window. More importantly you see both this browser view and the original application in the task list so you can switch back and forth easily:

tasklist

Handling Target Links

With the plug-in installed you can also simplify the process a bit more with a little bit of script code to capture target links and then automatically opening them in the browser. You can use the following as part of the startup code in your Cordova app:

window.addEventListener('load', function () {    
    $(document).on('click', 'a[target="_system"],a[target="_blank"]', function (e) {
            e.preventDefault();
            var url = this.href;
            window.open(url,"_system");                    
    });
    //}
}, false);

This code basically looks for anything that has _blank or _system in the target tag and if it does, routes that to window.open() instead. This makes it a little easier to use the functionality so that you don't have to hook up code just to open a new window. So instead calling my Angular handler or using an onclick that calls window.open() I can use a simple link instead and essentially get the behavior I'd normally expect in the browser:

<a ng-href="{{view.album.AmazonUrl}}" target="_blank"  
class="btn btn-sm btn-default"><i class="fa fa-dollar"></i> Buy</a>

Much nicer and more importantly allows me to leave my existing links – assuming they go to the right target – intact without having to change code specifically for Cordova.

Summary

It's amazing how complicated some simple things like this can be, isn't it? It seems like this would be trivial to handle natively inside of the WebView control. A window.open() *should* go out to a new browser window just like it would in a browser. Some devices – notably recent versions of Android – do get this right and work without requiring a plug-in to make this happen. On those platforms that natively support browsing to a new browser the implementation is just passed through. Unfortunately other platforms like iOS do require this lousy workaround and so we're stuck with using a plug-in. It's easy enough once you know what needs to happen and what the problem is. It's just another one of those weird things you have to remember. Hopefully this blog post helps finding this info.

Resources

Posted in Cordova  Mobile  

The Voices of Reason


 

Patrick
July 29, 2015

# re: External Links in Cordova for iOS

Great article, ran into this same problem. From the plugin docs, I think the
window.open
in your
vm.openAmazonUrl
function should include a "_system" argument to trigger the external browser launch?

Benjamin
October 03, 2015

# re: External Links in Cordova for iOS

Hey Rick,

No matter what I do I can't get links to open in Chrome on Android 5.1.1 instead of the in-app browser. I've tried your solution here and many more. I posted a StackOverflow question. Perhaps you could help?

http://stackoverflow.com/questions/32928318/using-cordova-how-can-i-open-external-urls-in-chrome-instead-of-the-in-app-bro

Luís Cunha
November 02, 2015

# re: External Links in Cordova for iOS

Is there a way to set all links to be opened in the system browser by default (window.open(url,"_system"); )? Google maps are basically unusable on iOS in a webview app as it dynamically creates links that, if clicked by the user, trap the user in whatever page it directs to, without any way to go back besides killing the app and reopening it.
How would be a good way to just open everything in the system browser?

Micro
June 01, 2016

# re: External Links in Cordova for iOS

Thanks Rick,

But this doesn't work with the anchor link inside the iframe.

http://stackoverflow.com/questions/37551267/anchor-link-inside-iframe-not-working-on-android-app

Chechs
March 14, 2017

# re: External Links in Cordova for iOS

Hi Rick,

Is there any way for links inside the In-App browser window to be opened in system browser?

Like I open a site www.internalsite.com using the in-app browser. And I have some links present on that site. Now I want to open that site in external, system browser like Chrome/Safari. How's that possible?

Thanks


Laxman
August 09, 2017

# re: External Links in Cordova for iOS

Hi Chechs, did you find any solution for your problem? I am having the same issue. widow.open([url], "_system") works only inside the app layer, it does not seem to work on a webpage inside the cordova app.

Thx


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2020