Skip to content
awwx edited this page Feb 28, 2013 · 27 revisions

This is my attempt to gather in one place the various issues for fully supporting Meteor on mobile devices. See also Improve mobile browser experience on the Meteor Roadmap.

I am at this point most interested in identifying tradeoffs. For example, it might not be possible to avoid having a bad user experience on iOS devices when tabs are unloaded to save memory, or it might be an unrealistic amount of work to "fully" support offline data... but I'd like to know the options.

I'm focusing only on web applications here. Some of these issues don't matter for native apps (e.g. running Meteor inside of PhoneGap), and native apps might have other needs not covered here.

Unwanted highlighting on Android

See Meteor issue #734: Android artifacts.

When a "click" handler is attached to any element on the page, all elements on the page become clickable. The Android browser highlights "clickable" elements when they are tapped.

Meteor's universal-events document event handler doesn't itself handle the event, but it temporarily attaches a second event handler to the target of the event which does handle the event. (events-w3c.js:117) Apparently attaching a "click" event handler in this way triggers the Android "clickable" handling.

Open questions:

  • Why does universal-events attach an event handler to the target of the event?

  • What functionality would be lost if this wasn't done? (And maybe that functionality, whatever it is, isn't needed on mobile devices...?)

  • Is there some way to turn off Android's default "clickable" handling? (Would preventDefault do the trick? But what would it affect if we used preventDefault all the time?)

jquery-events is an experimental stab at a jQuery implementation of universal-events for investigation. While it's not a package I'd recommend using at this point (I haven't investigated what Meteor features might be broken by using it), it does seem to be able to run the "todos" example.

Touch events

See Meteor issue #691: Touch events.

Adding mobile touch and gesture events to Meteor's event maps.

I haven't worked on this myself, but it looks like it could be an easy project.

setTimeout in inactive iOS tabs

This one is not an issue as far as I know, but worth being aware of.

iOS holds off delivering setTimeout and setInterval events in inactive tabs until the tab becomes active again.

Some blog posts say that inactive tabs are "suspended", but this isn't true. The tab will still respond to AJAX and storage events (and other events, as far as I know, if there's a way to deliver them to the tab). Thus for example, an inactive tab can make a long poll request to a server, handle the return result, and make another long poll request... and continue indefinitely as long as the server does. (But if it loses the connection it has no way to set a timeout and try again).

Meteor uses setTimeout(fn, 0) to run things in the next tick of the event loop, and in an inactive tab the call to fn won't happen until the tab becomes active again. As far as I know this isn't much of a problem... it does prevent updating the UI in the background, but Meteor is pretty fast anyway.

Window state

By "window state" I mean state which is unique to a particular browser window or tab.

Window state is more of an issue on mobile devices compared to the desktop because mobile browsers will unload inactive tabs to save memory. Thus if the application doesn't save the window state, the user will lose state merely by switching between tabs. (This happens more often on older devices with less memory or when the user is loading large pages in other tabs).

Meteor's Session

The window state is closely related to Meteor's Session, because Session is associated with the browser tab (each tab has its own Session), and Session is persisted across hot code reloads. However, not everything in Session is necessarily unique to the tab.

For example, in the "todos" example, the currently selected list id is stored in the URL and reflected in the Session list_id variable. This state is not "window state" by my definition (it is URL state instead): if the URL is opened in a new browser, it is the URL which determines the list id, not the (currently empty) window state.

As another example of a distinction, suppose the user is typing in some text, and the developer wants to ensure that the text isn't lost just because the user switches between tabs. One mechanism might be to store the draft text in the window state (assuming the window state is preserved). Or, the developer might like to save the draft to the server so that the user still has the draft when switching devices.

Other kinds of state is state associated with the browser across all tabs (e.g. is the user logged in or not) and state associated with a user across all devices a user is logged into (e.g. their username). "Is it possible to support window state" is one question, and "do we want to associate this particular piece of state with the window, URL, browser, or user" is another question.

Just use the URL?

A question is why not make all state URL state? That is, store all state in the URL (or a hash of the state in the URL), and all state becomes tied to the URL instead of to the particular window tab. This has an appealing simplicity, but leads to some odd behavior. For example, in the "todos" application whether or not I'm editing a todo title is part of the state. If that state were URL state, then if I shared my URL with someone else and they opened that URL, they'd also be editing the title.

And, the URL is connected to the browser Back button; clicking the Back button changes the URL to a previous URL. This is good if the URL specifies where you are in the application (looking at this list or that list), but weird if clicking the Back button means that you're editing someone again. (Imagine if you were writing an email, and you clicked "send", the email is sent, and then you clicked the Back button to get back to what you were doing before... and you were returned to editing the email).

So ideally (if it's possible) it would be nice if the developer could choose which state to associate with the URL and which with the window.

HTML5 Session Storage

The HTML5 session storage feature would be the natural place to save window state (and Meteor does use session storage for persisting Session across hot code reloads). However on iOS, when a tab is unloaded to save memory, the session storage is also cleared. This makes session storage useless for preserving state as the user switches between tabs.

Emulating Session Storage

If something in the tab were preserved across reloads (for example, if window.name were preserved), then that could be used to uniquely identify the tab, and as key into another kind of browser storage which isn't cleared when tabs are unloaded. But, I didn't find anything that was: as far as I can tell, everything is cleared when the tab in unloaded (except for the URL), and there's nothing that can uniquely identify the tab.

This appears to be an unsolved problem for web applications on iOS, as far as I know. In the Amazon Kindle Cloud Reader web app, for example, you can open book A in one tab and book B in a second tab, but if the first tab was unloaded and you return to it, the app displays book B. In Google Reader if you open a post, switch tabs, and come back after the Reader tab had been unloaded the post is no longer open.

Some may wonder if this is very important. I believe that it is, if you want to be able to use Meteor for serious applications that involve making updates (and don't want to have to publish your app through the app store). Losing ones place isn't too terrible in a passive application like the Kindle Cloud Reader, is annoying in Google Reader, and really terrible when trying to do any kind of data entry.

I did have a clever idea for emulating session storage on iOS, which I described on meteor-core. This is still waiting for an implementation though.

Offline

While somewhat orthogonal to mobile (desktop web apps can be offline, and mobile apps can be online-only), people do tend to want to especially use mobile apps offline.

Offline application cache

The appcache project implemented an offline application cache for Meteor, which allows the static parts of an application (the HTML, Javascript, CSS, and images) to be cached offline.

The app cache isn't too useful by itself (well, it does have some secondary advantages such as lessening the page refresh time on hot code reloads :), but it is a necessary component of supporting offline use.

Even if you didn't care able being able to launch an application offline, and just wanted your Meteor app to be able to survive losing the Internet connection occasionally, the app cache would still be important. The tab unloading behavior means that without the app cache the browser needs to be able to connect to the server to reload the app if it's been unloaded.

Offline data

Clone this wiki locally