Looking-back-at-retrollect is a project mainly written in Ruby, it's free.
This is a list of the dos and don'ts of building a cross platform HTML5 application as learned during the development of the Iphone and Android application Retrollect
This is a list of the dos and don'ts of building a cross platform HTML5 application as learned during the development of the Iphone and Android application Retrollect.
Topics that will be touched on:
Slides: http://looking-back-at-retrollect.heroku.com/
Retrollect is the free mobile app that lets you assemble the highlights of your experiences and create a visual mash-up of your life!
Retrollect allows you to create slide shows containing 8 pieces of content from your phone, Twitter account, Facebook status updates, Instagram, or your phone's camera in a visually interesting way. Retrollect uses the aesthetics of the vintage View-Master to add a sense of nostalgia to your collections of content.
Retrollect does this using a codebase mostly comprised of JavaScript, HTML, and CSS.
So you are going to develop a mobile application and are weighing your options, there is Objective-c combined with the cocoa APIs for iOS, Java and Android's API system, there are JavaScript to native code compilers like Titanium Appcelerator, and then there is the option to build straight HTML, JavaScript, and CSS using normal web app practices or an embedded web view.
We've all heard a lot about the shortcomings of HTML5 as a replacement for native code. There's the tired argument about HTML5 vs Native Apps and how HTML5 not good enough yet. I tend to agree and it frustrates me that my web apps aren't as fast or responsive as native code would be, but things are moving forward faster than ever. Looking at a lot of the experiments and absolutely amazing things people are pushing with it, I get really inspired. I think what is really needed is an objective look at what could be done with it, what has been done, and what needs to happen to help push the spec and it's implementations across vendors forward.
The question is how do we do this without absolutely loosing our minds?
"But the iphone's screen size is a set size!", you say?
Just make it fluid man, the point of using HTML5 in the mobile context is to not be tied to any platform beyond a browser. Browsers (even embedded web-views) can be different sizes, think iPhone, iPad, most Android handsets have different screen dimensions.
Making your design fluid (grow to fit any width and height) will save you a ton of headaches when putting your app on more than one device.
img
tags, actually don't use anything that renders images eitherMobile webkit HATES, HATES, HATES images. They are extremely slow to render and manipulate.
Also avoid:
-webkit-gradient
: It paints a bitmap on the viewport and is nearly the same as using images as far as the rendering engine is concerned. Making an iPad HTML5 App & making it really fasttext-shadow
, box-shadow
, opacity
, and alpha background colors also behave similar to using images, these should be avoided wherever performance is a concern.Good:
<div class="list">
<a href="#">Item 1</a>
<a href="#">Item 2</a>
<a href="#">Item 3</a>
</div>
Bad:
<div id="wrapper">
<div id="inner-wrapper">
<ul class="list">
<li class="list-item">
<a href="#">Item 1</a>
</li>
<li class="list-item">
<a href="#">Item 2</a>
</li>
<li class="list-item">
<a href="#">Item 3</a>
</li>
</ul>
</div>
</div>
Fewer elements means less work for the browser, and less confusion for your development team (this topic could be a whole separate presentation on managing complexity in CSS and Markup.)
Using a minimal amount of markup is really important when using css transforms and expecting reasonably responsive animations.
position: fixed;
It's not as broken on Mobile Safari but should still be avoided, it really blows up on android. Caused weird errors with text fields on Android. Fixed positioning in Mobile Safari »
Group DOM Queries and manipulation into separate chunks, do all your manipulation at once instead of spread out.
Quote from Stoyan Stefanov on phpied.com:
Anything that changes input information used to construct the rendering tree can cause a repaint or a reflow, for example:
- Adding, removing, updating DOM nodes
- Hiding a DOM node with display: none (reflow and repaint) or visibility: hidden (repaint only, because no geometry changes)
- Moving, animating a DOM node on the page
- Adding a stylesheet, tweaking style properties
- User action such as resizing the window, changing the font size, or (oh, OMG, no!) scrolling
clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth
Im looking at you pinned header and footer with a scrolling list view in between. While we were able to implement an acceptable solution to this common native UI pattern it still doesn't feel right, we got most of the way in a short amount of time but spending time on the rest felt counterproductive.
The Retrollect team spent a lot of time trying to force this behavior into our HTML5 app with a bunch of hacks which ended up being a hit to performance or backed us into other problems like circumventing Maobile Safari's rendering optimizations.
Things that are difficult to translate to into pure HTML5 implementations:
This is crucial and can actually speed up your development even though it will feel slow at first. It is also important to write tests that are more than testing internal javascript APIs between objects. You should be doing BDD here, when you want to make sure when a user taps a thing that something happens you should write a test for that behavior.
good test:
describe('foo.hasContent()', function(){
when('the slots are empty', function(){
it('should NOT have content', function(){
expect(disc.hasContent()).toBe(false);
});
});
when('the slots are NOT empty', function(){
it('should have content', function(){
expect(disc.hasContent()).toBe(true);
});
});
});
bad test:
describe('foo.hasContent()', function(){
when('the slots are empty', function(){
it('should should call .length', function(){
spyOn(disc._hiddenContent, 'length');
expect(disc._hiddenContent.length).toHaveBeenCalled();
});
});
});
You should play it like you are new to the game. What is going on in these mobile browsers if far different than anything I have ever experienced. For example:
setInnerHTML
in a timer that checked that the html was actually added. See "Problems with Safari and innerHTML"Examine the problems and explore its solution like a scientist. Make guesses, apply them, did it work? Nope? Do it again. Applying knowledge from the desktop world led us down many rabbit holes.
Debugging in a mobile browser is painstakingly slow and not accurate, no exceptions etc.
Also don't expect them to behave the same cross platform, things like timeouts and connection interruptions will look different then you are used to.
We had a haunting issue with base64 encoding images and it turned out to be limits on the heap size, take a look at this message board convo.
3D Transforms are hardware accelerated on iOS and have the bugs worked out for the most part, it is ideal the use them on iOS devices. But they don't work very well on Android, you should use normal transforms instead.
I am a huge fan of docco, take a look at these guys:
Without docs:
if (navigator.userAgent.indexOf('Android') != -1) {
// ...
}
With docs:
// Before doing transforms detect which environment we are in,
// Android's 3D Transforms are a bit buggy so use a 2D Transform
// instead.
// [See this StackOverflow issue for details](http://bit.ly/kwsl09)
if (navigator.userAgent.indexOf('Android') != -1) {
// ...
}
That's it, check out Retrollect which was built by Border Stylo.