Most apps have a website. It could be just a launch page, but also a full web-version of the app, like YouTube. In that case, the app is often the prefered way to view the content. But to share it you’d want to use the web URL so that people without the app can also get to it. So how to automatically open the content of a specific webpage in the app for the people who have it, without constantly disturbing the ones who don’t or visit the webpage on their desktop or another unsupported device?
NOTE: This post is outdated. There have been lots of issues and changes related to opening apps via intents and responding to URL schemes. See TIMOB-20490 for more information.
URL Schemes
The key to launching an app is to define an use a custom URL scheme. Like websites URLs start with the http://
scheme, every app can register its own, like myapp://
.
Jason Kneen did an excellent talk explaining what more can be done using URL schemes except just opening an app.
The goal
What we want is to open the app with myapp://view?id=123
when an user with the app installed visits http://myapp.com/view?id=123
. In this first blog, I will give a very detailed recipe on how to define, receive and parse a custom URL. In a next blog, I will show how to set up your web page in such a way that the flow from email/webpage to app using these URLs is as seamless as we can get it.
1. Defining the URL scheme
iOS
Define your scheme in tiapp.xml
like shown in the next snippet. The CFBundleURLName could be anything but best keep it the same as your app’s id. If you’re using the Facebook module or another module exposing it’s own custom URL scheme for your app, don’t forget to add that one as well. You’ll find out by first building your app without this modification and then opening build/iphone/Info.plist
.
Android
Build your app and open build/android/AndroidManifest.xml
. Copy the first <activity>
node to tiapp.xml
like shown in the next snippet. If you don’t have ti:app/android/manifest/application
just create the necessary elements. If you have, you can leave any custom attributes in there. There are 2 modifications to make. First add the second <intent-filter>
and set your scheme. Next, you probably want to add the android:launchMode="singleTask"
attribute to <activity>
. Read on to learn why.
LaunchMode
By default, when launching your app via another app, e.g. using the URL scheme in the browser, your apps main activity will be stacked onto the current app’s task. This might be what you want if the launched activity is for showing a map or sending a tweet, but as for your main app activity, this means that if your app was already running in the background or is launched directly later, you will have two instances of it!
Fortunately, by adding android:launchMode="singleTask"
attribute to <activity>
you can prevent Android from doing this. Optionally add android:alwaysRetainTaskState="true"
to stop Android from resetting your app’s state if it hasn’t been used for some time. Thanks to Jason for finding this out. Read more about launchMode in the Android docs.
Unfortunately, if your app uses heavyweight windows, using the singleTask launchMode will cause your app to hang after its first launch. So make all windows lightweight if you can and please watch the JIRA ticket for further updates on this bug.
2. Receiving the URL
When your app is opened using its URL scheme, you can find out what the exact URL was and act on it accordingly. This is a different process on both Android and iOS. On iOS, you can read Ti.App.getArguments().url
both when the app is first opened and when it resumes. On Android, you have to read Ti.Android.currentActivity.intent.data
asap. In the next Alloy example this means you have to read it in alloy.js
before your index controller gets created and then store it in Alloy.Globals
for later use. In a classic Titanium project you would do all this in app.js
.
app/alloy.js
app/controllers/index.js
3. Parsing the URL
Because URL schemes can be used by other apps to open your app, it is wise to follow the x-callback-url standard. I won’t get into that now, but in the code above I do use a slightly modified version of their Titanium URL parser, which can be found in my UTiL repo. This would parse myapp://view?id=123
in such a way that you can get view
using action()
, all parameters via params()
or a single one using param('id')
. Of course directly using these to open a controller is not the most wise thing to do.
Part 2
In part 2 I’ll show you how to open your app from an email or webpage with minimal effort.