物江 修

進化するWeb ~Progressive Web Appsの実装と応用~(de:code2018より)

2018年5月に開催された日本マイクロソフト主催のイベントde:code 2018で「進化するWeb ~Progressive Web Appsの実装と応用~」というセッションを担当しました。

イベントに参加できなかった方に向けてセッションの内容を記事にまとめましたので、ぜひご覧ください。

この記事では、昨今話題に上ることが多いPWAことProgressive Webアプリケーションについて、実際の作り方を解説しながら、それがいったいどういったものであるかを詳(つまび)らかに紹介することを目的としています。

Webでは、「ネイティブアプリと同じことができる」「ネイティブアプリを置き換える」など、期待に胸を膨らませずにいられない浪漫に満ちた噂がありますが、それが本当かどうか記事をご覧いただくとご理解いただけると思います。

Progressive Web Appsの作り方を紹介する前に、簡単に概要を紹介しましょう。

Progressive Web Appsとは?

Progressive Web Apps(以下PWAと記述) は、一言でいうならば「ネイティブアプリのようなUXを提供するWebアプリの概念」といったところでしょう。2015年の11月に開催されたChrome Dev Summit 2015のキーノートで発表されて話題となりました。

もともとは、その年の10月にAlex Russell氏(Google)がブログ記事に投稿した、クライアントの性能に合わせて段階的に進歩するWebアプリケーションのコンセプトでした。

PWAというと、”ネイティブアプリのような体験”といった特徴ばかりが注目されますが、大きな特徴がもう一つあります。それは、PWAという名前にも含まれているProgressiveというところです。

これはHTML5が普及しはじめた頃にあった、「HTML5が解釈できるモダンブラウザーにはリッチな体験を、そうでないブラウザーには従来とおなじ体験を」というデザインの考え方 Progressive Enhancementに由来します。

PWAはこの思想を踏襲し、性能の低いWebブラウザーを切り捨てることなく、クライアントの性能に合わせた機能提供を行います。

PWAは、性能の低いWebブラウザーからアクセスがあった場合は従来と同じWebページとして動作し、PWAが動作する機能を備えたWebブラウザーからアクセスがあった場合は、その機能を活かし、これまでのWebアプリにはなかったネイティブアプリのような体験を提供します。

Web アプリケーションの Progressive Enhancement

Progressive Web Appsを実現する機能

それでは、”これまでのWebアプリにはなかったネイティブアプリのような体験”というのはどのようなものがあるでしょう?

代表的なものとしては以下の4つが挙げられます。

  1. オフラインサポート
  2. プッシュ通知
  3. バックグラウンド処理
  4. デバイスへのインストール(デバイスのホーム画面から起動できる)

これらはの機能はそれぞれ以下のような新しいAPIによって実現されます。

  1. オフラインサポート – Cache API
  2. プッシュ通知 – Push API
  3. バックグラウンド処理 – Background Sync
  4. デバイスへのインストール(OSのメニューから起動できる) – Web App Manifest

そして、これらの機能を提供しているのが1~3がService Workerで、4がWeb App Manifestです。

新しいAPIの提供の図

PWAの定義に明確なものはありませんが、上記の機能の中からオフラインサポートと、インストール(アイコンをデバイスのホーム画面に追加)機能をサポートしていれば、PWAを名乗ってもそう否定されることはないでしょう。

これらの機能により、デバイスのホーム画面から、ナビゲーションバーなどのブラウザーUIがない状態でアプリケーションを起動し、オフラインの状態でも使用できる、という体験をユーザーに提供できます。これらの体験は、これまでのWebアプリケーションにはなかったネイティブアプリのならではのメリットと言えるでしょう。

それでは、逆に Web ならではのメリットとはなんでしょう?

Webのメリット

ネイティブアプリにはないWebならではのメリットを、Googleさんのイベントではよく以下のSLICEという言葉で表しています。

  • Secure (安全)
  • Linkable (リンク可能)
  • Indexable (インデックス可能)
  • Composable (再構成可能)
  • Ephemeral (一時的な利用)

これら従来のWebとネイティブアプリの体験上のメリットを合わせたものが、PWAのメリットとなります。

Progressive Web Appsが提供する価値

PWAの提供する価値について、GoogleさんのイベントではよくFIREという言葉で紹介されていますが、

  • Fast: パフォーマンスの良い、軽快な動作
  • Integrated: OSと統合されたユーザー体験
  • Reliable: オフラインでも動作する利便性と信頼性
  • Engaging: Webサイトの価値向上

この記事ではもう少しかみ砕いて紹介しましょう。

発見性

PWAであれば、アプリを公開するのにアプリストアにわざわざお金を払ってアプリを提出したり、審査を通すための不自由なルールに縛られる必要はありません。これまでのWebアプリと同じようにインターネットに公開しておけば、検索エンジンがクロールして見つけてくれます。

いままでのSEOのスキルがそのまま使える上、Webはアプリストアとは比べものにならない数のユーザーが、比べものにならないくらいの回数、常に検索を行っています。この圧倒的なオポチュニティ(機会)の違いは、自分自身が今日何回Webを検索したか、アプリストアを何回検索したか、比べてみればよくわかるでしょう。

インストール可能

PWAはさまざまなデバイスにインストール可能ですが、そのためにプラットフォームごとに違う開発言語で書き直したりパッケージンクしなおしたりする必要はありません。PWAをサポートしているWebブラウザーであれば、同じアプリケーションをその全部にインストールして使うことができます。

また、”インストールしなくても使える”点もメリットです。もともとがWebページなので、ちょっとだけ使ってみるということが可能です。

サービスを試用してみようと思ったときに、”アプリのインストールが障壁になって利用を中断してしまった”、という経験は誰にでもあることでしょう。また、スマートフォンでコンテンツを閲覧中に、突然、アプリのストア画面が表示され、アプリのインストールを促されて不快な思いをしたこともあるかと思いますが、そういったことを避けられます。

PWAであれば提供者もユーザーもアプリのインストールについてのネガティブな点を回避することができます。

再エンゲージ可能

従来のWebページであれば、ユーザーがコンテンツから離脱したあとは、再び訪問してくれることを祈るくらいしかできませんでしたが PWAはサーバー側から通知をPushすることができます。

これによりユーザーに即時的な価値を提供できます。例えば、ECショップであればタイムセールの開始であるとか、オークションサイトであればオークションの開始や、最高落札額の更新などです。

通知機能を適切に利用することで、ユーザー側の機会獲得を増やし、再エンゲージを促すことができます。

逆にどうでもいいことを通知しすぎるとユーザーの心象を害するので注意が必要です。特に、初めてページを訪問したユーザーに「プッシュ通知を許可しますか?」というメッセージを表示するのは避けたいところです。

ネットワーク非依存

PWAはオフラインでの使用が可能ですので、ネットワークの状態に左右されないように作ることが可能です。また、常に使用されるアセットをローカルにキャッシュすることで表示のスピードアップや、回線の使用料を減らすことにも貢献します。

プログレッシブとレスポンシブ

PWAはきちんとその思想を理解して開発すれば、低機能なブラウザーやPWAをサポートしないデバイスにもサービスを提供できます。また、さまざまな画面サイズに対応するためのレスポンシブな機能については、WebにはMedia Queries等、そのためのナレッジもリソースも豊富に存在するため、既存のスキルを活かして機能を実装できます。

安全

PWAはWebコンテンツと同じWebブラウザーの強力なサンドボックス内で動作するため、ネイティブアプリのようにユーザーの強い権限で動作しないため、誤動作や悪意のあるコードによってシステムに深刻なダメージを与えることはありません。またhttpsによる接続でのみ動作するのでサーバーとのやりとりを安全に行うことができます。

リンク可能

PWAはインターネット上でユニークな URL をもっており、ハイパーリンクのあるさまざまなところからサービスに接続することができます。PWAを使用するのにアプリ ストアは必要なく、面倒なインストールプロセスも必要ありません。

PWAのメリットは、Webとネイティブ アプリのメリットを合わせたということだけでなく、それらを状況に合わせて取捨選択できることです。

Progressive Web Appsを実現するAPI

ここからはPWAを実現するためのAPIと、それらをどのように使ってアプリケーションを構築していくかについて紹介します。

Service Worker

PWAを実現する上で中心となる機能を提供するのが、Service Workerです。

Service Workerとは何か? 端的に言うと、「バックグラウンドで動作する“プログラミング可能な”ネットワークプロキシ」です。

Service WorkerはWeb Workerの一つで、Webページのスクリプトとは独立して動作しており、DOMにアクセスしたりすることはできませんが、Webページのネットワークリクエストすべてをインターセプトできます。

つまり、Webページからのリクエストを横取りしてキャッシュしたものを返したり、改ざんしたりといったことができます。そのため localhost、127.0.0.1接続以外はhttpsの使用が必須となります。

Service Workerが提供する機能

Service Workerが提供する機能は主に以下の4つです。

  • キャッシュ
  • リクエストのハンドリング
  • Push通知
  • バックグラウンド同期

これらの機能を図で紹介します。

キャッシュとリクエストのハンドリング

Service WorkerはWebページからのリクエストの中に、自分のキャッシュリストに含まれるアセットを見つけると、レスポンスからこれを取得してキャッシュします。

それ以降、Webページからリクエストされたアセットがキャッシュ内に存在する場合は、そのキャッシュされたアセットを返します。ネットワークから取得しないので、アセットの取得は高速に完了し、通信コストは発生しません。

Push通知

関連付けられたWebページがアクティブでなくても、サーバーからのPushを受け取り、Notification API等を使用してユーザーに通知を行えます。

バックグラウンド同期

オフライン中にユーザーが行った操作をキャッシュし、

オンライン時にその内容を同期することができます。

このように Service Worker はプログラミング可能なネットワークプロキシとして、これまでにないさまざまな機能を提供します。

これらの機能の2018年6月時点のサポート状況は以下のとおりです。

ここからは、Service Workerの具体的使い方について解説していきます。

Service Workerを使う準備

Service Workerを使った開発を行う際に用意しなければならないものがあります。

まず、Service Workerを利用するためのWebアプリケーション、もしくはWebコンテンツが必要です。これはけしてSPA(Single Page Application)のようなアプリケーション然としたものである必要はなく、既存の一般的な Web ページであってもかまいません。

このWebページ、もしくはWebアプリケーションは、htmlやcss、jsや画像に代表されるメディアといった複数のファイルで構成されていると思いますが、それとはべつにService Worker用のJavaScriptファイルをひとつ用意します。ここでは便宜上sw.jsと名付けます。

このService Worker用のJavaScriptファイルはWebコンテンツ側のJavaScriptコードから登録され稼働を開始します。

Service Workerは、自身のファイルが配置された以下のディレクトリに対しスコープを持つので、コンテンツ/アプリケーション全体をキャッシュするなど管理下におきたい場合はWebサイトのルートに配置します。

Service Workerの登録

Service Workerの登録はWebコンテンツのJavaScript から行います。

登録に必要なコードは非常にシンプルで、最低限、以下のコードで問題を発生させることなく、Service Workerの登録が行えます。

Webコンテンツ側
//Service Workerがサポートされているかチェック if (navigator.serviceWorker) {    //Service Worker を登録    navigator.serviceWorker.register(‘/sw.js‘);
}

 

Webコンテンツ側のコードから、navigator.serviceWorker.registerメソッドにより、Service Workerの登録が行われると、Service Worker用のJavaScriptコード(ここではsw.js)ではinstallイベントが発生します。

sw.js側
//キャッシュするアセットのリスト
var urlsToCache = [
   ‘/’,
   ‘/index.html’,
   ‘/css/index.css’,
   ‘/script/index.js’
];
//install イベントのハンドラ
Self.addEventListener(‘install‘, function(event) {
   event.waitUntil(
      //キャッシュを開く
      caches.open(‘キャッシュの名前’)
         .then(function(cache) {
            //アセットのリストをキャッシュに登録
            return cache.addAll(urlsToCache);
   }));
});


install イベントハンドラ内では、キャッシュを開き

//キャッシュするアセットのリスト
var urlsToCache = [
   ‘/’,
   ‘/index.html’,
   ‘/css/index.css’,
   ‘/script/index.js’
];
//install イベントのハンドラ
Self.addEventListener(‘install’, function(event) {
   event.waitUntil(
      //キャッシュを開く
      caches.open(‘キャッシュの名前’)
         .then(function(cache) {
            //アセットのリストをキャッシュに登録
            return cache.addAll(urlsToCache);
   }));
});

 

キャッシュにアセットのリストを登録します。

//キャッシュするアセットのリスト
var urlsToCache = [
   ‘/’,
   ‘/index.html’,
   ‘/css/index.css’,
   ‘/script/index.js’

];
//install イベントのハンドラ
Self.addEventListener(‘install’, function(event) {
   event.waitUntil(
      //キャッシュを開く
      caches.open(‘キャッシュの名前’)
         .then(function(cache) {
            //アセットのリストをキャッシュに登録
            return cache.addAll(urlsToCache);
   }));
});

 

Service Workerが登録され、installイベント内の処理が無事に完了すると、次にactivateイベントが発生します。(もしinstallイベント内の処理が失敗した際にはerrorイベントが発生します)

self.addEventListener(‘activate‘, function(e) {
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (key !== ‘キャッシュの名前’) {
          return caches.delete(key);
        }
      }));
    })
  );
});

 

installイベント内で”しなければいけない処理”というものは特にないのですが、一般的に古いキャッシュを削除するのに使用されます。このサンプルコードではキャッシュからキーの一覧を取り出し、

self.addEventListener(‘activate’, function(e) {
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (key !== ‘キャッシュの名前’) {
          return caches.delete(key);
        }
      }));
    })
  );

});

 

キャッシュの名前(新しく指定された)と比較し、異なるものをすべて削除しています。

self.addEventListener(‘activate’, function(e) {
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key{
        if (key !== ‘キャッシュの名前’) {
          return caches.delete(
key)
        }
      }));
;
    })
  );
});

 

上記サンプルコードの、”キーの一覧と新しく登録されたキャッシュの名前を比較して異なるものをすべて削除してしまう”、というやり方は少々乱暴かもしれませんので、実際に実装を行う際にはどのようにキャッシュの削除を行うかは十分に考慮してください。

なぜ、activateイベントでキャッシュを削除するのか?

Service Worker自体は、Service Worker用のJSファイルを更新することで再登録されますが、キャッシュが削除されるわけではありません。よって、いずれどこかのタイミングで用済みとなった古いキャッシュを削除する必要が出てきます。

例えば、キャッシュの対象となっているindex.htmlに更新があった場合は、更新前のindex.htmlを保持しているキャッシュを削除しないかぎりユーザーにはいつまでも更新前のindex.htmlが表示されることになります。

Service Worker用のJSファイルが更新されたあと、新しいService Workerはバックグラウンドでインストールされますが、まだ動作はしません。新しいService Workerに制御が移るタイミングは、古いService Workerが制御しているすべてのページが閉じた後からになります。そのためinstallイベントで古いキャッシュを削除してしまうと、まだ動作している古いService Workerはキャッシュを使用できなくなってしまいます。

一方、activateイベントではページの制御が新しいService Workerに移っているので、古いService Workerが使用していたキャッシュを削除することができます。

何をキャッシュさせるか?

Service Workerにキャッシュさせるべきは、App Shellです。App ShellはアプリケーションのUIが機能するために必要な最小限のリソースです。App Shellをローカルにキャッシュすることで、アプリケーションのUI表示と使用可能となるまでの時間を短縮することができ、オフラインでの使用も可能になります。また要件に応じて、キャッシュが有効となるアセット類を指定してもよいでしょう。

しかし、何でもキャッシュさせればよいというものでもありません。例えば、WebGLベースのゲームは、一般的に使用するアセット類のサイズが大きく、これを毎度起動する際にサーバーからダウンロードするのは時間もかかり、通信コストもかさみます。たしかに、これらをキャッシュさせておけば、通信コストを下げ、ゲーム開始までの時間も短縮できます。

しかし、キャッシュストレージの容量には制限があり、また、cacheオブジェクトの代わりに容量の大きいIndexedDBを使用したとしてもクライアントのストレージを占有することになります。

PCなどのストレージ容量の大きいデバイスではあまり問題にならないかもしれませんが、スマートフォンのようなモバイルデバイスではストレージ容量を圧迫することにつながります。よって、なにをキャッシュさせるかは十分に考慮する必要があります。

リクエストのハンドリング

Service Workerは有効化(activate)された後、アイドル状態となり、WebページからのリクエストやサーバーからのPushを待ちます。

アイドル状態のとき、制御下にあるWebページでリンクをクリックするなどしてネットワークリクエストが発生するとService Worker ではfetchイベントが発生します。

self.addEventListener(‘fetch‘, function(e) {
    e.respondWith(
        caches.match(e.request)
           .then(function(response) {
              return response || fetch(e.request);
    }));
})

 

fetchイベントハンドラの引数として渡されるオブジェクトに、発生したリクエストが含まれるので、同リクエストがキャッシュ内に存在するのか調べ、

self.addEventListener(‘fetch’, function(e) {
    e.respondWith(
        caches.match(e.request)
           .then(function(response) {
              return response || fetch(e.request);
    }));
})

 

キャッシュ内にリクエストが存在すればそれを返し、存在しなければfetchメソッドを使用してネットワークにリクエストを投げます。

self.addEventListener(‘fetch’, function(e) {
    e.respondWith(
        caches.match(e.request)
           .then(function(response) {
              return
 response || fetch(e.request);
    }));
})

 

それぞれの処理結果をリクエスト元のWebコンテンツ側に返します。

self.addEventListener(‘fetch’, function(e) {
    e.respondWith(
        caches.match(e.request)
           .then(function(response {
              return response || fetch(e.request);
    }));
})

 

この動作により、Webコンテンツ側ではService Workerやキャッシュを意識することなく、これまで通りの方法でページの制作を行うことができます。

また既存のWebコンテンツにService Workerの機能を追加する場合も、Service Workerを登録するためコードを追加する以外の作業は基本的に必要ありません。

de:code 2018のセッション動画でService Workerを追加するデモを行っていますので、ぜひ実際の動作をご覧ください。

Web App Manifest

Web App Manifestは、デバイスのブラウザーによって[ホーム画面に追加]される際のアイコンや、ホーム画面から起動した際のスプラッシュアイコンや背景色、アプリケーションが動作するウィンドウのスタイルを定義します。

manifestは、json形式で記述し、以下のようなlinkタグをWebコンテンツ/アプリケーション側に追加して参照させます。

<link rel=”manifest” href=”/manifest.json“>

 

Web App Manifestについては、詳しい解説がMDNにあるので、そちらを参照することをお勧めしますが、以下に簡単な説明を兼ねたサンプルを掲示します。

{
   “lang”: “ja”, ← 言語
    “name”: “The enemy of galaxy”, ← アプリケーションの名前
    “short_name”: “T.E.O.G”, ← アプリケーションのショートネーム
    “start_url”: “/?utm_source=pwd”, ← 開始するときの URL (※1)
    “display”: “standalone”, ← ウィンドウのスタイル
    “background_color”: “black”, ← 背景色
    “description”: “銀河に平和を取り戻すためのゲームです。”, ← 説明文
    “orientation”: “portrait” ← 画面の向き
    “icons”: [{ (※2)
        “src”: “images/homescreen48.png”, ← 画像ファイルのパス
        “sizes”: “48×48”, ← サイズ
        “type”: “image/png” ← 画像の種類
      }, {
      “src”: “images/homescreen72.png”,
      “sizes”: “72×72”,
      “type”: “image/png”
   }]
}

(※1)Google Analytics等を使用しているときはクエリーストリングを使用することでPWAとして起動されたのか、ブラウザからページにアクセスしたのか判断が可能

(※2)iPhoneやiPadでアイコンが反映されない場合は、apple-touch-iconを使用

de:code 2018のセッション動画でWeb App Manifest を追加するデモを行っているので、ぜひ実際の動作をご覧ください。

現在のWindows 10バージョン 1803のProgressive Web Appsの動作

なお、2018年6月現在のWindows 10バージョン1803(OS ビルド 17134.112)では、残念ながらWeb App Manifestによるウィンドウの制御は有効になりません。

例えば、Windows 10のMicrosoft EdgeでProgressive Web Appsのページをタスクバーにピン留めして起動したとしてもWebブラウザーのUIを表示したまま起動してきます。

Windows 10でProgressive Web Appsにそれらしい動作をさせるにはUWP(Universal Windows Platform)アプリでラップする必要があります。

しかし、Windows 10の次のアップデートでは、Web App Manifestのdisplayの設定に対し、PWAのウィンドウは以下のような表示になるそうです。


 

マイクロソフトのProgressive Web Appsへの
取り組み

Windows 10の[Microsoft Store]から Progressive Web Appsとして作られたアプリを入手することができます。

Web App Manifestのところで書いたとおり、現在のWindows 10にプレーンな Progressive Web Appsをインストールしても、ネイティブアプリのような外観にはなりません。そのためMicrosoft Storeから入手できるProgressive Web AppsはUWPアプリにラップされています。

MicrosoftストアでのProgressive Web Appsの公開

MicrosoftストアでProgressive Web Appsを公開するには 2つの方法があります。

1つめがBingによる自動インデックスによる登録と、2つめがProgressive Web Appsの提供者がMicrosoftストアにProgressive Web Appsを提出するセルフパブリッシングです。

以下でこの 2つの方法について紹介します。

Bingによる自動インデックス(BETA)

現在はまだBETAの状態ですが、BingがWeb上のProgressive Web Appsを検出し、レビューを行い、自動的にMicrosoftストアに公開します。

Bingに自動インデックスされるための条件は、現在のところ以下のようなものがあります。

  • HTTPS、セキュアなエンドポイント
  • 高品質の manifest
  • オフラインサポート
  • ストアの審査をパス
    • デジタルグッズを販売しないこと
    • アダルトコンテンツを含まないこと
    • 不快なコンテンツは不可
  • Windowsならではの差別化がされている

上記リストで、最後の”Windowsならではの差別化がされている”というのは、少しわかりづらいかもしれません。

Microsoft Storeで公開されるProgressive Web Appsは、UWPアプリにラップされるのでWinRT (Windows Runtime) APIを使用することができます。

つまりWinRT APIを使用することで、Windows 10で使用した際には、純粋なProgressive Web Appsからはアクセスできないプラットプラットフォームやハードウェアリソースの機能を使用することができるため、これらを利用した機能を実装することで差別化を図ることができます。

Microsoftストア向けProgressive Web Appsの検証

決して、Progressive Web Appsの検証に限ったものでなく、ましてやMicrosoftストア向けのWebコンテンツ用に限定するものではありませんが、WebサイトやProgressive Web Appsの品質をチェックするためのsonarwhalというサービスが公開されています。

sonarwhalはProgressive Web AppsがMicrosoftストア向けの準備がてきているか、だけでなく、一般的なWebサイトやアプリケーションのパフォーマンスやユーザーエクスペリエンス、セキュリティの向上の役に立つ情報を提供してくれます。

Bingに検出されないようにするには

インターネットで公開しているProgressive Web AppsをMicrosoftストアから配布されたくない場合、Progressive Web Appsが以下のいずれかの条件に合致していた場合、Bingは自動インデックスを行いません。

  • manifest なし
  • https なし、もしくは オフラインで動作しない
  • Robot.txtファイルが設定されている
  • manifestのdisplay設定がbrowser

既に Microsoftストアで公開されているものを取り消したい場合は、アプリを削除するよう reportapp@microsoft.com宛にサービスリクエストを出します。

セルフパブリッシングによるMicrosoftストアでの公開

Bingの自動インデックスに期待するのではなく、明示的にMicrosoftストアでProgressive Web Appsを公開するにはdev.microsoft.comで開発者アカウントを取得し、パッケージ(appx)化してMicrosoftストアに提出します。アプリの内容がMicrosoftストアの審査をパスすればMicrosoftストアで公開されます。

Progressive Web Appsをパッケージ化するには以下の方法があります。

PWABuilder

PWABuilderはインターネットにホストされているサービスで、同じくインターネットにホストされているWebアプリケーションのためのmanifestファイルやService Workerのコード、Service Workerを登録するためのコードを生成します。

また、UWPのHostedアプリのパッケージも生成できるので、Progressive Web AppsをMicrosoftストアで公開する際にはこれをストアに提出します。

PWABuilderを使用した既存のWebアプリケーション用のmanifestファイルやService Workerのコードを生成する手順については de:code 2018 の セッション動画のデモをご覧ください。

Windows ストア(UWP) アプリの価値

前述しましたが、Progressive Web AppsをUWPアプリ(Hosted Web アプリ)としてラップすることで WinRT が提供するすべての機能を利用することができます。

これは予定表やアドレス帳といったプラットフォームが提供するソフトウェア的なリソースはもちろん、USBやセンサー類といったハードウェアリソースも含まれます。

アプリケーションが通常のWebブラウザー上で動作しているのか、UWPで動作しているかは JavaScriptのif文で簡単に判断できるので、WinRT APIにアクセスするためのコードをインターネットでホストされているコンテンツに含めておくことができます。

//UWP として動作しているかどうか
if(window.Windows){
   //セカンダリータイルをピン留めする
   tile = new Windows.UI.StartScreen.SecondaryTile(
       tileId, text, text,  activationArguments,
       newTileDesiredSize, logoUri);
}


JavaScriptで記述したUWPから,WinRT APIを使用するサンプルコードは以下に豊富に用意されているので、Windows 10限定となってしまいますが、純粋なProgressive Web Appsの機能だけでは実現できない要件がある場合は、こういった方法もあるということを覚えておくとよいかもしれません。

・コードサンプル – Windows アプリの開発

Visual Studio 2018を使用してProgressive Web AppsをUWPでラップし、WindowsRT APIを使用する方法については、de:code 2018 のセッション動画のデモをご覧ください。

その他、Progressive Web AppsをUWPとしてMicrosoftストアに公開することで、Microsoftストアの提供するマーケティングデータ (ダウンロード数、ユーザーの性別、年齢層、国、etc.)や、決済のためのストアのAPIが利用できたり、人気があればMicrosoftストアのトップページに掲示されたり、とさまざまなメリットもあります。

しかも、Hostedアプリであれば、共通のコンテンツでありながら純粋なままのProgressive Web Appsがインターネットでホストされているので、アプリストアを通さない場合のメリットも享受できます。

【参考】
・Windows デベロッパーセンター – PWAとWindows10

この記事では、UWPのHostedアプリについて紹介しましたが、Apache Cordovaでもさまざまなモバイル プラットプラットフォーム向けに Hostedアプリを開発することができます。興味のある方は以下の記事をご覧ください。

・Create a hosted web app using Apache Cordova

Progressive Web Appsの価値を高める API

ここまでWindows 10で動作するUWPアプリでラップされたProgressive Web Appsの紹介をしていましたが、ここからは再びプレーンな Progressive Web Appsについてです。

Progressive Web Appsは、ネイティブアプリのような体験を提供しますが、WebアプリケーションなのでWebブラウザーの機能を超えて動作することはできません。

例えば、表示速度や動作速度はネイティブアプリと比較するとどうしても劣ります。

しかし、Webブラウザーで新しくサポートされた以下のような機能を使用すれば、その能力的な差を縮めることができます。これまでの Webコンテンツにはなかった新しい体験をユーザーに提供したり、決済の仕組みもWeb標準のAPIを使用してこれまでよりも少ない工数で組み込むことができるので、これらを使用してアプリケーションの価値を高めることができます。

  • CSS3 3D Transform関連等GPUを使用する設定があり、これを意識的に利用することで描画速度を向上させることができます。
  • WebAssembly C言語などで記述し、あらかじめコンパイルしたものをWebブラウザーで動作させるため高速な計算が可能となります。
  • WebVR VRデバイスの提供するAPIをサポートし、ユーザーによりImmersiveな体験を提供できます。
  • Web Payments 決済画面その他、支払いに関する機能を提供します。これとPaypalやStripeなどが提供する決済用のAPIを繋げれば少ない工数で独自に決済期の機能を実装できます。自前の決済の仕組みがあれば、もう何かを販売するたびにアプリ ストアに使用料を支払う必要はありません

この他にも、最新のWebブラウザーにはこれまでできなかった機能が日々搭載されてきます。そういった新機能をいち早く利用できるのも、逆にレガシーなWebブラウザーには引き算した機能を提供できるのもProgressive Web Appsの良いところです。(もちろん、そう作れば、ですが)

Progressive Web Appsの位置づけ

PWAことProgressive Web Appsは「ネイティブアプリのような体験を提供する」という言葉が独り歩きしてしまい、まるで「ネイティブアプリを置き換える」といった印象を持っている人も多いようです。とはいえ、「ネイティブアプリを置き換えることはできない」というのも真ではないでしょう。

例えば、スマートフォンアプリには、アプリとしてインストールさせるためだけに外側をWeb Viewでラップしたいわゆる「ガワアプリ」と呼ばれるものがあります。こういったものは今後はProgressive Web Appsに置き換えられていくでしょう。

実際にイベントサイトや、カンファレンスのセッションスケジュールの部分をPWA化してユーザーが持ち歩けるようにしているサイトはすでにいくつか存在します。

こういったものもProgressive Web Appsが出てくる以前はWebページとは別にスマートフォン用のアプリを、場合によってはプラットフォームごとに別々の言語で開発し、それぞれのアプリストアに提出して審査を受けるといったことをしていました。

Webページとアプリの開発が共通化でき、配布の手間もかからないということは予算や時間的にも非常に大きなメリットがあります。

しかし、Webブラウザー内のJavaScriptからアクセスできないプラットフォームの機能を利用するアプリケーションや、高速な動作や描画を必要とする用途には今後もネイティブ アプリが選択されることでしょう。

Progressive Web AppsはWebアプリケーションとネイティブアプリの体験的なメリットを受け継ぎ、ちょうどその中間に位置し、その隙間を埋めるものです。

Webのページではpoorだったもの、逆にネイティブアプリではtoo muchだったものが、Progressive Web Appsには最適なのかもしれません。

少し作って試すだけの実装ならそれほど難しくありませんので、気になる人はぜひProgressive Web Appsを作ってみることをお勧めします。

 

週間PVランキング

新着記事

Powered byNTT Communications

tag list

アクセシビリティ イベント エンタープライズ デザイン ハイブリッド パフォーマンス ブラウザ プログラミング マークアップ モバイル 海外 高速化 Angular2 AngularJS Chrome Cordova CSS de:code ECMAScript Edge Firefox Google Google I/O 2014 HTML5 Conference 2013 html5j IoT JavaScript Microsoft Node.js Polymer Progressive Web Apps React Safari SkyWay TypeScript UI UX W3C W3C仕様 Webアプリ Web Components WebGL WebRTC WebSocket WebVR