Deep linking inside of cordova apps build with angular is a common request. It’s a fairly easy task to get the deep link URL. Write a global function to handle the call to handleOpenURL then do stuff inside of it to move to the correct place in your application. Android users will need this plugin. https://github.com/EddyVerbruggen/Custom-URL-scheme
Doing this inside of AngularJS and Ionic bring a few extra challenges. First, you have the problem of getting the global call to handleOpenURL to be handled inside of your Angular application. There are a few solutions out there like fetching the body element, getting the scope and calling a function. I found these to be a bit hacky.
Next you have to deal with a race condition. The time between handleOpenURL being called and your application varies greatly if your app is starting from cold or from the background. Some solutions just wait X amount of seconds. As you can guess this isn’t ideal.
The final problem is with Ionic, well ui-router technically. It has no concept of history inside the state system provides by the router. Ionic fills this gap with their $ionicHistory service. Where this falls down though is in deep linking. If you jump straight to a state even one level deep. It doesn’t know how to come back up that tree. So the ugly hack is to use nested $state.go calls, or use modals for the views you want to pop to the front. Again both not ideal.
So here’s a solution we’re using to tackle these issues to provide deep linking into our application from a custom URL or from push notifications.
The first change is we need to give our state model an idea of the hierarchy of the states. This will let us page back through states in a predictable way if we jump into any state in our application.
The state of tab.feeds/feed shows that the tab.feeds state is a parent of that state. State hierarchy is denoted by the ‘/’.
This is not to be confused with parent concepts inside of ui-router.
Back Button Directive
Now we need some way to workout when we’re deep in our application and how to get us back to the top level view. A directive is perfect for this. The code below creates a back button that only shows when there is a parent view to navigate to but there is no back view in the history.
The interesting part here is the canGoBack method which checks the current state. Splits it by ‘/’ and works out if there is a fake state to jump back to. It only returns true if there is no back state in history already. The next key part is in the $ionicHistory.nextViewOptions() call. We disable the history and back button. If we didn’t back would actually be forward were we just came from.
Using the directive, you’ll want to place it on nested views in between the ion-view and ion-content directives.
// TODO: I’m sure this can be improved by hooking into ionic’s actual back button
Handling Custom URLs
Next we need to take care of the race conditions and global handling when handling custom URLs. When looking at this problem, I was thinking. Why doesn’t this work like the deviceready event. Why can’t I attach an eventListener that is triggered if the openURL has already been found and handled.
A bit of digging revealed that cordova has functionality to create sticky event handlers. So we can use the exact same functionality used to handle the deviceready event.
This code in your main app.js file will handle the global call to handleOpenURL and fire off a cordova document event. This is designed to work with cordova.addStickyDocumentEventHandler so that once this event has been fired, the result is stored an any listeners attached to it in the future will be given the historic result. It also works as expected if the listener is attached before the event is trigger. This eliminates the race condition problem
Now we just need something to consume the event as we would any other event and change the location of the application when it’s fired. Note the changes to $ionicHistory. These make sure that view history will not be preserved when jumping into this state. This ensures that our fake back button directive will work from earlier.
Rounding this all up
Well I hope this helps someone get there deep link on inside of their Ionic app with ease in a clean, and testable way. The beauty about this is if you keep your urls and state kinda identical when mapping out the states. It makes it super simple to use the same system to deep link using an object from a push notification. But I’ll leave that for later.