Time.zip - Developing an Android-iOS, smartphone-tablet app in one month

Post on 07-May-2015

745 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slides of the talk I gave at the HTML5 Frontend Development @L'Aquila (Italy). 13th November 2013. Abstract: Last June a customer asked us to develop and publish a mobile application for both Android and iOS platforms which could run on both smartphones and tablets. Everything's fine you may say, there was only one small issue: we had only the equivalent of one month available to complete the project. We accepted the challenge and decided to exploit web technologies and PhoneGap to build a mobile application with cross-platform, cross-device and high performance in mind right from the beginning. To make the story short: we succeded! This talk will take you through the process, decisions and technical solutions we took for achieving this strong result in such a short period of time.

Transcript

Time.zipDeveloping an Android-iOS, smartphone-tablet app in one

month

Ivano Malavolta and Alessio d'Arielli

Ivano MalavoltaResearch fellow @University of L'Aquila

Alessio d'ArielliGraphic Designer and Web developer

RoadmapApp overview

Organizational best practices

Technical best practices

AppOverview

The Customer

Who is Frascati ScienzaWhat kind of app they asked for

Stuff provided

A Storyboard (with many revisions)

Stuff provided

Photoshop Mockups (with many revisions too)

Stuff provided

Anxiety (with many emails and phone calls)

Technical constraintsPlatforms to support: Android 4.x & iOS 6.xDevices: Smartphone & tabletOffline supportQR code scannerVideo streamingIn-app browserNews from RSS feeds

Timeline

2 people, ~4 hours a day

Total working hours: ~320

(~1 month, 2 people full time)

The app

More than 5000 participants to the event

The treasure hunt carried on in the center of Rome

It involved 1000 people, 150 participants

OrganizationalBest

Practices

1. Debug contract before the code

Ask about the operational flow, the UI / UX, the intention of

the app IMMEDIATELY. Write the whole thing in the contract.

Why? If you rework parts already completed you arespending money from your own pocket

2. Web-based feasibility

If they ask to support multiple platforms, evaluate if web-

based development (PhoneGap) is feasible

Search and test any component for advanced features(in our case, QR-code scanning and child browser)

3. Balance efforts

Equally divide the workload, responsibilities and testing.Notify quickly to colleagues about bugs.

4. Use code repositories

Use repository for remote collaboration (SVN, git, etc.)rather than email or other methods that have no chance to

undo.

5. Avoid micro-multitasking

Focus on and complete a task at time, otherwise probability

of injecting bugs is terribly high.Optimize your schedules.

6. Use MVC pattern

By structuring the app with the MVC pattern, you can workin parallel on JS and frontend HTML5/CSS3

7. Modularity

Max simplification of navigation and extreme modularity soany changes to the content or presentation are quickly

realizable without affecting the overall structure.

8. Be reactive

Maximise reactivity and cooperation with colleagues.

Don't empower customer's anxietyresponding immediately to every request

9. Organize revisions with customers

Keep the customer in the loop, arrange periodical meetingsin order to update him and to collect a list of tasks or

corrections to be made.

10. Get all testing tools

Book many hours for testing (and blasphemy),

get as soon as you can as much devices as possible fortesting.

Summary1. Debug contract before the code

2. Web-based feasibility

3. Balance efforts

4. Use code repositories

5. Avoid micro-multitasking

6. Use MVC pattern

7. Modularity

8. Be reactive

9. Organize revisions with customers

10. Get all testing tools

TechnicalBest

Practices

1. First day: search and test alreadydeveloped components

require.config({ paths: { jquery: '../lib/jquery/jquery-1.9.1.min', /* '../lib/jquery/zepto', */ underscore: '../lib/underscore/underscore-min', backbone: "../lib/backbone/backbone", text: '../lib/require/text-1.0.6', async: '../lib/require/async', handlebars: '../lib/handlebars/handlebars', templates: '../templates', leaflet: '../lib/leaflet/leaflet', datamanager: 'datamanager', spin: '../lib/spin/spin' }, shim: { 'jquery': { exports: '$' }, 'underscore': { exports: '_' }, 'backbone': { deps: ['jquery', 'underscore'], exports: 'Backbone' }, 'handlebars': { exports: 'Handlebars' }, 'leaflet': { exports: 'L' } }});

Once you are done, setup your boilerplate app

2. Establish the software architecturefrom the beginning

It will guide the structure of your code

var EnteView = Backbone.View.extend({ model: Ente,

initialize: function () { this.title = this.model.get("titolo");

},

var EventoView = Backbone.View.extend({ model: Evento, /**/ initialize: function () { var date = new Date(this.model.get("timestamp") * 1000); var gg, mm, aaaa, hours, mins; gg = date.getDate() + "/"; mm = date.getMonth() + 1 + "/"; aaaa = date.getFullYear(); hours = date.getHours(); mins = date.getMinutes(); this.title = gg + mm + aaaa + " - " + hours + ":" + mins; /**/ },

Define your own coding patterns**more patterns will show up during the presentation

3. Clearly separate concerns

It makes the code more testable

More easy to extend and refine the app

It allowed us to follow a micro-process

from JS developer's perspective...

I commit this after update I see this

Example (home view)

define([..., "text!templates/frascatiscienza.html"], /* dependency to template file */function ($, _, Backbone, Handlebars, Ente, template) { /* template contains a string now */ var FrascatiScienzaView = Backbone.View.extend({

model: Ente,

className: "default_wrapper",

events: { "touchstart #enti": "enti", "touchstart #_eventi": "eventi", "touchstart #partner": "partner", "touchstart #frascati": "continua" },

template: Handlebars.compile(template), /* template compilation, it is a function now */

render: function () { /* gestione nav bar */ this.updateNavbar(); $(this.el).html(this.template(this.model.toJSON())); /* template execution */ /* */ return this; } }); return FrascatiScienzaView;});

4. Keep performance in mind from thebeginning

Avoid to fall into the fancy-framework trapUse pure JS as much as you canNo JS animation, just switch classes + CSS3transitions/transformsUse native touch events, not onClick (300ms delay)Minimize browser reflowsAvoid complex CSS selectorsTry to use id-only selectors...

5. Views first, then datavar AppRouter = Backbone.Router.extend({ /* */changePage: function (page) { if (this.currentView) { this.currentView.trigger("removed"); this.currentView.remove(); } this.currentView = page; this.structureView.currentView = page; page.render(); this.structureView.$el.find("#content").append($(page.el)); this.structureView.trigger("updateTitle", page); this.currentView.trigger("inTheDom"); // here the new view can fetch data return true;}/* */

Valid especially when data is coming from the network

6. Let the user forget he is looking at abrowser

@charset "UTF-8";/* STANDARD FOR MOBILE */

* { /* transparent link selection */ -webkit-tap-highlight-color: rgba(0,0,0,0);}

body { -webkit-touch-callout: none; /* no callouts during tap and hold */ -webkit-text-size-adjust: none; /* no fonts auto inflation */ -webkit-user-select: none; /* no copy and paste, etc. */ background-attachment: fixed; font-family: 'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif; height: 100%; margin: 0px; padding: 0px; width: 100%;}

#mainContainer { /* get the whole display */ position: absolute; height: 100%; width: 100%; margin: 0px; padding: 0px; left: 0; top: 0;

7. Minimize network access// We launch the Apprequire(['underscore', 'backbone', 'spin', 'router', 'datamanager'], function (_, Backbone, Spinner, AppRouter, Data) {

String.prototype.endsWith = function (suffix) { return this.indexOf(suffix, this.length - suffix.length) !== -1; };

String.prototype.strip = function () { return this.replace(/(<([̂>]+)>)/ig, "").replace(/(<([̂>]+)>)/ig, ""); };

document.addEventListener("deviceready", run, false);

function run() { Data.initialize();

/* */ } }); Try to prefetch data as much as possible

If some data is always the same, bundle it into the app

8. Take special care of imagesAvoid to resize images (both via CSS and JS)Be robust w.r.t. 404 errors

Show a spinner in place of an image while it is loading

/* in the template */<img src="{{ immagine }}" onerror="ImgError(this);">

/* in your JS */function ImgError(source){ empty1x1png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQI12NgYAAAAAMAASDVlMcAAAAASUVORK5CYII=" source.src = "data:image/png;base64," + empty1x1png; source.onerror = ""; return true;}

.heavyImg { background: url('../res/loader.gif') no-repeat; background-position: center; min-height: 20%; width: 100%;}

9. When it's simple, leave it simpleDon't overelaborate. Complexity will come by itself.

goBack: function (self) { var that = (self instanceof StructureView) ? self : this; if (!that.currentView) { return false; } if (that.currentView instanceof IntroTappaView) { return false; } if (that.currentView instanceof DomandaCacciaView) { return false; } if (that.currentView instanceof RisultatoCacciaView) { Backbone.history.navigate("introcaccia", {trigger: true}); return false; } if (that.currentView instanceof FineCacciaView) { Backbone.history.navigate("caccia", {trigger: true}); return false; } window.history.back();}

ANTIPATTERN ABOVE

10. Test, debug, test, debug

In this context, your desktop browser is the killer app!

Check consoleBreakpointsUpdate the DOM at run-timeAccess to all local DBsNetwork profilingCPU and memory profilingMonitor event listenersMonitor elements’ rendering time

Summary1. First day: search and test already developed components

2. Establish the software architecture from the beginning

3. Clearly separate concerns

4. Keep performance in mind from the beginning

5. Views first, then data

6. Let the user forget he is looking at a browser

7. Minimize network access

8. Take special care of images

9. When it's simple, leave it simple

10. Test, debug, test, debug

ConclusionsWe extracted a set of organizational and technical bestpractices from a true story

In any case, step zero to success is to be technologicallyready

for example, many people tend to underestimate JavaScript, don't!

BonusAn RSS reader in 20 lines of pure JavaScript ;)

define(["jquery", "underscore", "backbone", "models/Rss"], function ($, _, Backbone, Rss) {

var RssList = Backbone.Collection.extend({

model: Rss,

populate: function (feedUrl, view) { var xmlhttp = new XMLHttpRequest(); var self = this; xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { var feed = xmlhttp.responseXML; var news = feed.getElementsByTagName("item"); var title, description, link; for (var i = 0; i news.length; i++) { title = news[i].getElementsByTagName("title")[0].textContent.strip description = news[i].getElementsByTagName("description")[0].textContent link = news[i].getElementsByTagName("link")[0].textContent.strip if (title && description && link) { self.create({ title: title, description: description, link: link }); }

Ivano MalavoltaResearch fellow

ivano.malavolta@univaq.it

www.ivanomalavolta.com

@IMalavolta

github.com/iivanoo

Alessio d'ArielliGraphic Designer and Web developer

info@alessiodarielli.com

www.alessiodarielli.com

top related