Compose Multiplatformで作ったWebアプリをPWA化する

## TL;DR

```diff_toml:gradle/libs.versions.toml
[versions]
# ...

+ yuyuyuyuyu-composePwa = "0.1.0"

[libraries]
# ...

[plugins]
# ...

+ yuyuyuyuyu-composePwa = { id = "dev.yuyuyuyuyu.composepwa", version.ref = "yuyuyuyuyu-composePwa" }
```

と

```diff_kotlin:composeApp/build.gradle.kts
// ...

plugins {
//  ...

+   alias(libs.plugins.yuyuyuyuyu.composePwa)
}
```

を追記して Gradle project sync して

```shell
./gradlew :composeApp:buildPWA
```

を実行すればOK

`composeApp/build/dist/wasmJs/productionExecutable/` にPWAとしてビルドされたWebアプリが出力されます

（`./gradlew :composeApp:wasmJsBrowserDistribution` を `./gradlew :composeApp:buildPWA` で置き換えるイメージです）

## はじめに

AndroidエンジニアだってWebアプリ作りたくなる時くらいありますよね？！？！？！

でもやっぱりスマホにインストールしてオフラインで動かしたいですよね？！？！？！？

普段使っている技術スタックでWebアプリを作ってそれをスマホにインストールできたらうれしいですよね？！？！？！

でもCompose Multiplatformで作ったWebアプリをPWA化するライブラリ（プラグイン）が無い！！！！！！😭

ということで作りました

https://github.com/yu-ko-ba/ComposePWA

## ComposePWAの導入方法

`gradle/libs.versions.toml` と `composeApp/build.gradle.kts` でそれぞれ下記のように追記し、 `gradle sync` すればOKです
```diff_toml:gradle/libs.versions.toml
[versions]
# ...

+ yuyuyuyuyu-composePwa = "0.1.0"

[libraries]
# ...

[plugins]
# ...

+ yuyuyuyuyu-composePwa = { id = "dev.yuyuyuyuyu.composepwa", version.ref = "yuyuyuyuyu-composePwa" }
```

```diff_kotlin:composeApp/build.gradle.kts
// ...

plugins {
    // ...

+   alias(libs.plugins.yuyuyuyuyu.composePwa)
}

// ...
```

## 使い方
基本的には
```shell
./gradlew :composeApp:buildPWA
```
を実行するだけでOKです

`./gradlew :composeApp:buildPWA` を実行することで、Compose Multiplatformで作ったWebアプリがPWAとしてビルドされ、 `composeApp/build/dist/wasmJs/productionExecutable/` に出力されます

## 出力されるPWAをカスタマイズしたい場合

### アプリアイコンを独自のものにする場合

アプリアイコンを独自のものにしたい、という場合は `ngx-pwa-icons` が便利です

アプリアイコンのPNGファイルがあるフォルダで

```bash
npx ngx-pwa-icons
```

を実行し、出来上がった `icons/` で `composeApp/src/wasmJsMain/resources/icons/` を置き換えてください。

※ `ngx-pwa-icons` の実行にはNode.jsが必要になります

https://github.com/pverhaert/ngx-pwa-icons

### それ以上のカスタマイズがしたい場合

自動生成された `composeApp/src/wasmJsMain/resources/manifest.json` や `composeApp/workbox-config.js` をいじってください

内部的にはnpmモジュールの [workbox-cli](https://www.npmjs.com/package/workbox-cli) を使っています

Workboxのドキュメント

https://developer.chrome.com/docs/workbox

workbox-cliを呼び出している箇所

https://github.com/yu-ko-ba/ComposePWA/blob/main/plugin/src/main/kotlin/dev/yuyuyuyuyu/ComposePwaPlugin.kt#L29

## ComposePWAはなにをするのか

`buildPWA` というGradleタスクを提供します

`buildPWA` は呼び出されると

1. ビルドキャッシュの削除（前回のビルドで生成されたサービスワーカーを削除するため）
1. 依存しているnpmモジュールの同期（念のため）
1. 必要なHTMLタグの追記（既に存在していたらスキップ）
1. 必要なファイルの生成（既に存在していたらスキップ）
1. Webアプリとしてビルド
1. サービスワーカーの生成

を行い、Compose MultiplatformのソースコードをPWAとしてビルドします

### composeApp/src/wasmJsMain/resources/index.html にどのような変更を加えるのか

```diff_html
<!doctype html>
<html lang="en">
  <head>
    <!-- ... -->

+   <script type="application/javascript" src="registerServiceWorker.js"></script>
+   <link rel="manifest" href="manifest.json">

  </head>
  <!-- ... -->
<html>
```

### 自動生成されるファイルたち
- composeApp/workbox-config.js
- composeApp/src/wasmJsMain/resources/icons/icon-*.png
- composeApp/src/wasmJsMain/resources/manifest.json
- composeApp/src/wasmJsMain/resources/registerServiceWorker.js

## Workboxが分かる人向け

### デフォルトのキャッシュ戦略

デフォルトのキャッシュ戦略は `Stale-while-revalidate` にしてあります

基本的にキャッシュから読み込みつつ更新があるかチェック、更新があったらバックグラウンドでダウンロードしておいて次回起動時に差し替える、というやつです

## おわりに

Compose Multiplatform製のWebアプリをPWA化するプラグインを作ったと言ってはいますが、やっていることはほとんど `gradle-node-plugin` を使って `workbox-cli` を呼び出しているだけ、なのでこれらを作ってくださった偉大なる先人たちに圧倒的感謝です！！！！！！

https://github.com/node-gradle/gradle-node-plugin

https://github.com/googlechrome/workbox

## おまけ

### GitHub Pagesでホストするには

ComposePWAを使ってPWA化したWebアプリをGitHub Pagesでホストしているので、参考までにそこで使っているGitHub Actionsのリンクを貼っておきます


https://github.com/yu-ko-ba/how-old-am-i/blob/main/.github/workflows/deploy.yml