Clojurescript and Phonegap: Setup for a Mobile App

The following instructions will enable you to develop an mobile application mainly using ClojureScript. Phonegap is being used to enable the support of specific phone hardware like the camera. For alternatives see http://heikobehrens.net/2010/10/11/cross-platform-app-development-for-iphone-android-co-%E2%80%94-a-comparison-i-presented-at-mobiletechcon-2010/.

 

Step 1: Setting up the ClojureScript project

If you do not have it on your system already, download and install leiningen following the instructions on https://github.com/technomancy/leiningen. Note that the version of the package manager is probably quite old: when I tried it, the version retrieved by typing sudo apt-get install leiningen was 1.7.1 whereas the version of the manually installed script was 2.4.0. Also note that to run leiningen easily from any point of your system, you need to copy the script to /usr/bin:

$ sudo cp ~/bin/lein /usr/bin/lein

Now create the project and jump in:

$ lein new test-app
$ cd test-app

 

Step 2: Setting up the Phonegap project

Download Phonegap (a.k.a Cordova) and create Phonegap project within the ClojureScript project (to keep everything together):

$ sudo apt-get install nodejs
$ sudo npm install -g cordova
$ cordova create assets com.test-app “Test Application Name”

Add desired target platforms (see http://cordova.apache.org/docs/en/3.0.0/guide_overview_index.md.html#Overview) and hardware support (see http://cordova.apache.org/docs/en/3.0.0/guide_cli_index.md.html):

$ cd assets
$ cordova platform add android
$ cordova plugin add org.apache.cordova.camera
$ cordova plugin add org.apache.cordova.dialogs
$ cd ..

Note: org.apache.cordova.dialogs lets you use native notifications, in step 7 I will use this plugin to test if the setup has been successful.

 

Step 3: Setting up the testing environment

As I am developing on a Linux system (Ubuntu 13.10), Android is the only platform i can test it with for now. But as I understand it, I will only have to use a different system (Windows or iOs) to build it for the other platforms without having to do more than add the platforms with cordova platform add and cordova build. I will report on this as soon as I have made experiences with this.

So to set up a fully functional testing environment for an android application either install an emulator or use your own android device. Since you’re essentially building a web application with a few nasty but effective adaptations most parts will run on a simple web browser as well without even having to build it with cordova build. This is a great alternative as the building procedure takes a few seconds (and thats suuuch a long time…).

I personally use a browser for testing the basic features of my app and my phone to test the hardware interop. The emulator takes a while to start and feels bulky when you want to test something that requires user input. But you only have to start the emulator once and then you can reuse it next time.

Step 3.1: Setting up the Android SDK (required for emulator and (!) device option)

Download the Android SDK from https://developer.android.com/sdk/index.html (“SDK Tools Only” should suffice), then unpack files and make the commands reachable:

$ tar zxvf android-sdk_r22.6.2-linux.tgz -C /opt/android
$ rm android-sdk_r22.6.2-linux.tgz
$ echo ‘PATH=PATH:/opt/android/platform-tools:/opt/android/tools’ >> ~/.bashrc

For testing:
$ cd assets
$ cordova emulate android

… should start the emulator and install and run the provided test-app. You might have to set up an emulator with required hardware first: http://developer.android.com/tools/devices/managing-avds-cmdline.html

 

Step 3.2*: Setting up Devices for Development

1) Plug in device; you don’t have to turn on USB storage.
2) On your device go to Settings and see section System
3) If there is no item named “Developer options”, let’s do some magic:
Go to section About phone and tap Build Number 7 times, then go back to Settings
4) Go to Developer options and tick USB Debugging
5) Run

$ adb devices

5.1) If the last non-empty line is List of devices attached, the SDK doesn’t know the vendor ID of your device. This is commonly the case if the manufacturer of your device is little known, as they do not actually have an own vendor ID but often use 2207 as a default.
You will have to tell the SDK that’s it’s ok anyway:
5.1.1) Find out what the vendor id of your device is:
You might be able to look it up here (first column): http://www.linux-usb.org/usb.ids

Or you run

$ lsusb

With common vendors you will get something like this:

Bus 001 Device 004: ID 2232:1024
Bus 001 Device 003: ID 8087:07da Intel Corp.
Bus 001 Device 005: ID 0bb4:0c03 HTC (High Tech Computer Corp.)
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

With uncommon vendors you will get something like this:

Bus 001 Device 004: ID 2232:1024
Bus 001 Device 003: ID 8087:07da Intel Corp.
Bus 001 Device 008: ID 2207:0010
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

The vendor id in the first case is 0bb4 and in the second case 2207, which I found out by unplugging the device, running lsusb and comparing the results.

If unplugging doesn’t cause a change in lsusb, there’s something wrong about the connection. Try different ports of both devices or a different cable.

5.1.2) Replace 2207 by your vendor ID and run

$ echo ‘0x2207’ >> adb_usb.ini

Note: From this moment on, DO NOT run android update adb as the manual changes will be lost!

5.2) If there is a line following List of devices attached but there seems to be a permission problem, there is a permission problem.
Solve it by replacing 0bb4 with the vendor ID of your device (see 5.1.1) and run

$ sudo echo ‘SUBSYSTEM==”usb”,ATTR{idVendor}==”0bb4″,MODE=”0666″,GROUP=”plugdev”‘ >> /etc/udev/rules.d/51-android.rules
$ sudo chmod a+r /etc/udev/rules.d/51-android.rules

5.3) If there is a line following /List of devices attached/ looking like this:

0123456789ABCDEF device

… be happy!

5.4) Run

$ cd assets
$ cordova run android

This command causes your application to be installed and run on your device if there is one attached or on an emulator otherwise.

Step 3.3*: Setting up Project to Run in Browser

To get rid of browser errors you have to add three files to the www directory of your project, which will keep the app from being deployed properly!

$ cp assets/config.xml assets/www/config.xml
$ cp .cordova/lib/ubuntu/cordova/*/www/cordova*.js assets/www/cordova.js
$ touch assets/www/cordova_plugins.js

Therefore I wrote a little script to switch between the two options:

file refresh:

<br />if [ "$1" == "device" ]<br /> then<br /> cd assets<br /> mv www/cordova.js www/.cordova.js<br /> mv www/cordova_plugins.js www/.cordova_plugins.js<br /> mv www/config.xml www/.config.xml<br /> cordova run android<br /> mv www/.config.xml www/config.xml<br /> mv www/.cordova_plugins.js www/cordova_plugins.js<br /> mv www/.cordova.js www/cordova.js<br /> cd ..<br /> else<br /> cd assets/www<br /> php -S localhost:8008<br /> cd ../..<br />fi<br />

It is far away from being elegant but it it does its job:
I call

$ ./refresh

when I want to test basic browser functions and

$ ./refresh device

when I want to deploy it for testing on my phone.

Step 4: Setting up Files

1) For a lightweight ClojureScript project change the project.clj in your directory to

 <br />(defproject test-app "0.1.0-SNAPSHOT"<br /> :description "FIXME: write description"<br /> :url "http://example.com/FIXME"<br /> :license {:name "Eclipse Public License"<br /> :url "http://www.eclipse.org/legal/epl-v10.html"}</p><p>:dependencies [[org.clojure/clojurescript "0.0-2173"]]</p><p>:plugins [[lein-cljsbuild "1.0.1"]]</p><p>:cljsbuild {:builds {:dev {:source-paths ["src"]<br /> :compiler {:output-to "assets/www/js/index.js"<br /> :optimizations :simple<br /> :pretty-print true}}<br /> :prod {:source-paths ["src"]<br /> :compiler {:output-to "assets/www/js/index.js"<br /> :optimizations :advanced<br /> :pretty-print false}}}})<br />

Note that no org.clojure/clojure dependency is necessary as long as you don’t use any clojure-macros.

2) To test the ClojureScript-Phonegap interop, you only have to change a few things:
First, change a line in assets/www/index.html:

$ sed -r -e ‘s/app.initialize()/test-app.app.initialize()’ -i /assets/www/index.html

Then, create a file src/test-app/app.cljs and fill it with:

<br />(ns traffic.app)</p><p>(defn ^:export onDeviceReady []<br /> (-&gt; (js* "navigator") (.-notification)<br /> (.alert "PhoneGap is working" (fn [] nil) "" "")))</p><p>(defn ^:export initialize []<br /> (.addEventListener js/document "deviceready" onDeviceReady true))<br />

Pay attention to the two different ways to access javascript objects!

Compile ClojureScript to Javascript by running

$ rm assets/www/js/index.js
$ cljsbuild once dev

Now, all your clojurescript functions you marked for export plus the ClojureScript dependencies are compiled into the assets/www/js/index.js. It seems like the compiler keeps the existing index.js file from being replaced and even cljsbuild clean does not have any effect on the initial file – thats why you have to remove it manually.

Later you might want to run

$ cljsbuild clean

before recompiling your code to make sure that you are not using an old version of the index.js getting yourself confused and making you chase bugs that already have been caught.

Intro to “Clojurescript and Phonegap: Setup for a Mobile App”

“What are you studying?”
– “Computer Science.”
“Cool, so you could write an app for what we’re just doing?”
– “Yeah, sure I could.”
“Would you write it for iPhones or for Android?”
– “Android.”
“Ahh, wrong answer, see, most of us here have an iPhone.”

This is a piece of a conversation I had in April. But I never had written an app before, I just guessed I could do it because in the end, programming is programming and nowadays you find instructions for almost anything on the internet, especially in that field. Plus, if I really want to achieve something programming-related, I always succeed – even if it means feeling like trying to run through walls for days, going to bed when the birds have stopped their dispraising song only few hours ago and getting up four hours later… Anyway, a few weeks ago, I decided to prove myself that I actually can do it.

The thing that bugged me though, was that people seemed convinced that you have to decide which platform you were targeting. Of course I said “Android” at the time – I only own Android devices as I would have a bad conscience to pay much more for a device than I have to as long as I’m not rich. (Yet, I have to say, I like the look of apple products and my heart of a perfectionist would be happiest if all my devices would match. But then again – you never know if you will blow yourself up once you have the full set…) But I just couldn’t accept the thought that I would program something and a large amount of people wouldn’t be able to use it. Plus, I didn’t believe that companies that want to make money out of that would accept that. But from what I heard and given the fact that there is relatively little talk about how to achieve platform independency on the internet, it seems like most of them do: they target one of those platforms and if the other group is lucky the app will appear later on their platform, the time gap being due to the fact that it has to be reprogrammed to support their operating system. That is so ridiculous.

I knew there was an easy solution for applications that wouldn’t need access to specific hardware of the device like the camera or GPS functionality for example: building a web application and run it on an embedded browser. But circumstances were that the idea I had for a first test app would require a camera and I was to proud to change my plans. So after a quick research I decided to go with Phonegap which allows you to build an platform independent app by essentially programming a Web app using HTML and JavaScript but with the addition that you have access to phone hardware.

From the beginning, it was clear to me that I wouldn’t be writing JavaScript code as I had fallen in love with clojure. Clojure would force me to write small to-the-point functions, keep me from getting confused with the values of variables due to its functional approach and, most of all, strangely gives me the feeling of playing with Plasticine when processing data.

So this is why I decided for this setup.