blob: 0f622026d14dea57f8bb106e534d9e01c59197f8 [file] [log] [blame]
<h1>Offline First</h1>
<p>
Because internet connections can be flakey or non-existent,
you need to consider <em>offline first</em>:
write your app as if it has no internet connection.
Once your app works offline,
add whatever network functionality you need
for your app to do more when it’s online.
Read on for tips on implementing your offline-enabled app.
</p>
<h2 id="overview"> Overview </h2>
<p>
Packaged apps get the following for free:
</p>
<ul>
<li> Your app’s files&mdash;all of its JavaScript,
CSS, and fonts, plus other resources it needs
(such as images)&mdash;are <b>already downloaded</b>. </li>
<li> Your app can <b>save and optionally sync</b>
small amounts of data using the
<a href="storage.html">Chrome Storage API</a>. </li>
<li> Your app can <b>detect changes in connectivity</b>
by listening for
<a href="https://developer.mozilla.org/en/Online_and_offline_events">online and offline events</a>. </li>
</ul>
<p>
But those abilities aren't enough to guarantee that your app
will work offline.
Your offline-enabled app should follow these rules:
</p>
<dl>
<dt> Use local data whenever possible. </dt>
<dd> When using resources from the internet,
use <code>XMLHttpRequest</code> to get it,
and then save the data locally.
You can use the Chrome Storage API,
IndexedDB, or
Filesystem API to save data locally. </dd>
<dt> Separate your app’s UI from its data. </dt>
<dd>
Separating the UI and data not only
improves your app's design and
eases the task of enabling offline usage,
but also lets you provide other views of the user's data.
An MVC framework can help you keep the UI and data separate.
</dd>
<dt> Assume your app can be closed at any time. </dt>
<dd> Save application state
(both locally and remotely, when possible)
so that users can pick up
wherever they left off. </dd>
<dt> Test your app thoroughly. </dt>
<dd> Make sure your app works well in both
<a href="#testing">common and tricky scenarios</a>. </dd>
</dl>
<h2 id="possibilities"> Security restrictions </h2>
<p>
Packaged apps are limited
in where they can place their resources:
</p>
<ul>
<li>
Because local data
is visible on the user's machine
and can't be securely encrypted,
<b>sensitive data must stay on the server</b>.
For example, don't store passwords or credit card numbers locally.
</li>
<li> All <b>JavaScript</b> that the app executes
must be in the app's package.
It <b>cannot</b> be inline.
<br />
<span class="comment">
{PENDING: Should "JavaScript" be "executable code"?
What about NaCl and Dart? Anything else -- shaders, e.g.?}
</span>
</li>
<li> All <b>CSS styles</b>, <b>images</b>, and <b>fonts</b>
can be initially located
either in the app's package
or at a remote URL.
If the resource is remote,
you can't specify it in your HTML.
Instead, get the data using <code>XMLHttpRequest</code>
(see <a href="app_external.html#external">Referencing external resources</a>).
Then either refer to the data with a blob URL
or (better yet) save and then load the data using the
<a href="app_storage.html">Filesystem API</a>.
<p class="note">
<b>Note:</b>
Styles can be inline or in separate <code>.css</code> files.
</p>
</li>
</ul>
<p>
You can, however,
load large media resources such as videos and sounds
from external sites.
One reason for this exception to the rule
is that the &lt;video> and &lt;audio> elements
have good fallback behavior when an app
has limited or no connectivity.
Another reason is that fetching and serving media
with <code>XMLHttpRequest</code> and blob URLs
currently does not allow
streaming or partial buffering.
</p>
<p>
To provide a sandboxed iframe,
you can create an &lt;object> tag.
Its contents can be remote,
but it has no direct access to the Chrome app APIs
(see <a href="app_external.html#objecttag">Embed external web pages</a>).
</p>
<p>
Some of the restrictions on packaged apps are enforced by the
<a href="app_csp.html">Content Security Policy (CSP)</a>
which is always the following and cannot be changed for packaged apps:
</p>
<pre>
default-src 'self';
connect-src *;
style-src 'self' blob: data: filesystem: 'unsafe-inline';
img-src 'self' blob: data: filesystem:;
frame-src 'self' blob: data: filesystem:;
font-src 'self' blob: data: filesystem:;
media-src *;
</pre>
<h2 id="manifest"> Specifying offline_enabled </h2>
<p>
It is assumed that your app behaves well offline. If it doesn't, you should
advertise that fact, so that its launch icon is dimmed when the user is offline.
To do so, set <code>offline_enabled</code> to <code>false</code> in the
<a href="manifest.html">app manifest file</a>:
</p>
<pre>
{
"name": "My app",
...
<b>"offline_enabled": false,</b>
...
}
</pre>
<p class="comment">
{PENDING: should we link to <a href="https://chrome.google.com/webstore/category/collection/offline_enabled">Offline Apps collection</a>?
show a screenshot of how offline apps are highlighted?
anything else?}
</p>
<h2 id="saving-locally"> Saving data locally </h2>
<p>
The following table shows your options for saving data locally
(see also <a href="app_storage.html">Manage Data</a>).
</p>
<table>
<tr>
<th> API </th> <th> Best use </th> <th> Notes </th>
</tr>
<tr>
<td> Chrome Storage API </td>
<td> Small amounts of string data </td>
<td> Great for settings and state.
Easy to sync remotely (but you don't have to).
Not good for larger amounts of data,
due to quotas.
</td>
</tr>
<tr>
<td> IndexedDB API </td>
<td> Structured data </td>
<td> Enables fast searches on data.
Use with the
<a href="manifest.html#permissions">unlimitedStorage permission</a>. </td>
</tr>
<tr>
<td> Filesystem API </td>
<td> Anything else </td>
<td> Provides a sandboxed area where you can store files.
Use with the
<a href="manifest.html#permissions">unlimitedStorage permission</a>. </td>
</tr>
</table>
<p class="note">
<b>Note:</b>
Packaged apps cannot use Web SQL Database or localStorage.
The WebSQL specification has been deprecated for awhile now,
and localStorage handles data synchronously
(which means it can be slow).
The Storage API handles data asynchronously.
</p>
<h2 id="saving-remotely"> Saving data remotely </h2>
<p>
In general, how you save data remotely is up to you,
but some frameworks and APIs can help
(see <a href="app_frameworks.html">MVC Architecture</a>).
If you use the Chrome Storage API,
then all syncable data
is automatically synced
whenever the app is online
and the user is signed in to Chrome.
If the user isn't signed in,
they'll be prompted to sign in.
However, note that the user's synced data
is deleted if the user uninstalls your app.
<span class="comment">
{QUESTION: true?}
</span>
</p>
<p>
Consider saving users' data for at least
30 days after your app is uninstalled,
so that users will have a good experience
if they reinstall your app.
</p>
<h2 id="mvc"> Separating UI from data </h2>
<p>
Using an MVC framework can help you design and implement your app
so that the data is completely separate from the app's
view on the data.
See <a href="app_frameworks.html">MVC Architecture</a>
for a list of MVC frameworks.
</p>
<p>
If your app talks to a custom server,
the server should give you data,
not chunks of HTML.
Think in terms of RESTful APIs.
</p>
<p>
Once your data is separate from your app,
it's much easier to provide alternate views of the data.
For example,
you might provide a website view of any public data.
Not only can a website view
be useful when your user is away from Chrome,
but it can enable search engines to find the data.
</p>
<h2 id="testing"> Testing </h2>
<p>
Make sure your app works well under the following circumstances:
</p>
<ul>
<li>
The app is installed, and then immediately goes offline.
In other words, the first use of the app is offline.
</li>
<li>
The app is installed on one computer
and then synced to another.
</li>
<li>
The app is uninstalled and then immediately installed again.
</li>
<li>
The app is running on two computers at the same time,
with the same profile.
The app must behave reasonably
when one computer goes offline,
the user does a bunch of stuff on that computer,
and then the computer comes online again.
</li>
<li>
The app has intermittent connectivity,
switching often between online and offline.
</li>
</ul>
<p>
Also make sure that the app saves <b>no sensitive user data</b>
(such as passwords) on the user's machine.
</p>
<p class="backtotop"><a href="#top">Back to top</a></p>