HSP3Dish.jsでPWA
目次
言い訳から始める
今更の話ですが、とっくにHSP3の領域ではありません。 私はサーバーエンジニアでもネットワークエンジニアでもないので、正確な知識は持っていません。 出来るだけ情報を集めて、正確な記述をしようとしていますが完ぺきではないのでご了承ください。 正確な…とは言いつつも、分かりやすくするため意図的に例外を無視して言い切りの表現をしている部分もあります。 不安な箇所や気になる点があれば、自分で調べて確認してください。
※専門家の方へ … 改善点があればご指摘ください。(心理的or物理的に余裕があれば)修正いたします。
PWAとは
PWA(Progressive Web Application)とは、webサイトやwebアプリをネイティブアプリ(通常のアプリ)のように利用できるようにする技術や仕組みのことです。 詳しくは、検索して調べた方が正確な情報が得られます。ここではHSP3をPWA化するという視点で見ていきます。
webアプリをPWAにすると、ネイティブアプリのようにスマホやPCにインストールしてホーム画面にアイコンを追加することができます。 またリソースをキャッシュするように作られている場合は、オフライン環境下でも使用することが出来ます。 そのほかにプッシュ通知、バッジ(アイコン右上の数字)の表示を取り入れることも仕組みとしては可能です。もっと情報が欲しい場合は、下記サイトを参照してください。
      今PWAで意外と出来ること・出来ないこと
      https://qiita.com/anoChick/items/50673084c78c98ce64b7
    
公開はwebサイトの形で行うことができるため、アプリストアへの登録や審査を受ける必要がありません。 個人的にはこれが一番大きいと感じています。
HSP3Dish.jsでPWA
HSP3で作成したプログラムは、HSP3Dish.jsを使うことでwebアプリ化することが出来ます。 そして前述した通りwebアプリはPWA出来ます。 つまりHSP3でPWAが作成可能ということです。
アプリストアの登録も審査もハードルが上がっているのでありがたい話です。 PWAはあくまでブラウザ上で動作するものであるため、それゆえの制約はありますが、ストアに登録するほどではないちょっとしたアプリにはありがたい機能です。
作成例
例を見ながらのほうが分かりやすいと思うので、作成例を提示します。 スマホの場合は、サイトを開くとインストールするかのメッセージが開く場合もあります。 何も表示されない場合は、次の手順でインストール出来ます。
- iOSの場合、Safariで「共有アイコン(四角いボックスに上向き矢印)」をタップ。
「ホーム画面に追加」をタップ。 - Androidの場合、Chromeで開いてメニュー(右上の…アイコン)から「ホーム画面に追加」をタップ。
ダイアログが表示されるので「インストール」をタップ。 - Windowの場合、Chromeで開いてメニュー(右上の…アイコン)から「キャスト、保存、共有」>「ページをアプリとしてインストール...」をクリック。
 
      作成例:
      PWA:./pwa1/mobile01_f7.html
    
アンインストールは、通常のアプリと同じ手順で行ってください。
PWAに必要なもの
HSP3でPWAを作成した場合、アプリは以下のようなファイルで構成されています。 ここでは、これらのファイルの準備方法について記載していこうと思います。
- HSP3Dish.js(hsp3dish.js)
 - プログラム本体(???.data)
 - HTMLファイル(HSP3Dish helperの出力)
 - アイコン用画像ファイル
 - マニフェストファイル(manifest.json)
 - Service WorkerのJavaScriptファイル(service-worker.js)
 
PWAの細かい要件などはこちらのサイトが詳しいようです。 詳しい情報が知りたい方は、後で目を通すといいと思います。
HTMLファイル
HSP3Dish.jsとhtmlファイルは、HSP3Dish helperが出力してくれます。 HSP3Dish.jsはそのまま使うので、まずはhtmlファイルの書き換え作業です。
       次のmetaタグをheadタグに追加します。
    
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-capable" content="yes">
    このタグがあるとブラウザは、webページをwebアプリとして扱います。 ユーザーはこのページを「ホーム画面に追加」(ショートカットを作成)する事が出来るようになります。
PCで開いた場合、アドレスバー内の右にインストールのアイコンが出てくるはずなんですが出てきません。これについては調査中。
アイコン
普段HSP3でWindowsアプリを作っている感覚だと、無くても動くのではという気がしてしまいます。 HSP3の場合は、HPS3側がアイコンを用意してくれているので何も考えなくていいだけです。 必要なのが普通なのです。
などとHSP3の開発環境に感謝しつつ、アイコン(ファビコン)にするための画像を1個用意します。 これからアイコンに加工するので元絵は大きい方がいいです。 アプリのアイコンサイズに小さく表示されるので、小さくなっても判別しやすい絵がいいと思います。
元画像をアイコンに加工します。ここからは単純作業です。
- 手作業だと大変すぎるので、ジェネレータサイトを使います。
Favicon generator
https://realfavicongenerator.net/ - 準備した画像を「Drop your favicon image here」にドラッグ&ドロップします。
 - どんな出力結果になるかを表示してくれるので、好みに合わせて調整します。
 - PWA化した後のアプリ名もここで設定しておきます。
 - ファビコン パスは、後で修正するのでそのままで構いません。
 - Nextを押して次に進みます。
 - HTMLやReactなど選べますが、HTMLのままにしておきます。
 - downloadボタンを押すと、作成されたアイコン一式をダウンロードできます。
 - このページには作成したファイルの使い方も書いてあるのでメモしておきます。
<link rel=…と数行書かれた部分です。
headタグにこれらのコードを埋め込めばいいみたいですが、使い方は後述します。 - ダウンロードしたfavicon.zipの中身を確認します。以下のファイルが入っています。
apple-touch-icon.png
favicon-96x96.png
favicon.ico
favicon.svg
site.webmanifest
web-app-manifest-192x192.png
web-app-manifest-512x512.png 
アイコンファイルの準備は以上で終了です。 画像サイズとファイル形式さえ合わせれば自作でもいいはずなので、ここで修正を加えるのもありだと思います。
マニフェストファイル
manifestファイル(マニフェストファイル)は、PWA化したアプリのアプリ名やアイコン画像などを指定したJSON形式ファイルです。 アプリ化に必要な情報が入っています。記載する内容は、検索すると出てくるので調べてください。 なお、iOSはmanifestファイルの一部の項目に対応していません。対処方法については後述します。
これも手作業で作ると大変なので、先ほどのジェネレータの出力を使用します。このファイルです。
site.webmanifest
そのまま使用できますが、今回は最後の方を次のように修正してみました。 必ず必要な修正ではありません。修正時はカンマに注意。
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "fullscreen",
  "orientation": "portrait-primary",
  "dir": "ltr",
  "lang": "ja-JP"
    追加した内容と、そのほかに今後使いそうな項目についていくつか説明を記述しておきます。
      display
       アプリ起動時にブラウザUIをどれだけ表示するかを設定します。
      省略するとfullscreenが選択されるので書いた効果はないですが、standaloneに書き換えて使うこともあるかなと思って書いてみました。
      fullscreen …アドレスバー、ステータスバー、ナビゲーションボタンなどが非表示
      standalone …アドレスバー、ナビゲーションボタンなどが非表示
    
      orientation
       起動時の画面の向きを定義できます。端末によっては設定が無視されることがあります。(手元の端末では無視されました。)
      向きに依存しないアプリならanyを指定するか、orientationを指定しない。
      縦向きの場合は、portraitを指定。
      横向きの場合は、landscapeを指定。
      プログラム側でも効果がない場合を想定しておくことをお勧めします。
      このほかの設定値についてはこちらも参照ください。
      PWAのManifestにおけるorientationメンバの挙動メモ
      https://zenn.dev/takumi1001/articles/809f2e5587cb8a
    
      start_url
       アプリ起動時に開くURLを指定するものですが、HSP3Dish.jsの場合は単一ページのアプリなのでmanifestファイルの設置場所(後述します)さえ気を付ければ省略しても問題ないようです。
      指定が必要な場合は、マニフェストファイルのURLからの相対URLを指定すればいいようです。
    
      description と screenshots
       文章とスクリーンショットでPWAの説明を設定します。
      この2つが設定されているとAndroidの場合のみインストール時に説明が表示されます。
      (→PWA をインストール可能にする インストールプロンプトのカスタマイズ)
      この他で表に現れることは無いようです。
      ↓実装例
      画像と説明文を表示する、よりリッチなPWAインストール
      https://www.suzukikenichi.com/blog/richer-pwa-install-ui-displaying-scheenshots-and-description/
    
      dir
      テキストの読む方向を設定します。日本語は、左から右なのでltrです。
    
      lang
      言語設定をします。日本語はja-JPです。
    
そのほかmanifestファイルの詳しい解説は、こちらを参照してください。
最後に、修正を加えたファイルは必ずUTF-8で保存してください。(ASCII文字のみで日本語などを使わない場合は、Shift-JISと表示されてても大丈夫です。理由はUTF-8とShift-JISの文字コードの問題で…以下長くなるので省略)
アイコンとmanifestファイルを設定する
先ほど作成した画像ファイルとmanifestファイルは、htmlファイルと同じサーバーに置きます。 画像ファイルはディレクトリを作って置いても構いませんが、パスを正しく記述する必要があります。 manifestファイルはhtmlファイルと同じディレクトリ内に設置します。 今回は以下のような構成で設置することにします。
example.com/
|-- /appName/
|    |-- index.html
|    |-- site.webmanifest
|    |-- /favicon/
|        |-- apple-touch-icon.png
|        |-- favicon-96x96.png
|        |-- favicon.ico
|        |-- favicon.svg
|        |-- web-app-manifest-512x512.png
|        |-- web-app-manifest-192x192.png
    
       manifestファイルにはアイコン画像へのパスが記述されているので修正が必要です。
      site.webmanifest ファイルをテキストエディタで開いて、パス名が "src": "/… となっているので正しいパス名に書き換えます。
      パスの1文字目が「/」の場合、ルートディレクトリを意味します。
      「./」に書き換えて相対パス表示にする必要があります。
    
修正後
      "src": "./favicon/web-app-manifest-192x192.png",
      …
      "src": "./favicon/web-app-manifest-512x512.png",
    
       htmlファイルも修正が必要です。
      先ほどアイコン作成をするためにジェネレータを使った際、メモしたlinkタグをhtmlに追加します。headタグ内のmetaタグの後なら何処でも構いません。
      画像ファイルへのパスも修正が必要です。パス名が href="/… となっているので正しいパス名に書き換えます。
    
  <link rel="icon" type="image/png" href="./favicon/favicon-96x96.png" sizes="96x96" />
  <link rel="icon" type="image/svg+xml" href="./favicon/favicon.svg" />
  <link rel="shortcut icon" href="./favicon/favicon.ico" />
  <link rel="apple-touch-icon" sizes="180x180" href="./favicon/apple-touch-icon.png" />
  <link rel="manifest" href="site.webmanifest" />
    
       iPhoneの場合、manifestファイルの一部が対応していません。headタグに次の行を追加します。
    
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="MyWebApp">
    
       このタグ内に描かれているMyWebAppは、アイコンに表示されるアプリ名です。manifestファイルのnameと同じものに書き換えておきます。
      このmetaタグに関する解説は以下のサイトを参照。apple-touch-iconの指定は、最近は不要になっているようです。
    
      ホーム画面へのアプリ追加(iOS編)【これからはじめるPWA】
      https://bagelee.com/programming/pwa/ios-korekara-pwa/
    
ここまで作業をすれば、「ホーム画面に追加」が出来るようになります。 インストール結果にアイコンやアプリ名が反映されるようになります。 オフラインでの利用が必要ないなら、これ以降の作業は不要です。
Service Worker
ここから先は、少し難しくなるので無理してやる必要はないと思っています。どうしてもオフラインでの動作が必要だったり、起動を早くしたりしたい場合に手を出してください。
さて、webアプリを丸ごとキャッシュしてオフラインでも使えるようにするためには、Service Workerという機能を利用します。
Service Workerファイルは、キャッシュの取り扱いについて記述したJavaScriptファイルです。 ローカルにキャッシュさせるファイルを指定したり、古いキャッシュの更新に関する動作が書かれています。 このファイルがあると、オフラインでもアプリを実行することができるようになります。 webサイトに更新が無かったりオフライン中などの場合は、キャッシュを使ってService Workerがサーバーのふりをして動作してくれます。 キャッシュを使ってくれるので、2回目以降の読み込みが早くなるなどのメリットもあります。
Service Workerは、HTMLファイルから呼び出して使用します。 HSP3アプリでの使用の場合、1つのアプリに対して1ファイル用意します。 管理の面から、複数のアプリで1つのService Workerファイルを共用したりしないほうがよさそうです。
Service Workerの例
// キャッシュのバージョンを識別する名前
// アプリ更新時には必ず変更してください。
// 	例:'アプリ名-YYYYMMDD-vX'
const CACHE_NAME = 'app-cache-20250102-v5';
// キャッシュしたいリソースのリスト
const urlsToCache = [
  'mobile01_f7.html',
  'hsp3dish.js',
  'mobile01.data',
  // 必要なリソースをここに追加
];
// インストールイベント
self.addEventListener('install', event => {
  // 新しいService Workerを即座にアクティブにする
  self.skipWaiting();
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        // 指定されたリソースをキャッシュに追加する
        return cache.addAll(urlsToCache);
      })
      .catch(error => {
        // ネットワークの切断やファイルサイズ オーバーなどで、キャッシュに失敗する事があります。
        // 必要であれば、Service Workerからフロントエンドへメッセージを送信する処理を追加してください。
        console.error('キャッシュの追加に失敗しました:', error);
      })
  );
});
// アクティベートイベント
// 古いキャッシュをクリーンアップする。
// Service Workerが新しいバージョンに更新されたとき、古いキャッシュを削除します。
self.addEventListener('activate', event => {
  event.waitUntil(
    // 古いキャッシュを削除
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(name => name !== CACHE_NAME).map(name => caches.delete(name))
      );
    }).then(() => self.clients.claim()) // クライアントがすぐに新しいバージョンのService Workerを利用する
  );
});
// フェッチイベント
// レスポンスを返す
// ネットワークが利用可能な場合は、常に最新のデータを取得してキャッシュを更新します。
self.addEventListener('fetch', event => {
  // リクエストメソッドがGETの場合のみキャッシュを使用
  if (event.request.method === 'GET') {
    event.respondWith(
      fetch(event.request)
        .then(response => {
          // レスポンスのステータスチェックを行い不安全・不要なものを除外
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response;
          }
          // ネットワークから最新のリソースを取得し、キャッシュに保存
          return caches.open(CACHE_NAME).then(cache => {
            cache.put(event.request, response.clone());
            return response;
          });
        }).catch(() => {
          // オフライン時にはキャッシュからリソースを提供
          return caches.match(event.request);
        })
    );
  } else {
    // GET以外のリクエストの場合、通常のネットワークリクエストを行う
    event.respondWith(fetch(event.request));
  }
});
    
       この例は、ネットワークが利用可能な場合は、常に最新のデータを取得してキャッシュを更新します。なんだかたくさん書いてよくわからんと思いますが、修正が必要なのはCACHE_NAMEとurlsToCacheの部分だけです。
      知識がある方は、機能追加とかしてください。
      (参考資料:キャッシュ)
      ただし作り方を間違えると、キャッシュが更新されなくなってしまうことがあります。
      この場合ブラウザのキャッシュクリアが必要になるので注意が必要です。
    
      CACHE_NAME
       アプリのバージョンを識別するための名前を記述します。
      アプリを更新した場合は、必ずここを更新する必要があります。
      Service Workerは、CACHE_NAMEを見比べてキャッシュの更新をするかどうかを判定しています。変わっていないとキャッシュから読み込まれてしまうため、更新結果がユーザーに見えません。
    
      urlsToCache
       キャッシュしたいファイル(オフラインでも参照出来るようにしたいファイル)のリストを記載します。
      使用するファイルを例のように記述してください。
    
一番重要な事を忘れていました。Service Workerを動作させるには、httpsまたはlocalhost環境が必要です。 サーバー上に置く必要があり、ローカルでは動きません。
Service Workerの影響範囲
Service Workerは浅い階層に設置すると、設置されたディレクトリ以下の階層全てに影響を及ぼす可能性があります。 例えばルートディレクトリに置いてしまうと、同じドメイン内全てのページで動作してしまいます。 他の階層に置いたPWAがなんかかの影響を受けてしまう可能性があります。(インストールができなくなるなど)
無用なトラブルを避けるため、PWAは1つにつき1つのサブディレクトリを作って配置するといいようです。
      例
      example.com/
      |-- /pwa1/ (1個目のアプリ)
      |    |-- index.html
      |    |-- service-worker.js
      |    |-- …
      |
      |-- /pwa2/ (2個目のアプリ)
      |    |-- index.html
      |    |-- service-worker.js
      |    |-- …
    
       Service Workerは、基本的に呼び出し元のhtmlファイル(helperが出力するHTML)と同じ階層に置いた方がいいようです。
      異なる階層に置いてしまうと、HTML内でService Workerを登録する際に、scpe(影響範囲を指定する引数)を明示的に設定する必要があります。
      修正の手間が増えるのと修正忘れを考えると、Service WorkerふぁるはHTMLファイルと同じ階層に置いたほうがいいという訳です。
    
Service Workerを設置
Service Workerのjsファイルを用意します。ここではファイル名をservice-worker.jsとします。service-worker.jsは、HTMLファイルと同じディレクトリに置きます。
       HTMLファイル内に、Service Workerを登録する記述を追加します。
      追記箇所はどこでも構いません。私は先ほど追加したlinkタグの直後に入れています。
    
    <!-- ServiceWorker -->
    <script>
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('service-worker.js')
                .then(registration => {
                    console.log('Service Worker がスコープに登録されました。:', registration.scope);
                }).catch(error => {
                    console.log('Service Worker の登録に失敗しました。:', error);
                });
        }
    </script>
    変更箇所はService Workerのファイル名だけです。Service Workerファイルは、どのアプリでも同じファイル名を使った方がHTMLの書き換えが楽そうですね。
さてこのあたりで今回のファイルの設置例を確認しておきましょう。
example.com/
|-- /appName/
|    |-- index.html
|    |-- site.webmanifest
|    |-- service-worker.js
|    |-- /favicon/
|        |-- apple-touch-icon.png
|        |-- favicon-96x96.png
|        |-- favicon.ico
|        |-- favicon.svg
|        |-- web-app-manifest-512x512.png
|        |-- web-app-manifest-192x192.png
  プッシュ通知
アプリのバージョンアップなどをプッシュ通知でお知らせできると便利そうですね。 通知の配信サービス Firebase Cloud Messaging(FCM)などを使えば出来るらしいです。 他にもいろいろとやることがあるらしく大変そうなのであきらめました。
動作確認
PWAに必要なものはそろいました。とりあえずPCのウェブブラウザで開いて動作確認していきます。
manifestファイルの動作確認を行います。 サーバーに上げたHTMLファイルにアクセスして、デベロッパーツールを起動します。 アプリケーションを選択して、マニフェストを選択します。 ブラウザが読み取ったmanifestファイルの情報が表示されます。 エラーと警告などがいくつか表示されますが、致命的なものでなければ動作には影響しないようです。問題があればここを確認しながら修正してください。
Service Workerの動作確認を行います。 先ほど同様に、デベロッパーツール>アプリケーション>ServiceWorkers を開きます。 ブラウザに保存されているService Workerの情報が表示されます。 正常に動作している場合、ステータスは緑の丸が付き、実行中ですと表示されています。
       キャッシュストレージの三角をクリックすると、キャッシュされているService WorkerのCACHE_NAMEが確認できます。ここでService Workerが正常に更新されているかを確認することができます。service-worker.jsのCACHE_NAMEを修正してサーバーに上げなおしてwebアプリを再読み込みしてみると、キャッシュストレージに表示される内容が新しくなる事が確認できるはずです。更新前の物が残る場合は、Service Workerのプログラムが正しく動作していない可能性があります。
    
デベロッパーツールをざっと眺めて問題なさそうなら、スマホのブラウザで開いてインストールしてみます。 ちゃんと出来ていれば、ショートカットの作成ではなくインストールが可能です。 想定したアイコンが使われているか、インストール後に表示される名前に問題は無いか、オフラインでも問題なく使用できるかなどの動作確認を行ってください。
最後に
これでHSP3でのPWA作成が出来ました。 前回のフルスクリーン化と合わせて使用するとかなりネイティブアプリっぽくなると思います。
manifestファイルはもう少し作りこみたいですが、とりあえずはこれでいいでしょう。きっといずれ誰かが修正案を出してくれるにちがいありません。 他にも修正や調整をしたいところはありますが、まずはPWA化の道が開けたということで今回はこれにて。

