Apache Cordova can be used to compile HTML5 applications for iOS and Android. This technique allows a developer to easily deploy a static web app to multiple mobile devices. For many genres of application its a perfectly acceptable strategy, especially with javascript performance getting better and better. Personally I want to have cross platform deployment while avoiding the hassle of programming in ObjectiveC for iOS as well as Android’s dialect of Java.
AngularJS is a widely popular javascript framework for all sorts of interactive web apps. Including my own Party Sense project. It is very well suited for developing static website based mobile applications. Because
To avoid having to start from scratch ionicframework bridges the gap between cordova and angularjs. Ionic is essentially a mobile-optimized library of HTML, CSS and JS components for building interactive apps with angularjs. The mentality seems very similar to the Python development world:
Gulp like most build systems has the concept of tasks and dependencies. Here is an example task which depends on the
Compression and minification can act much better on the entire app, I usually aim to have no more than three javascript files. The first is the
I do the same type of thing for my application code, but this code also gets passed through jshint to let me know if I’m doing something wrong!
AngularJS is a widely popular javascript framework for all sorts of interactive web apps. Including my own Party Sense project. It is very well suited for developing static website based mobile applications. Because
angularjs
is used all over the web it doesn’t have much specific support for mobile development, it can however be used to build up common mobile user interactions for example things like tabs, swipe cards etc.To avoid having to start from scratch ionicframework bridges the gap between cordova and angularjs. Ionic is essentially a mobile-optimized library of HTML, CSS and JS components for building interactive apps with angularjs. The mentality seems very similar to the Python development world:
Develop once, deploy everywhere.Although you can just throw your code straight into the
www
directory and run cordova build platform
; I like to maintain a working development version of the mobile app and then compile/minimize/concatenate the source into the final product. The main tool I use for building web applications is gulp.js - the streaming build system for node.Gulp like most build systems has the concept of tasks and dependencies. Here is an example task which depends on the
bump
and tag
tasks:gulp.task('release', ['bump', 'tag'], function () {
console.log('Installing latest stable release of dependencies from bower');
bower.commands.install();
});
Across a number of projects I have a few common tasks. Minify and Concat
This task is the most important, it rewrites css and javascript paths found within html, minify and uglify things. Inside my entry point html file I have notation like the following:<!-- build:css css/style.css -->
<link rel="stylesheet" href="css/stylesheet.css"/>
<!-- endbuild -->
This allows me to have my verbosely commented development css stylesheets split across multiple files and the build system will take the entire contents tagged with build:css
and after any processing put the output into css/style.css
. The processing I do uses the incredible gulp-usemin plugin in this task:gulp.task('usemin', function(){
gulp.src('app/*.html')
.pipe(usemin({
css: [minifyCss(), 'concat', rev()],
html: [minifyHTML({empty: true})],
vendorjs: [uglify(), rev()],
js: [jshint.reporter('default'), uglify(), rev()]
}))
.pipe(gulp.dest('www/'));
});
Running this task copies any top level html documents across to my output directory and rewrites any js/css paths. The innocuous command rev()
puts a unique revision on the generated javascript file to avoid all kinds of cache pain.Compression and minification can act much better on the entire app, I usually aim to have no more than three javascript files. The first is the
ionic.bundle.min.js
which includes Ionic, AngularJS, and a few other angular modules. The second is vendor.js
which is a squashed and concatenated file containing all the project dependencies. For example in one application I use underscore
and jsbn
, so the index.html file has the following section:<!-- build:vendorjs vendor.js -->
<script src="lib/underscore/underscore.js"></script>
<script src="lib/jsbn/jsbn.js"></script>
<script src="lib/jsbn/jsbn2.js"></script>
<!-- endbuild -->
These unminified javascript libraries will get squashed together into something like vendor-4f7118c3.js
, and the resulting index.html file will contain <script src=vendor-4f7118c3.js></script>
.I do the same type of thing for my application code, but this code also gets passed through jshint to let me know if I’m doing something wrong!
<!-- My Application Code -->
<!-- build:js app.js -->
<script src="js/app.js"></script>
<script src="js/filters.js"></script>
<script src="js/controllers.js"></script>
<script src="js/services/services.js"></script>
<script src="js/directives.js"></script>
<!-- endbuild -->
Angular Templates
During development there is no problem with creating a network request for every template, but it would add unnecessary overhead for a deployed app or website. The next task takes a folder of angular html templates and combines and compresses them. I add a'templates'
dependency to my main app:var app = angular.module('myapp', [ ... , 'templates']);
Which would result in an error running in development mode, so I create a dummy app/templates.js
file:angular.module('templates', [])
.run(function($log){
$log.info("Running in Development Mode");
});
The gulp templates task then creates a new templates.js
file which contains all information for all templates in a much smaller single request.gulp.task('templates', function () {
gulp.src(['./app/templates/*.html'])
.pipe(minifyHTML({ quotes: true }))
.pipe(templates({filename: 'templates.js', root: 'templates', standalone: true}))
.pipe(size({title: 'HTML fragments'}))
.pipe(gulp.dest('www/'));
});
Images
Often full resolution images are too large, I use the following task to set a maximum width and optimize all my images. As image resizing and compression can take an appreciable time I only run whenchanged
.gulp.task('images', function () {
"use strict";
var DEST = 'www/img';
gulp.src(paths.images)
.pipe(changed(DEST))
.pipe(imageResize({width: 1080}))
.pipe(imagemin({optimizationLevel: 4, progressive: true, interlace: true}))
.pipe(gulp.dest(DEST));
});
sass
Probably goes without saying but ionic uses sass and after tweaking any of their style you have to run a sass preprocessor.gulp.task('sass', function (done) {
gulp.src(paths.sass)
.pipe(sass())
.pipe(gulp.dest('./app/css/'))
.pipe(minifyCss())
.pipe(rename({ extname: '.min.css' }))
.pipe(gulp.dest('./www/css/'))
.on('end', done);
});
gulp.task('watch', function() {
gulp.watch(paths.sass, ['sass']);
});
Library Copy
I seem to often end up with a copy operation for things like fonts and javascript maps. I usually define all the paths and copy them fromapp
to www
with this task:gulp.task('libcopy', function () {
// the base option sets the relative root for the set of files,
// preserving the folder structure
gulp.src(paths.libs, {base: 'app'})
.pipe(gulp.dest('www/'));
gulp.src('app/data/**/*', {base: 'app'})
.pipe(gulp.dest('www/'));
});