UPDATE : Another Ionic developer has a more elegant solution to this problem on the forum.

Last week, someone on the Ionic Framework forum asked about integrating custom URL's in an Ionic app using Cordova's Custom URL Handling Scheme. I dug into it a bit and came up with a solution. Here, I'll finally document it in a bit more detail.

Shameless Self-Promotion : Please checkout Kids In Touch - a family friendly messaging app for young kids.

In case you aren't familiar with custom URL's on mobile, let me explain. Suppose you have an app that can send invites or notification to other users of the app via SMS or email. You would want the receiver to tap the link and have it automatically open your app. However, how do you tell a mobile device to open an app for a specific URL? For example, you'd have some custom URL like : http://MyGreatApp?state=invite?inviteId=a382-dkd3

To get this to work, you have to register your custom URL with your app. Once that custom URL is registered, the device will observe any requests for that URL and then open the associated app. When a Cordova app gets opened via a registered URL, the app has to have a global function for dealing with the request.

On the forum, the original poster was wondering how to do this in an Ionic app. As I explained, this was not really an Ionic question but more of an Angular question. In general, how do you get external code to communicate with an Angular app. Usually, you try not to do this. However in some cases such as this one, you must implement some means to have legacy code "talk" to your AngularJS app. This handly Stack Overflow answer expalains how to allow this type of communication.

Okay, so how do we use this info to create an Ionic Framework app that handles custom URLs. Let me walk through it.

First, let's build a very simple Ionic app.

> cd ~/Documents/Apps
> ionic start ionic start customUrlApp blank
> cd customUrl

That code is simply going to create a new app called 'customUrlApp' based on a "blank" Ionic template. It's going to be pretty bare bones.

Now, we need to setup the mobile platform we'll be working with.

> ionic platform add ios

At this point, you could go digging about in Xcode to register your custom URL. However, it's a bit of a pain to do.

Cordova plugin to the rescue! Eddy Verbruggen wrote the excellent LaunchMyApp plugin to make this so much easier. Run this code to register your custom URL.

> cordova plugin add https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin.git --variable URL_SCHEME=MyGreatApp

So, now our app has a custom URL scheme registered. Unfortunately, we aren't done yet. When such a URL is hit, Cordova needs a global function to handle the request. That function must be named handleOpenURL and can accept a URL as a parameter function handleOpenURL(url).

Once the function is called, we'll somehow need AngularJS to deal with it. First, let's modify our "index.html" to have a "Main Controller".

<!DOCTYPE html>
<html ng-app="starter">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">

    <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
    <link href="css/ionic.app.css" rel="stylesheet">
    -->

    <!-- ionic/angularjs js -->
    <script src="lib/ionic/js/ionic.bundle.js"></script>

    <!-- cordova script (this will be a 404 during development) -->
    <script src="cordova.js"></script>

    <!-- your app's js -->
    <script src="js/app.js"></script>
  </head>
  <body ng-controller="MainController">

    <ion-pane>
      <ion-header-bar class="bar-stable">
        <h1 class="title">Custom URL</h1>
      </ion-header-bar>
      <ion-content class="padding">
        <p >Has Launched : {{data.launched}}</p>
      </ion-content>
    </ion-pane>
    
  </body>
</html>

In the snippet above, I've moved where the AngularJS app is initialized to the <html> tag. In the <body>, I've added the new MainController. I've also added a <p> to the body to show if the app has been launched via a URL or not.

Now, we need to edit the "app.js" to do two things. First, we need to add the MainController. Then, we need to add the global handleOpenUrl function.

angular.module('starter', ['ionic'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})

.controller("MainController", [ '$scope', function($scope) {

	$scope.data = {
		"launched" : "No"
	};

	$scope.reportAppLaunched = function() {
		console.log("App Launched Via Custom URL");

		$scope.$apply( function() {
			$scope.data.launched = "Yes";
		});
	}

}]);

function handleOpenURL(url) {

    var body = document.getElementsByTagName("body")[0];
    var mainController = angular.element(body).scope();
    mainController.reportAppLaunched(url);

}

Let me explain what's happening here. In the handleOpenUrl function, we're using a tag selector to get the body. Because the body has been associated with the MainController, we're then able to access the scope of the controller. Once we have the scope, we can access any method or property in it. I chose to use a reportAppLaunched method so I could do a few things like printing to the console and updating the model.

So, when the app is launched via a URL, the handleOpenURL function is called. It gets the scope of the MainController and then updates the model's launched property. Notice that the property update must be wrapped in $scope.$apply. This is because the property is being updated outside of an Angular app's digest cycle. If this is not wrapped, the view will never update.

Let me know via Twitter if you have any suggestions for improving this app.

Also, the full code is available on GitHub : https://github.com/calendee/ionicCustomUrl

Here's a sample of this running in Chrome. Notice, I'm just manually callin the "handleOpenUrl" method from the console.