かずきのBlog@hatena

日本マイクロソフトに勤めています。このブログは個人ブログなので、ここに書いている内容は個人的な意見で会社の公式見解ではない点にご注意ください。好きなものは XAML と C# 。苦手なものは型の無い言語です。

Progressive Web app を Windows 10 のインストールパッケージに固めてしまおう(Microsoft ストアに出せるようになるよ)

さて、Progressive Web app(PWA)が Microsoft store で配られるのですが自分のサイトも!!と思ってる人は以下のような手順でいけると思います。

マニフェストを用意しよう

PWA Builder というサイトを使うと簡単に出来ます。

www.pwabuilder.com

まぁ、マニフェストファイル用意するだけなら別にいらないんですが。 このサイトに URL を入れて開始すると manifest.json を生成してくれます。まだマニフェスト無いひとはやってみよう!

f:id:okazuki:20180417155412p:plain

manifest.json という名前で保存して Web サイトの html の head タグに以下のような定義を追加します。

<link rel="manifest" href="manifest.json">

manifest.json と修正した html をデプロイしましょう。

注意点

Azure Web app にデプロイすると json ファイルへアクセスしても 404 エラーになるので以下のような web.config もセットでデプロイしましょう。

<?xml version="1.0"?>

<configuration>
    <system.webServer>
        <staticContent>
            <mimeMap fileExtension=".json" mimeType="application/json" />
     </staticContent>
    </system.webServer>
</configuration> 

ネイティブ API (Windows Runtime の API)を呼び出してみよう

あとは固めておしまいなんですが、面白くないので Windows Runtime の API を叩いてみましょう。 そのまま呼べばいいのですが、それだとブラウザで見たときとかにエラーになるので実行時に API の存在チェックなどをして UWP として動いてないときは、Windows Runtime の API は叩かないようにします。

例えば、以下のような感じ。

<!DOCTYPE html>
<html>

<head>
    <link rel="manifest" href="manifest.json">
    <title>winrt sample</title>

    <script type="text/javascript">
        function myAlert() {
            if (isInUWP()) {
                var dlg = new Windows.UI.Popups.MessageDialog('Hello world');
                dlg.showAsync();
            } else {
                alert('Hello world');
            }

        };

        function isInUWP()
        {
            return typeof Windows !== 'undefined';
        }
    </script>
</head>

<body>
    <h1>PWA sample</h1>
    <button onclick="myAlert()">Alert from Windows Runtime API.</button>
</body>

</html>

ブラウザだと alert 関数で、UWP として動いてるときは Windows Runtime API の MessageDialog クラスを使って書いてます。 デプロイしてみましょう。これで下準備完了です。

パッケージ化

PWA Builder のコマンドラインツールを入れます。 Node.js があれば npm で入れれます。

npm install -g pwabuilder

まずは、以下のコマンドを叩きます。これでマニフェストから Windows 10 のアプリパッケージをつくるための下準備をしてくれます。

pwabuilder WebサイトのURL -d .\output -p windows10

そうすると output フォルダに アプリ名\PWA\Store packages\windows10 というフォルダが出来ます。 その中の manifest\appxmanifest.xml がストアに提出するときに使うアプリのマニフェストファイルになります。これを適切な値にしましょう。

3 箇所 INSERT-YOUR-... という箇所があります。これは DevCenter ダッシュボードから取得した値にします。 アプリを登録したときにアプリ情報だったかな?で見れる値です。

因みに、ここの編集をしないで次の手順をすると、ここの値をちゃんと入れろ!みたいなログが出ます。

[error] pwabuilder  : Failed to package the Windows 10 Platform app.
                      The specified path does not contain a valid app manifest file.
                      The application manifest is incomplete. Register the app in the Windows Store to obtain the Package/Identity/Name,
                      Package/Identity/Publisher, and Package/Properties/PublisherDisplayName details.
                      Then, use this information to update the corresponding placeholders in the appxmanifest.xml file before
                      creating the App Store package.

とりあえず試すだけなら適当に変えれば OK です。ストアに出すときにはダッシュボードに書いてある値にしましょう。

例えば適当に変えたらこんな感じね。

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build" IgnorableNamespaces="uap mp build"> 
    <Identity Name="winrtsample" Publisher="CN=sample" Version="1.0.0.0" ProcessorArchitecture="neutral"/> 
    <mp:PhoneIdentity PhoneProductId="06bef117-32a6-3ec2-d8e4-2acdf8a2626a" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> 

    <build:Metadata>
        <build:Item Name="GenerationTool" Version="pwabuilder"/>
        <build:Item Name="GenerationToolVersion" Version="2.0.3-rc.0"/>
    <build:Item Name="PlatformId" Value="windows10"/>
    <build:Item Name="PlatformPackage" Value="pwabuilder-windows10"/>
    <build:Item Name="PlatformVersion" Version="2.0.3-rc.1"/>
        <build:Item Name="GeneratedFrom" Value="CLI"/>
        <build:Item Name="GenerationDate" Value="2018-04-17 07:30:34 Z"/>
        <build:Item Name="GeneratedURL" Value="https://pwalab-okazuki.azurewebsites.net/manifest.json"/>
    </build:Metadata>

    <Properties> 
        <DisplayName>winrtsample</DisplayName> 
    <PublisherDisplayName>okazuki</PublisherDisplayName>

    <!-- 省略 -->

</Package>

これで準備が出来ました。output\アプリ名\PWA\Store packages\windows10 にコマンドプロンプトなどで移動して以下のコマンドを打ちましょう。

pwabuilder package -p windows10

package フォルダが出来て、その中に windows.appx というファイルが生成されます。これがストアに提出するパッケージです。

サイドローディング(野良アプリインストール)して試してみよう

サイドローディングするためには証明書で署名する必要があります。Windows 10 SDK が入っていれば以下のコマンドでさくっと作れます。

makecert -r -pe -n "CN=sample" -eku 1.3.6.1.5.5.7.3.3 -pe -sv my.pvk my.cer
pvk2pfx -pvk my.pvk -spc my.cer -pfx my.pfx
signtool sign /fd SHA256 /a /f .\my.pfx .\windows.appx

パスワードを求められるので None とでも押しておきましょう。パスワードなしていく感じになります。 以下のようなログが出れば成功です。

Done Adding Additional Store
Successfully signed: .\windows.appx

注意

makecert コマンドの CN=xxxx のところは appxmanifest.xml に書いた値とそろえましょう。

証明書のインストール

では、この証明書を信頼済みの証明書としてインストールします。(企業でサイドローディングで配布するときはきちんとした証明書を使って署名するといい感じになると思います)

my.pfx をダブルクリックして保存場所をローカル コンピューターにします。いくつか次へ次へと進めていくと証明書ストアを選択する画面になります。そこで「信頼されたルート証明機関」を選択します。

インストールして実行

インストールが出来たら windows.appx をダブルクリックしてみましょう。以下のような画面が立ち上がりインストールできます。

f:id:okazuki:20180417164338p:plain

証明書関連で何か失敗してるとインストールボタンを押したところでエラーになります。もしくは Windows でサイドローディングを許可されてないか。

サイドローディングは、設定で更新とセキュリティの開発者向けのところで選択できます。

実行してボタンを押してみましょう。Webサイトはアラート関数、アプリとして実行すると Windows のダイアログが出ます。

f:id:okazuki:20180417164652p:plain

ちなみに appx にしてインストールしてますが Web 上の html/javascript を実行してるのでアプリの更新は Web サイトの更新で OK です。 お手軽。

ストアに出すには

Microsoft Store に出すには appxmanifest.xml をダッシュボードの値を編集したりアイコンをきちんとしたものにしたりして提出すれば審査を受けてストアに公開されると思います。

まとめ

ということで Web サイトを公開しててネイティブの API を使えばもうちょっといい体験をユーザーに提供できるのになぁと思っているけれど、だからといってネイティブアプリを 1 から作るほどのリソースも無い…というケースでは、上記のような方法で Web サイトに追加する形でストアからインストールしてくれた人にはネイティブの API を使った機能を提供しつつ Web サイトでは今まで通りの機能ということが出来そうですね。

Web サイトでは特定のリッチな機能のページにアクセスしたり、OS を検出するコードをを埋め込んで Windows 10 だったらストアへの誘導の導線を出すことで、ライトユーザーは普通に Web で、使い込んでくれてるユーザーはインストールしてくれるかも?という流れが出来そうですね。

頑張ればオフラインでも動くようにしたりとか出来るので強い。