Boost the performance of your mobile web app using YUI Compressor

From well established web performance guidelines, we learned a few years ago that it has certain enhancing effects to the performance of a website if the number of HTTP requests made to external assets is reduced and their content is stripped of any unnecessary characters or content. This is commonly referred to as concatenation and minification of, in our case, JavaScript and CSS files. Knowing that byte size and HTTP requests matter even more on mobile devices, the following article attempts to explain how to achieve such performance boost using YUI Compressor in an Xcode project.

While working on version 2 of BikeNav, I attempted to make some improvements not only to the app itself, but also to my development and build environment. Streamlining the process of minification was one of the goals I had in mind. The following step-by-step guide illustrates how I integrated YUI Compressor into the BikeNav Xcode project, which in itself is a PhoneGap project. Nevertheless, this can be applied to any mobile web application that stores assets (HTML, CSS and JavaScript) on the device itself.

Step 1: Download YUI Compressor

Obviously we need to download YUI Compressor first. After downloading and extracting the ZIP file, we end up with a folder yuicompressor-[version-number]. I decided to copy that folder into my iOS project folder into a directory tools, that I had created earlier. The final path to the YUI Compressor folder would be:

/Users/komenda/Documents/PhoneGapApps/BikeNav2/tools/yuicompressor-2.4.2

Step 2: Create a Shell Script File

Since YUI Compressor only handles minification for us, we need to make sure that our JavaScript and CSS files are concatenated into one file each before fed to the Compressor. Also, in order to include this step into the iOS build process, we need to point the build settings to some file it can execute. I decided to create another folder in my iOS project directory called scripts in which I created a shell script called bikenav_build.sh which will hold the shell commands to concat the files first and then call YUI Compressor to minify them.

$ pwd
/Users/komenda/Documents/PhoneGapApps/BikeNav2/scripts
$ ls -l
total 8
-rwxrwxrwx@ 1 komenda  _lpoperator  1098 Nov  8 15:51 bikenav_build.sh

Step 3: The Build Script

rsync

In order to be able to do development in the browser before pushing anything on to the iOS Simulator or the device, I had set up my dev environment in such a way that I am serving all the BikeNav code from within my local Apache DocumentRoot (since there is no server-side processing involved, Apache is not even necessary, but because I store all my other projects in there as well, it keeps things nice and tidy). The path and folder structure looks like this:

$ pwd
/Users/komenda/Webroot/BikeNav
$ ls -l
total 264
drwxr-xr-x  18 komenda  _lpoperator     612 Nov  8 16:35 audio
drwxr-xr-x   5 komenda  _lpoperator     170 Oct 24 17:11 css
drwxr-xr-x  27 komenda  _lpoperator     918 Oct 29 16:46 img
-rw-r--r--   1 komenda  _lpoperator   12497 Nov  8 17:48 index.html
drwxr-xr-x  13 komenda  _lpoperator     442 Nov  8 15:49 js
-rw-r--r--   1 komenda  _lpoperator  116575 Aug 17 17:33 phonegap-1.0.0.js

Once I reach a certain point during development where I feel it is time to try things out on simulator or device, I sync this directory with the www directory inside the iOS/PhoneGap project. A great tool to do this is rsync, which already comes with Mac OSX. The following command is how I run rsync when working on BikeNav (from inside the scripts directory in the iOS project). For details on rsync command line options please refer to the documentation.

rsync -avz --exclude 'index.html' --delete ~/Webroot/BikeNav/ ../www/

This is also the first thing I put into the bikenav_build.sh script:

#!/bin/bash

echo "Custom BikeNav build script"
echo "==========================="

echo "Step 1: rsync files from web directory"

rsync -avz --exclude 'index.html' --delete ~/Webroot/BikeNav/ ../www/

Concatenation

As mentioned above, we need to concatenate our JavaScript and CSS files into one single file each before passing it on to the YUI Compressor for minification. For this, we simply use the Unix cat command like so:

cat ../www/js/one.js ../www/js/two.js ../www/js/three.js > ../www/js/combined.js
cat ../www/css/one.css ../www/css/two.css > ../www/css/combined.css

These two commands create two new files, combined.js and combined.css, each holding the contents of the JavaScript and CSS files, respectively. With that, we are now ready to give the YUI Compressor something to…well…compress.

Minification

Now we can send YUI Compressor to work to give us a nicely minified JavaScript and CSS files:

java -jar ../tools/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar -o ../www/js/bikenav.min.js ../www/js/combined.js
java -jar ../tools/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar -o ../www/css/bikenav.min.css ../www/css/combined.css

This will create bikenav.min.js and bikenav.min.css in their respective directories, based on the concatenated files created in the previous step. With some additional output and commenting as well as deleting the concatenated files after minification (since we don’t need them any more after that) the final bikenav_build.sh script looks like this:

#!/bin/bash

echo "Custom BikeNav build script"
echo "==========================="

echo "Step 1: rsync files from web directory"

rsync -avz --exclude 'index.html' --delete ~/Webroot/BikeNav/ ../www/

echo "Step 2: minify JavaScript and CSS files"

echo "=== JavaScript ==="
echo "concatinating files"

cat ../www/js/one.js ../www/js/two.js ../www/js/three.js > ../www/js/combined.js

echo "minifying files"

java -jar ../tools/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar -o ../www/js/bikenav.min.js ../www/js/combined.js

echo "removing combined.js"

rm ../www/js/combined.js

echo "=== CSS ==="
echo "concatinating files"
cat ../www/css/one.css ../www/css/two.css > ../www/css/combined.css

echo "minifying files"

java -jar ../tools/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar -o ../www/css/bikenav.min.css ../www/css/combined.css

echo "removing combined.css"

rm ../www/css/combined.css

Step 4: Integration into iOS Build Settings

Now that we got our build script ready, we need to integrate it into the build process of our project in Xcode. The following instructions are based on the Xcode 4 user interface.

  1. After opening the project, make sure that the “Project Navigator” view is selected
  2. Select the top level project, in my case it would be BikeNav
  3. In the project editor window, select the target, BikeNav in my case
  4. Select “Build Phases”
  5. In the lower right corner, select the + sign labeled “Add Build Phase” and select “Add Run Script”

After going through these steps, a new bar in the sequence of Build Phases will show up labeled “Run Script”. Because we want to run our build script before any data is copied onto the simulator or device, we need to move it up in the sequence (I put it right after “Target Dependencies”). Now we only need to specify the path to the build script and how to run it. Therefor, in the text area inside that “Run Script” phase, I put the following:

cd /Users/komenda/Documents/PhoneGapApps/BikeNav2/scripts
./bikenav_build.sh

For better illustration, have a look at this screenshot, showing how that “Build Phase” section should look like after you added our build script.

Step 5: Testing

To test whether everything works correctly, build and run your project either on the iOS Simulator or an actual device. To verify that the script is being run, select the “Log Navigator” in the Xcode UI and then “Build [Projectname]” in the left sidebar. In the “Build target [projectname]” section in the log output you should see an entry “Run custom shell script ‘Run Script’”, which should show the familiar commenting messages we put in the shell script.

As a sidenote, of course in order to use the minified files, these need to be properly referenced in the index.html of your project.