Fixing InAppBrowser not being able to open ‘_system’ links (e.g. Facebook) in Cordova and Phonegap

InAppBrowser provides you the ability to open links in a custom browser in the app, or in an external browser.  In my case, dynamic content is coming into the app where a user can open external links.   All links would work, but for some reason, certain facebook links wouldn’t work such as

cordova.InAppBrowser.open('https://www.facebook.com/events/1701263153475443/', '_system');

I performed these following tests:

Scenario 1:

  • When the Facebook app was installed on the iPhone, Facebook links wouldn’t work
  • When the Facebook app was removed the Facebook links would work.

 

If you take a look at

https://github.com/apache/cordova-plugin-inappbrowser/blob/master/src/ios/CDVInAppBrowser.m

- (void)openInSystem:(NSURL*)url
{
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url];
    } else { // handle any custom schemes to plugins
        [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
    }
}

InAppBrowser eventually calls canOpenURL on the links.  When reading the api docs canOpenURL checks to see if the link is openable by any app.

Now if you notice on the iPhone, when you click on a link like http://google.com/maps it opens the Google Maps.  This is because when the Google App is installed, it registers a a URL Scheme.

Doing a little Googling yields a page about IOS9 and Facebook.

https://developers.facebook.com/docs/ios/ios9

The docs aren’t exactly clear, but what is that Cordova can’t open the Facebook links because it is missing a schema

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>fbapi</string>
    <string>fbapi20130214</string>
    <string>fbapi20130410</string>
    <string>fbapi20130702</string>
    <string>fbapi20131010</string>
    <string>fbapi20131219</string>    
    <string>fbapi20140410</string>
    <string>fbapi20140116</string>
    <string>fbapi20150313</string>
    <string>fbapi20150629</string>
    <string>fbauth</string>
    <string>fbauth2</string>
    <string>fb-messenger-api20140430</string>
</array>

For Phonegap there was this post describing a fix to the problem.

Add this to config.xml and you should be good

 <gap:config-file platform="ios" parent="NSAppTransportSecurity" mode="replace">
		<dict>
			<key>NSAllowsArbitraryLoads</key>
			<true/>
		    <key>NSExceptionDomains</key>
		    <dict>
		        <key>facebook.com</key>
		        <dict>
		            <key>NSIncludesSubdomains</key>
		            <true/>                
		            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
		            <false/>
		        </dict>
		        <key>fbcdn.net</key>
		        <dict>
		            <key>NSIncludesSubdomains</key>
		            <true/>
		            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
		            <false/>
		        </dict>
		        <key>akamaihd.net</key>
		        <dict>
		            <key>NSIncludesSubdomains</key>
		            <true/>
		            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
		            <false/>
		        </dict>
		    </dict>
		</dict>
	</gap:config-file>
    <gap:config-file platform="ios" parent="LSApplicationQueriesSchemes" mode="replace">
		<array>
			<string>fb</string>
         	<string>twitter</string>
          	<string>gplus</string>
          	<string>pintrest</string>
          	<string>instagram</string>
          	<string>youtube</string>
          	<string>vnd.youtube</string>
          	<string>yelp</string>
          	<string>linkedin</string>
          	<string>tumblr</string>
          	<string>whatsapp</string>
          	<string>snapchat</string>
          	<string>comgooglemaps</string>
          	<string>amazon</string>
          	<string>skype</string>
          	<string>googledrive</string>
          	<string>itms-apps</string>
          	<string>paypal</string>
		    <string>fbapi</string>
		    <string>fbapi20130214</string>
		    <string>fbapi20130410</string>
		    <string>fbapi20130702</string>
		    <string>fbapi20131010</string>
		    <string>fbapi20131219</string>    
		    <string>fbapi20140410</string>
		    <string>fbapi20140116</string>
		    <string>fbapi20150313</string>
		    <string>fbapi20150629</string>
		    <string>fbauth</string>
		    <string>fbauth2</string>
		    <string>fb-messenger-api20140430</string>
		    <string>fb-messenger-api</string>
		    <string>fbshareextension</string>
		</array>
    </gap:config-file>

Series: Ionic 2 App, Part 2: Set-up, Configuration, and Tabs Overview

In part 1 we learned a little bit about the basics of a mobile hybrid app.  Now let’s get to the details of how to set up our environment.  You can also follow this link:

http://ionicframework.com/docs/v2/getting-started/installation/

Some prerequisities:

  • Mac or ubuntu (you can use brew if you are using a mac)
  • npm installed
npm install -g ionic@beta
npm install -g phonegap [for phonegap]
npm install -g cordova@latest [for phonegap

Note that Ionic is still in beta, so some of the cli auto generation doesn’t come out quite correctly.

To start off run

ionic serve

//which outputs
Cordova CLI: 6.0.0
Ionic Version: 2.0.0-beta.3
Ionic CLI Version: 2.0.0-beta.22
Ionic App Lib Version: 2.0.0-beta.12
OS: Distributor ID: Ubuntu Description: Ubuntu 14.04.3 LTS 
Node Version: v5.4.1

ionic serve is useful when you need to submit a bug via github or ask for help on the Ionic Slack Forums.

Create a project by running

ionic start tutorial --v2

and then run

ionic serve

On your browser you should now be able to see:
Capture

Let’s take a look at the project structure:

Capture

  • Root – the root folder is tutorial and there is config.xml.  That will be important later for testing on your phone and deploys
  • app.js – This is where the app starts initializing
  • providers folder – this doesn’t exist yet, but will be where your data mappings will be
  • pages folder – This is where all of your pages will live.

Before that, let’s take a look at the main file app.js

 

import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';


@App({
  template: '<ion-nav [root]="rootPage"></ion-nav>',
  config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
  static get parameters() {
    return [[Platform]];
  }

  constructor(platform) {
    this.rootPage = TabsPage;

    platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      StatusBar.styleDefault();
    });
  }
}

There is a lot going on here, so let us break things into sections

Tabs and Imports

  • Lines 1-3 the import lines import the class so you can use it the current class.  If you have coded Java before, this may seem very familiar to you.  You can read more from this blog.
  • In particular let us focus on line 3 which states
    import {TabsPage} from './pages/tabs/tabs';

What this is saying is it is importing the ‘TabsPage’ class from the location ‘pages/tabs/tabs’.  Even though it doesn’t state it, it is implicilty referring to tabs.js

If you open up tabs.js you will see

export class TabsPage

Notice that the import ‘TabsPage’ matches the name of the import.

Root Page

Now if you take a look at line 16, this is setting the rootPage of your app to the TabsPage.  Note that it can be any arbitrary page.

Tabs Page.js

import {Page} from 'ionic-angular';
import {Page1} from '../page1/page1';
import {Page2} from '../page2/page2';
import {Page3} from '../page3/page3';


@Page({
  templateUrl: 'build/pages/tabs/tabs.html'
})
export class TabsPage {
  constructor() {
    // this tells the tabs component which Pages
    // should be each tab's root Page
    this.tab1Root = Page1;
    this.tab2Root = Page2;
    this.tab3Root = Page3;
  }
}

  • In the tabs.js you will note a bunch of new imports.
  • In line one, you have an import from a core class
  • In lines 2-4, you have imports for other pages
  • And if you notice in lines 14-16 variable instances on the TabsPage are being set to the pages.

Tabs.html


<ion-tabs>
  <ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="pulse"></ion-tab>
  <ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="chatbubbles"></ion-tab>
  <ion-tab [root]="tab3Root" tabTitle="Tab 3" tabIcon="cog"></ion-tab>
</ion-tabs>

Here you see that in the ion-tabs element, it is being set to ‘tab1Root’.  tab1Root is mapped to ‘Page1’ so when you click on the ‘Tab 1’ page it loads.

Here ionic defines special ‘ion-tabs’ tags (which is described on the api here).

Review:

Capture

  • When ‘ionic serve’ is run app.js loads
  • In this scenario, TabsPage is instantiated (e.g. new TabsPage() )
  • When the constructor runs in TabsPage, any class variables are set
  • In the tabs.html, those variables are implicitly available without references ‘this.’

To Do:

  • Open page1.html and change some text
  • Open page2.html and change some text
  • Open tabs.html and change the ‘tabTitle’

Get a feel of what is changing

Next Post:

  • Stay tuned for a post of how to render data and use providers.

Extras:

Subscribe to our newsletter to get notified of new articles in the Ionic 2/Tutorial Series!

[mc4wp_form id=”47″]

Adding Cordova Facebook SDK 4 Plugin to a Phonegap Project

Recently I had a need to install the Facebook SDK to help Facebook keep track of app installs.

When reading this documentation:
http://ionicframework.com/docs/v2/native/Facebook/

Running this command
cordova plugin add cordova-plugin-facebook4 --save --variable APP_ID="123456789" --variable APP_NAME="myApplication"

Doesn’t quite generate the right edits to config.xml.  If you are using phonegap, use these settings for config.xml

<gap:preference name="android-build-tool" value="gradle" />
<gap:plugin name="cordova-plugin-admobpro" source="npm" />

<gap:plugin name="cordova-plugin-facebook4" source="npm" spec="1.7.0">
<gap:param name="APP_ID" value="app_id_value" />
<gap:param name="APP_NAME" value="app_name" />
</gap:plugin>

And you should be good to go.