| <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—all of its JavaScript, |
| CSS, and fonts, plus other resources it needs |
| (such as images)—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 <video> and <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 <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> |