Cloud Four Blog

Technical notes, War stories and anecdotes

Seven Things I Learned While Making an iPad Web App

Earlier this year I was in the Cloud Four office with my iPad, so I decided to show @grigs what we’d done about orientation change for Lucid Meetings. He looked at it, then got kind of frowny faced and said:

“Do you need to use the browser forward and back buttons in Lucid?”

Naturally, this wasn’t the reaction I was expecting, but I said “Nope! Uh, why do you ask?”

And that’s when Jason suggested that, while the orientation support was fine, we were losing a lot of screen real estate to the fat browser chrome, and wouldn’t it be even nicer to have use of that space?

Thus began my foray into making a “traditional web application” perform double duty as an “iPad web application.”

1. Removing Browser Chrome

So, I wanted to remove the browser chrome, but what that meant was that I actually needed to do the following:

  • tell the browser that Lucid Meetings was “web-app-capable” (yes)
  • tell the browser what kind “web-app-status-bar” to use (black)

I added these lines to the HEAD element:

  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />

Then I refreshed and… nothing changed. Read, read, read. Oh, I see. I needed to save the application to my home screen by pressing that little icon thing and selecting the Save to Home Screen option (and, I thought, how many users will know to do this???). I relaunched the site from the saved icon and… victory! The browser chrome disappeared. We’re done. Moving on.

Not so fast, bucko.

2. Staying In The Web App

As soon as I clicked any link or button, the web app immediately closed and the site reopened in the browser. That was weird and unexpected. I never did find the exact answer as to why that occurs for internal site links, but it does and there are a couple ways to deal with it.

One way is to convert the site to a single page application with fragment links or AJAX callbacks. That’s exactly how the real-time meeting engine within Lucid works, so that part was golden. The other way is to bind to the click events and prevent the default browser behavior that causes Safari to open (remember, we are in web app, not in Safari, per se). For the multipage, non real-time portion of Lucid, we took the latter approach.

After a bit of hunting around, I found a neat little jquery extension that does what you need: https://github.com/mrmoses/jQuery.stayInWebApp

For various reasons I didn’t want to hook all the links, so I restricted the hook to those links with the “stay” class. Then I added the “stay” class to nearly every link in the application and added the following jquery snippet:

  $(document).ready(function() {
      $(function() {
          $.stayInWebApp('a.stay');
      });
  });

With this jquery extension in place and those “stay” classes added, the Lucid Meetings application now stayed in full web app mode, and that part of the task was finally done.

3. Add to Home Screen

Now the question became “how do I provide a (non-annoying) hint to people to encourage them save Lucid to their home screen?” I normally don’t save web sites to my home screen unless there’s a compelling reason and I never in the past considered that the site might improve its behavior if I did so.

So I went hunting around again and found this little gem: http://cubiq.org/add-to-home-screen

The introduction for this script captures it all:

I found that many iPhone and iPad users don’t know that they can add their favorite web sites to the Home Screen and interact with them like standard native applications. This script helps them to discover this great feature and suggests the steps needed to add your web app to the dashboard.

This script works exactly as advertised and is highly configurable (to reduce the annoyance factor). It also works quite well with the “apple-mobile-web-app-capable” meta tag set to “yes.” That is, when you relaunch as a web app, the script is smart enough not to prompt you, yet again, to save to your home screen.

4. How to Sex it Up a Little

Having gone that far, I now wanted to glam up the web application a bit by adding a startup splash screen and an actual icon for the home screen (to replace the auto-generated screenshot icon). This is a well trod path, though I did have to futz with things a bit. In the interest of putting all the pieces in one place, here are a few more things we added to the HEAD element:

  <link rel="apple-touch-icon" href="/images/lm57.png" />
  <link rel="apple-touch-icon" href="/images/lm72.png"  sizes="72x72" />
  <link rel="apple-touch-icon" href="/images/lm114.png" sizes="114x114" />
  <link rel="apple-touch-startup-image" href="/images/startup-iphone.png"  sizes="320x460"  media="(max-device-width: 480px) and not (-webkit-min-device-pixel-ratio: 2)" />
  <link rel="apple-touch-startup-image" href="/images/startup-iphone4.png" sizes="640x920"  media="(max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2)" />
  <link rel="apple-touch-startup-image" href="/images/lmsplash1004.png"    sizes="768x1004" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)" />
  <link rel="apple-touch-startup-image" href="/images/lmsplash748.png"     sizes="1024x748" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)" />

The first three link elements refer to icons in the standard sizes defined by Apple: 57×57, 72×72, and 114×114. The device selects the icon that’s right for it, with the 114×114 icon working for the non-retina iPad. As you might tell, the Lucid web app mode is also setup for iPhones, though we haven’t yet built our dream user interaction model for handsets. More goodness to come!

The next four link elements refer startup images. Three of them are portrait mode images for the iPhone, iPhone with retina display, and iPad. The fourth is a landscape startup image for the iPad. Note that the sizes here, as shown in the “size” attributes, are absolutely required or the startup image will simply be ignored. Yep, found that one. A couple times.

While trying to get the right startup image to show I saw some references to the associated media queries, so we ended up adding them as well. I didn’t dive all the way in to see if both the “sizes” attribute and the media query are required, but I can say that the above examples all work on their respective devices.

5. How to Keep the Display from Scaling on Orientation Change

At this point we had a pretty nifty full screen web app, but unlike the game of Horseshoes, close enough isn’t good enough — so more tweaking and optimization were called for. In this case, I wanted the application to fit well into the display for both portrait and landscape modes, and I wanted the behavior to feel more like an application than a website.

The starting point for all this activity is to set the viewport meta tag to indicate that the application is sized appropriately for the display and doesn’t need to be scaled THANKYOUVERYMUCH. It turns out, however, that we were not sized correctly. Hmm. Fortunately for us only a minor amount of tweaking was required, so we made a few judicious changes and optimized our application so as not to exclude iPad compatibility. I think of this as mindfulness of design and it’s the same thinking we’ve applied when working on the IE7 user experience (yes, that does matter).

With the layout fixed, we added the viewport meta tag to set the initial scaling. For completeness, I’m also showing the CSS inclusion, which shows that we think in terms of landscape, followed by switching to portrait mode. That’s my desktop legacy shining through. Woot. Don’t worry, it works if you go the other way too. One special note: the “(max-device-width:1024px)” part of the media query is present to ensure this only applies to tablets. There are some technical (read: foreign technology integration) reasons why this needs to be the case, and this isn’t the right place to delve into those, but rest assured we’re drumming those out as well.

  <meta name='viewport' content='initial-scale=1.0,width=device-width' />
  <link type="text/css" rel="stylesheet" media="all" href="/css/landscape.css" />
  <link type="text/css" rel="stylesheet" media="only screen and (max-device-width:1024px) and (orientation:portrait)" href="/css/portrait.css" />

So, we were all done, right? Not quite. With the iPad, an orientation change results in a scaling operation which means text either zooms in or out, which is quite disorienting for a carefully sculpted application! If you hunt around you’ll find quite a discussion about this, as well as some competing recommendations. From my perspective, an application is different than a website where scaling might be expected. Once again, the internet provides a solution in the form of some custom scripting.

The wonderful Stack Overflow captures this discussion and provides a solution: http://stackoverflow.com/questions/2557801/how-do-i-reset-the-scale-zoom-of-a-web-app-on-an-orientation-change-on-the-iphon

  <script type="text/javascript">
    /*
     * This bit of code disables user scaling on iOS until the user tries to scale with pinch/zoom.
     * http://stackoverflow.com/questions/2557801/how-do-i-reset-the-scale-zoom-of-a-web-app-on-an-orientation-change-on-the-iphon
     */
    if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
        var viewportmeta = document.querySelector('meta[name="viewport"]');
        if (viewportmeta) {
            viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
            document.addEventListener('gesturestart', function () {
                    viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=10';
                }, false);
        }
    }
  </script>

As the embedded comment suggests, the script detects that we are on an iPad device and changes the viewport meta tag to disable scaling by setting the minimum and maximum scale to the initial scale. In addition, if the user performs a pinch/zoom gesture (I don’t see this working reliably with double-tap in practice) then we hook the gesture start and “unlock” the scaling capability. In this way, we are pushing our “don’t scale me” agenda, while leaving an option for the user to override our default behavior.

6. How to force a numeric keyboard

Okay, this is a small one, but it DROVE ME CRAZY. One easy thing you can do to say “I love you” to your users is to use HTML5 input types, such as email, url, date, number, and search. In addition to validation possibilities, devices with configurable keyboards may be able to configure themselves with the right keys to speed input. A number field probably doesn’t mean you need to see a traditional QWERTY keyboard.

On the iPad, a “number” input type activates a spinner control that allows you to select from a list. This really doesn’t work, for example, when entering a 16-digit credit card number. For the life of me I couldn’t figure out how to get what I wanted: a simple numeric keypad. It wasn’t until Brad Frost started a discussion on the topic that it clicked for me: add a pattern attribute to a text input field. Voila!

  'pattern' => '[0-9]*

7. Resources Are Available

We’ve only scratched the surface for what we have planned for Lucid Meetings, but what I’ve discovered is we have a vibrant, noisy community of people ready to help. I want to highlight one resource in particular, the Mobile Web Best Practices site, which is a sort of meta resource for me. Grab a beer and check out the resource page. It’ll be a fun afternoon. See you on the other side.

Mobile Web? iPhone, Android, and BlackBerry, mostly.

Here are a few interesting tidbits from some mobile websites we’re monitoring. The following stats are from the two month period inclusive of December, 2010 and January, 2011. The sites are U.S. centric and tend to draw from a younger (18-35 year old) demographic.  You can make what you will of this data, I just thought it was interesting to look some actual numbers from working sites.

The data set contains 5,408 unique user agent strings submitted to the mobile sites during the sample period.  These UAs are mostly minor variations on a few major device categories, so we’re thankful that the WURFL API collapses similar UAs into the same base device for us.  There are also a fair number of weird UAs, as you’ll find whenever you traipse through server logs.

For the approximately 6 million site accesses over this period, 96% of all traffic arrives via iPhone, Android, or BlackBerry.  And of those, iPhone dominates site usage with a whopping 63% of all traffic.  While 4% of the traffic falls into the “Other” category, that does translate to about 240,000 site accesses from those other guys, so it seems reasonable to care about their experience too.  Okay, some of those are bots, so maybe we don’t worry too much about those.

Mobile Stats

The iPhone traffic is pretty much split between OS 4.x (78%) and OS 3.x (21%), with a tiny smattering of other stuff mixed in.  Virtually all Android traffic is coming from Android 2.x devices, though for this purpose I didn’t bother to break it down further than that.   BlackBerry traffic is interesting because, while BlackBerry 5.x represents 68% of the traffic, the “older” BlackBerry 4.x devices still represent 16% of all BlackBerry traffic we’re seeing.  I put that “older” in quotes because, c’mon, they’re not that old — and we know from experience that some people really love those devices (or are too afraid to let them go).  The BlackBerry 6.x devices are also about 16% of all BlackBerry traffic for these sites, which means that 84% of BlackBerry traffic… isn’t.

So, that’s all for now.  I don’t have any stunning revelations, but thought someone, somewhere may be wondering about mobile traffic breakdown for some project they’re working on, and perhaps this will help.

Busy Summer!

We have been extraordinarily busy this summer, doing a *lot* of really cool things. With some luck we’ll find a little time to poke our heads up and write about some of them! Stay tuned :)

Are CSS Sprites A Mobile Web “Worst Practice?”

There’s an interesting article on mobiforge.com that talks about the pitfalls of using CSS sprites for mobile web content. While there are several reasons why sprites may not be a good idea, two stand out for me: CSS2 support is required for the necessary background positioning, and there may be longer term performance penalties associated with using the layout engine for positioning on every page.

That performance aspect is something to think about.  The author states that it may be better to deliver a set of images once, paying a relatively small penalty for extra (cached, with long expiration) HTTP transactions, than to pay a layout engine penalty on every subsequent page rendering.

I can’t say for sure, but I can certainly imagine that some mobile devices are better than others when it comes to page rendering performance.  As one of the commenters in the article states, this “brings us back to the golden rule in mobile: know thy browser…”.  What works best in the desktop world may not be best in the mobile world.

The W3C says that CSS sprites are a best practice for the mobile web so — this would seem to be a odds with that recommendation.  What do you think?

High Performance Web Sites (CS193H)

As many of you know, we’re fairly enamored with the YSlow techniques for understanding and evaluating web performance for web applications.  We have successfully used the information to improve the user-perceived performance for “traditional” websites and for mobile websites / applications.  We’ll be talking about more of those experiences in upcoming posts.

In addition to the work we’ve done for our customers, we also like to promote good practices that everyone can follow.  Which leads me to today’s post.  I’ve recently finished re-reading the slides presented as part of the High Performance Web Sites course at Stanford University.  These slides have been made available by Steve Souders and they provide a nice accompaniment to his book, High Performance Web Sites.  The course also included guest lectures, and those slides have been made available to anyone who is interested in web front-end performance.  The guest slides are chock full of real world information from people who really know this stuff.

If you are a web developer, you should familiarize yourself with the Yahoo Exceptional Performance info by reading their best practices list, reviewing the CS193H slides, and keeping the Souders book in front of you so you don’t forget to do it!