Creating an iPhone App for a Drupal Website
Recently, Zengenuity released the LA2M to Go iPhone app, a companion to the Lunch Ann Arbor Marketing website. The LA2M website is one we built on Drupal 6, and it uses custom content types, Views and Imagecache for displaying upcoming and archived event information for the group. In creating our app, I wanted to build a system to synchronize the mobile device with the website, since event information changes on at least a weekly basis. It took a lot of research and testing various techniques (many of which of which didn’t end up working), but finally I worked out how to have the app to pull all its information automatically from the Drupal site. I hope that by publishing this blog post, I can speed up this process for you, and save you some frustration. There isn’t room here to go into every detail, but I’ll discuss the major frameworks I used, and I’ll point out some important issues that wasted a lot of my time.
The Titanium Mobile Framework
LA2M to Go is built with the Titanium Mobile Framework by Appcelerator. The Titanium SDK is free, and it allows you to write native iPhone apps in web developer friendly Javascript, instead of Objective-C. It also allows you to compile the same source code as a native Android app, though this is something I haven’t tried yet. To get started with Titanium, download the SDK here. This will provide you with the Titanium Developer application you need to compile your app. You will also need to have developer tools for the target mobile platform you plan to work with. So, if you are writing an iPhone app, you need to have an Apple iOS developer account, and download XCode and the iOS SDK from Apple. Android developers will need to get the Android SDK from Google.
When coding for Titanium, you have to use your own text editor or IDE, and then load your project into the Titanium Developer application. Titanium-based apps are compiled in a two-step process. First, your code is rewritten by Titanium Developer as native iPhone or Android source code. Then, command line tools provided by the iPhone and Android SDKs are used to compile the native code and install it into the simulator or directly onto your mobile device.
One word of warning: This process absolutely sucks. It’s the worst compilation and debugging system I’ve ever experienced. When working with Titanium, be prepared to write a lot of output log statements, because that’s how you’ll be doing much of your debugging. Also, the Titanium platform is seriously lacking in documentation, and the docs that do exist are difficult to use and search. There are very few example code snippets in the API docs, which means you need to search the developer forums. In the forums, code is often wrong or out-of-date, as there’s no way to know which version of the Titanium API it’s designed for. (The API has changed significantly from its initial release.) Appcelerator recently purchased Aptana, the makers of the excellent Aptana Studio IDE. So, I’m praying they ditch the Titanium Developer application for a more fully-featured IDE, with debugging tools and built-in documentation. This would help developers immensely. (Update (5/17/11): They have now done exactly this: http://developer.appcelerator.com/blog/2011/05/major-titanium-updates.html)
That said, I’ve written code in Objective-C with XCode before, and while XCode has both excellent debugging features and documentation, the speed at which you I create robust apps with Titanium, in a language that is more familiar to me as a web developer, means I’ll coming to back to it for my next app, despite its failings.
The DrupalCon Chicago App
If you were at DrupalCon Chicago, you probably used the conference app that was available for iPhone and Android. That app was created in Titanium, by the folks at Palantir. Fortunately for all of us developing Drupal-connected apps, they have made the app’s source code available, and their blog post about it is very informative.
There isn’t a lot of technical documentation about how expand or change the app, but after spending some time with it, I was able to figure out the key details:
Schema Definitions
The app is designed to import Drupal nodes and store them in an SQLite database on the device. This way the app can be used offline. In the code, there is a schema definition designed for the DrupalCon event node type in the Resources/drupalcon/entity.js file. (You will also find a schema for Drupal.org users in there, too.) If you are building a new app from this source code, you need to modify their schema to match whatever fields your content types have. They have chosen to call their event schema “node”, but you can rename this to whatever you want, and add additional schemas for more types. If you are adding new schema types (or changing the name of them), you need to make entries for them in the Resources/drupal/entity.js file. There’s a section at the top that starts with
main: { ... types { [a list of types and primary keys is here]
Add your new schema names to this list, following the pattern of the others in that section.
One thing I found useful when modifying the schema were the two commented out lines in app.js that clear the DB on every app launch.
//Drupal.entity.db('main', 'node').initializeSchema();//Drupal.entity.db('main', 'user').initializeSchema();
These obviously need to be commented out in production, but are useful when changing the schema. (Adjust the parameters to fit your schema names.) Another helpful tool you will want to have is some sort of SQLite database browser. I ended up using the SQLite Manager Firefox extension, though, I didn’t especially enjoy it. It works, but it’s kind of clunky. If you are building and iPhone app and working in the simulator, the SQLite database is difficult to find. Here’s where it’s at:
/Users/[your_username]/Library/Application Support/iPhone Simulator/[sdk_version]/Applications/[application_guid]/Library/Application Support/database/main.sql
Yeah...it’s really buried down there. One of those folders is hidden too, so you will not find that database file with Finder. You just have to know where it is.
Renderers
If you look in Resources/drupalcon/drupalcon.js, you will see a series of sections that start like this:
DrupalCon.renderers.session = function(session) {
These sections define “renderers” for different entities that you have in the SQLite database. They don’t do anything on their own. You have to call it yourself after you query the DB. But, when you look around in the app code, you’ll see that having a function that renders an SQLite database row as a TableViewRow is very handy. It’s sort of like defining a theme template for a particular node type. If you have added fields and types to the database schema, you will need to update the renderers to display your new fields.
Windows
In Resources/windows are a bunch of files that define the various screens of the app. I don’t have space in this post to go through them all, but if you look at each one, you’ll get an idea of what you need to do to make similar windows that match your project. Most of them are TableViews, which is often what you want. There’s also a MapDetailWindow that shows a static image, and other windows to render HTML content. Be sure to include any new window files you create in your Resources/app.js file. (There’s a section in it with all the includes in a list.) Also, you need to add your new windows to the app’s tab group. That can be done by following the pattern in Resources/main.js.
Connecting the App to Drupal
When I started this project, I expected to use the Services module to access the Drupal data over JSON. My plan was to create a view for each content type that I needed to download and expose those views with the Views Service module. However, when trying this approach, I came across a big limitation of Views Service. This module only exposes the Views data that is directly returned by the query. It completely skips the normal rendering step of Views. This means if you want to include the URL for a specific Imagecache-processed version of an image, it can’t be done with Views Service. You also can’t rewrite the output of fields to combine two fields into one. Pretty much everything that is awesome about Views you can’t do with Views Service.
Views Datasource to the rescue! The day after I hit this limitation, Palantir released their code for their DrupalCon app. In their post, you will see they reference the Views Datasource module as an alternative to Services. Views Datasource allows you to expose Views as JSON, and it DOES send the output through the normal Views rendering engine. So, this means you can rewrite fields, get Imagecache urls, change field labels, and probably re-theme fields if you need to, though I didn’t try this last one. With this module, I was able to get exactly the JSON output I needed to download data into the app. I’m not going to go into a full detailed explanation, because actually, if you know how to use Views, it’s pretty straightforward. It works exactly like you expect it to.
Putting Everything Together
Once I had the JSON-formatted view on the Drupal site and the schemas defined in my application, everything else was just a matter of getting of the user experience and workflow that I wanted. (Ever app is different, so you’ll have to study the Titanium API and figure out what works for your project.) After only a couple weeks of development, I was able to go from conception to launching LA2M to Go in the App Store. While it was frustrating at times deciphering both the Titanium debug output and the DrupalCon app’s design, I’m really happy with the outcome. I’ll definitely use both of these technologies in future for Drupal-connected iPhone apps, and hopefully, this post will make it a little less frustrating for you to do the same.
Useful Resources
Getting Started with Titanium Mobile Platform
How to Create a Pull-to-Refresh Table in Titanium - Everyone loves this UI control.
Drupal-iOS SDK - A separate project that connects an iPhone app to a Drupal site using only native code in XCode. Have not tried this one out.