オープン Web アプリをオフラインでも動作させることは重要です。なぜならユーザーの移動中にネットワークが切れてしまうことがあるからです。加えて、端末内にファイルを保持しておくことによりサーバアクセスする回数が少なくなるため、アプリの反応が良くなります。
この記事ではアプリをオフラインでも動作させる方法の中から、簡単で手っ取り早いものを FAQ やサンプルと共に紹介します。IndexedDB、localStorage、Application Cache、XMLHttpRequest など使われている技術について詳細を知りたい方のために、ページ下にリンク集もあります。
オフラインのワークフロー
次の図はオフラインアプリの典型的なワークフローを示したものです。最初のステップは、ダウンロードもしくはインストールのあと、アプリが立ち上がった時です(ダウンロードはオンライン Web アプリ、インストールは Firefox OS などのインストールアプリの場合です)。この時点での標準的な挙動は、アプリで使われるアセットや初期状態のデータをデバイスに保存することでしょう。
以降の挙動は、実行時にオフラインかそうでないかを判断すべきでしょう。もしオンラインの場合はサーバーと同期し、新しいデータやアセットをダウンロードします。もしオフラインの場合は、端末上にあるものでアプリを動作させます。
推奨する方法
ここで紹介するのは、Web アプリをオフラインで実行させる際のベストプラクティスです。
オフライン状態の検出
ユーザーの接続状態を知る API にはいくつかあります。これらを使うとユーザーがオフラインと分かる場合には HTTP リクエストをしなくて済むようにできるため便利です。また、これらを使うと一般的なエラーメッセージを減らせます。たとえば、もしあなたが Twitter クライアントを開発している場合、「HTTP 接続に失敗しました」というメッセージよりも「オフラインのため新しいツイートを読み込めませんでした」といったメッセージのほうが親切でしょう。
API からのデータ読み込みは必要なときだけ行い、さらにデータはキャッシュして、同じデータを二度読み込まないようにしましょう。また、オフライン時の動作に必要なページを先読みし、ユーザーが開いていない場合でもオフライン時に利用可能にしましょう。
Network information API はオンライン状態を検出するものですがあまり信用できません。他にもオフライン検出の方法はいくつかありますが、offline.js といったオフライン検出ライブラリをおすすめします。
XMLHttpRequest でデータを動的に更新している場合、レスポンスを確認するとネットワーク接続が途中で切れていないか確かめられます。もしあなたのアプリが systemXHR 特権 を持っている場合、落ちないであろうサイトへの接続を確認することで接続がアクティブか確かめられます(たとえば、iOS は apple.com を利用して接続状態を確認しています)。
// オンラインではないとする var online = false; // systemXHR パーミッションをもつパッケージアプリと仮定する // https://developer.mozilla.org/docs/Web/Apps/App_permissions var request = new window.XMLHttpRequest({mozSystem: true}); request.open('HEAD', 'https://www.mozilla.org/robots.txt', true); request.timeout = 5750; request.addEventListener('load', function(event) { console.log('We seem to be online!', event); online = true; }); var offlineAlert = function(event) { console.log('We are likely offline:', event); } request.addEventListener('error', offlineAlert); request.addEventListener('timeout', offlineAlert); request.send(null);
アプリが最初に読み込まれたもしくはインストールされたとき、アプリはアセットとデータを端末に保存すべきです。以降の読み込みについては、プログレッシブ・エンハンスメントな考えで対処します。オフライン状態を前提にオフラインストレージ上のデータセットでアプリを動作させ、そしてオンラインの際にデータとアセットを更新するのです。
アセットの保存
もしアプリをパッケージアプリとしてアプリストア(Firefox OS アプリの場合、Firefox マーケットプレイス)から提供したい場合、ここの説明はとくに必要ありません。アプリが端末にインストールされたとき、そのアセットも端末内に保存されます(Packaged apps をご覧ください)。
もしアプリがオンライン上にあり、オープン Web アプリとして提供したい場合、できることは少し限られています。この場合使えるのは、Application Cache です。
CSS、フォント、JavaScript、画像などアプリで利用するファイルは、AppCache マニフェストファイルに記述します。マニフェストはツールから生成できますが、手でも書けます。次の例は Face Value のマニフェストファイルです。
CACHE MANIFEST # v 0.1 / 12 CACHE: css/app.css img/coins/[email protected] js/main.js js/lib/require.js NETWORK: * https://* https://*
注: mustache のような JavaScript テンプレートを使っている場合、それらも AppCache マニフェストファイルに追加するか、JavaScript ファイルにコンパイルし提供しましょう。
重要: AppCache のマニフェストには異なるドメインのリソースを指定できません(リダイレクトであってもです)。Chrome ではリソースが SSL で提供されている場合にはこれが可能ですが、これは仕様に準拠していない挙動のため他のブラウぞではサポートされていません。異なるドメインの CDN からコンテンツを提供する場合にはこのことを念頭においてください。
AppCache の問題と将来
巨大なアプリや複雑なアプリにとって AppCache は問題のあるソリューションで、あまり使ってほしくないものでさえあります。もしアセットをオフラインで欲しい場合、XMLHttpRequest でアセットを取得し IndexedDB のようなオフラインデータストアに保存することをおすすめします。
将来的に Service Workers が使用可能となれば、オフラインアセットの保存や利用にとてもよい解決法となるでしょう。
データの保存
オフラインにデータを手っ取り早く保存する場合、localForage をおすすめします。これはオフラインデータ技術のブラウザごとの差異を抽象化する便利なライブラリです。localForage はまず IndexedDB をサポートするブラウザには非同期なデータストレージを提供し、サポートしていないブラウザ(古い Safari など)には WebSQL にフォールバックします。どちらもサポートしていない場合は、より基本的な localStorage を使用します(詳しくは localForage のサポート情報をご覧ください。)
localForage は localStorage に似た構文でデータアイテムをセット・取得します。
localforage.getItem('key', function(value) { alert('My value is: ' + value); }); } localforage.setItem('key', 'value', doSomethingElse);
注: 詳しい説明は localForage の使い方をお読みください。
注: Firefox OS では大きなデータオブジェクトの保存用に Device Storage API がありますが、IndexedDB(localForage)でも大丈夫なはずです。ポッドキャストのサンプルアプリでは IndexedDB を使いエピソードを保存しています(該当のコードを見る)。
注: オフラインデータストレージには dexie.js もよいでしょう。IndexedDB を簡単な構文で書けるラッパーです。
ステートの保存
アプリの状態は、実行中は定期的にオフラインデータストアに保存するようにし、終了時にもちゃんとローカルに保存するようにしましょう。
サーバーとの同期
アプリはデータをサーバーと定期的に同期すべきです。アプリがオフラインの場合、それを判断しデータの同期をキューに登録する方法を考えてください。そして、ネットワークが回復したらデータを更新するようにしましょう。私たちは同期を簡単に行える方法について検討しています。
サードパーティ製のコードも参考になるでしょう。
サンプル
- To-do Notifications (サンプル): IndexedDB リファレンスの例として作られたサンプルアプリです
チュートリアル
localForage やその他技術の使い方については、以下のチュートリアルが参考になるでしょう。