Using Yeoman With Phonegap and Backbone.js

Being at µForge, we had to face some serious problems trying to fix and use a correct workflow for our Phonegap application.

The application has its core built in Backbone.js and uses jQuery Mobile to render itself. It also comunicates with a Django server that has an API built using Tastypie to get/set the data.

The app is being compiled for Android and I actually have no experiences with Phonegap’s iOs building process, but the principle is sureley the same.

I dont give a f**k about your app, just show us the code!

OK, OK, use the following steps to setup all the base things needed to correctly build a web app with Yeoman using a single Gruntfile:

NOTE: I’ve used the Phonegap 2.7.0 version for this example app, I’m sure that you’ll figure out how to make it work for Phonegap 3+ (afaik it’s easy).

The Setup

First of all, use THIS guide to setup the Phonegap environment. Basically you need to install the Android ADT bundle and setup the global path to make you have the adb command in the terminal and stuff like that. Create a basic app using the Phonegap binary (inside phonegap-2.7.0/lib/android/bin/)

1
./create ../../../../helloapp/hello_android com.example.hello hello

Install Yeoman and the basic backbone generator (or the basic generator-webapp if you prefer)

1
npm install -g yo && npm install -g generator-backbone

Create a Yeoman project inside the helloapp folder that you’ve created before with the Phonegap’s create command

1
2
3
cd helloapp # whenever the helloapp is
mkdir Hello && cd Hello
yo backbone --coffee --template-framework=handlebars # You can use different configurations ofc

As you can see, this command will generate a shitload of files in the current directory. Note that the directory name has the first letter uppercase, ‘cause the backbone generator will use that name as the basic namespace.

Install Ripple for Chrome if you want to have a nice looking of the whole app in a pseudo-device. Unfortunately the Phonegap “integration” seems to be broken at the moment, so if you activate the plugin just say that you are testing a normal mobile Web app.

So here’s the trick, you must delete all the asset folder inside helloapp/hello_android/assets/www to allow you to build the app from Yeoman. Create a link from the dist directory of the Yeoman app to the www directory of the Phonegap – android app.

Don’t forget to copy the cordova*.js file to your vendor folder of the Yeoman app.

1
2
3
cp ../hello_android/assets/www/cordova*.js app/scripts/vendor
rm ../hello_android/assets/www/* -rf
ln -s ../hello_android/assets/www dist

Your directory tree should look pretty much like this:

1
2
3
4
5
6
7
8
9
10
11
12
├── Hello
│   ├── app
│   ├── dist -> ../hello_android/assets/www/
│   ├── node_modules
│   └── test
└── hello_android
    ├── assets
    ├── bin
    ├── cordova
    ├── libs
    ├── res
    └── src

Basic configurations

Add these lines in the <head> section of the app/index.html file

1
2
3
<!-- build:js scripts/cordova.js -->
<script src="scripts/vendor/cordova-2.7.0.js"></script>
<!-- endbuild -->

Open the Gruntfile and add these two lines

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Inside the Gruntfile...
copy: {
    dist: {
        files: [{
            expand: true,
            dot: true,
            cwd: '<%= yeoman.app %>',
            dest: '<%= yeoman.dist %>',
            src: [
                'cordova-*.js', # these two lines are needed to make Yeoman include the cordova js plugin
                'cordova_plugins.json', #
                '*.{ico,txt}',
                '.htaccess',
                'images/{,*/}*.{webp,gif}'
            ]
        }]
    }
},

Disable yuglify if you are using backbone, because it does not allow the app to load the files in the correct order. I’ve noticed that if you don’t do this, the main.js files in the www directory will always have the scripts included in an apparently random order (so it’s a huge problem if you’re using Backbone)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
grunt.registerTask('build', [
    'clean:dist',
    'coffee',
    'createDefaultTemplate',
    'handlebars',
    'compass:dist',
    'useminPrepare',
    'imagemin',
    'htmlmin',
    'concat',
    'cssmin',
    // 'uglify',
    'copy',
    'rev',
    'usemin'
]);

Let’s write a basic main.coffee that allow us to have two different behaviour if we are browsing from Chrome (for debug purposes) or directly from the app:

(main.coffee) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
window.Hello =
  Models: {}
  Collections: {}
  Views: {}
  Routers: {}
  init: ->
    'use strict'
    console.log 'Hello from Backbone!'

console.log 'Waiting for the device to be ready...'

$ ->
    window.DEBUG = false
    console.log 'init Started'
    if not window.device.available
        console.log "WE ARE IN DEBUG"
        window.DEBUG = true
        # In this case we should be in a desktop application (debug)

        # Login with the test user (that has limited privileges and/or makes the app point to another endpoint)
        Hello.BASEURL = 'localhost:8000'

        console.log 'Initializing the main router'
        Hello.init()

    # Waiting for phoneGap to load
    document.addEventListener 'deviceready', ->
        console.log "WE ARE NOT IN DEBUG"
        # PhoneGap is loaded
        try
            throw "Cordova variable does not exist. Check that you have included cordova.js correctly" if (typeof cordova is "undefined") and (typeof Cordova is "undefined")

            # FB plugin initialized

            Hello.BASEURL = 'api.yourdomain.com'

            console.log 'Initializing the main router'
            Hello.init()
        catch e
            window.last_error = e
            console.error e
            throw e
    , false

Finally, create an empty cordova_plugins.json in the root of the app folder as required by Phonegap (for cross-domain issues if I remember well)

1
touch app/cordova_plugins.json

Testing

If you managed to do everything in the correct way, let’s test the mudafucka!

After ALL these passages, you should be able to use the grunt server command from the Hello folder to use Yeoman in the browser along with Ripple. You should see something like this:

The base app with Ripple in Chrome

The JS console should show ~this:

The JS console screenshot – click to zoom

Notice the WE ARE IN DEBUG part that indicates the flow of the code. If we launch a grunt --force from the Hello folder, we will not have all the code compiled in the assets/www folder of our Android project.

Just use Eclipse and open the hello_android folder as an existing Android project and launch it with an emulator or your phone and you should see these lines:

The logcat screenshot – click to zoom

As you can see, the code followed another flow in comparison with the webapp. In the end these passages allow you to have this kind of workflow:

  1. You code with your favorite editor while launching grunt server to have LiveReload active and be able to see your modifications in REAL TIME (which is the biggest plus)
  2. Test everything related to the app with Ripple, being able to use the JS console to directly debug your code
  3. Run grunt --force to build your app to the android project
  4. Test it every time you need it to an emulator via Eclipse

I’m sure that there are some simplier ways to achieve this but right now this kind of workflow is still mainly unused from the mass. I personally digg too much the livereload advantages along with the ability to debug without the need to place thousands of console.log. Keep also in mind that these libraries are in continue development so its pretty hard to find the right way to do things. ENJOY!

Comments