9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VR法人HIKKYAdvent Calendar 2023

Day 15

Nuxt3のメタバースサービスにPWAを導入した話

Last updated at Posted at 2023-12-15

この記事はVR法人HIKKY Advent Calendar 2023、15日目の記事です。

昨日の記事は@otsuka_kenchanさんの「Railsで開発を行うときに考えていること」でした。

フロントエンドしかわからない自分にはオフチョベットしたテフを(以下略)でした。Railsに触れる方はぜひ。

はじめに

みなさん、PWAしてますか?

VR法人HIKKYでフロントエンドのリードエンジニアをしています、伊織です。

先日、Nuxt3で実装されている「My Vket」というサービスにPWA(Progressive Web Apps)を導入しました。

My VketはWebブラウザ上で誰もが気軽に楽しめるメタバースサービスです。3D空間に自分の部屋を作ったりできます。スマホブラウザで動きます。すごいね。

この記事では「なぜブラウザメタバースにPWAを導入したか?」やNuxt3でのPWA導入手順と課題を書きます。
解説記事というよりほぼ回想録です。

※PWAやServiceWorkerそのものの解説は省略します

PWA導入の動機

My Vketはスマホブラウザでもアクセスできるメタバースサービスで、このようなトップページになっています。

My Vketトップページ

あまりにもかわいいアバターに目を奪われてしまったかもしれませんが、実はこのトップページ(このサービス)には

  • トークルーム
  • イベント
  • SNS機能(フォロー、DM)

が含まれており、これらのプッシュ通知が送りたいと要望が出てきました。
メタバースにコミュニケーションは欠かせませんから、プッシュ通知は欲しいですよね。

これまでスマホ端末へのwebプッシュはAndroid限定でしたが、2023/03に配信されたiOS16.4でiOSでも対応されました。

ホーム画面に追加したWeb Appの通知

ただし上記にあるように「ホーム画面に追加したWeb App」への対応であり、ホーム画面に追加(=PWAインストール)しないと通知が飛びません。

ということで、スマホでもプッシュ通知を飛ばすためにPWAを導入することになりました。

今回はスコープ外として実装しませんでしたが、メタバースのレンダリングに必要なファイルをキャッシュできるとローディングが早くなって快適になります。

元々のデザインもアプリっぽいしPWAになると快適そう!PWA最高!

Nuxt3へのPWA導入

@vite-pwa/nuxtのインストール

Nuxt3でのPWAには@vite-pwa/nuxtを利用しました。
入れるだけでServiceWorkerもmanifestファイルも作ってくれる優れもの。

READMEに記載の通り、インストールしてmoduleに追加します。

yarn add @vite-pwa/nuxt -D
nuxt.config.ts
import { defineNuxtConfig } from 'nuxt/config'

export default defineNuxtConfig({
  modules: [
    '@vite-pwa/nuxt'
  ],
  pwa: {
    /* PWA options */
  }
})

PWA optionsの設定については公式のサンプルが参考になりました。
manifestファイルもoptionとして定義します。

PWAの設定

@vite-pwa/nuxtを入れるとServiceWorkerが自動生成されます。

大変便利なのですが、ここで3つの課題が出てきます。

  1. ServiceWorkerを自動更新にしたい
  2. プッシュ通知の実装どうする?
  3. キャッシュをしない設定はある?

ServiceWorkerを自動更新にする

registerType: 'autoUpdate'とすると、クライアントサイドでも自動で更新が反映されるようになります。

VitePWA({
  registerType: 'autoUpdate'
})

プッシュ通知の実装どうする?

ライブラリを使う or 自前で実装する。

今回は自前で実装しました。
ServiceWorkerの自動生成を止めて自前のServiceWorkerを使います。

VitePWA({
  strategies: 'injectManifest',
  srcDir: 'service-worker',
  filename: 'sw.ts'
})

ServiceWorkerにプッシュ通知のイベントリスナーを定義します。

src/service-worker/sw.ts

declare const self: ServiceWorkerGlobalScope
self.__WB_DISABLE_DEV_LOGS = true

// プッシュ通知を受信するイベント
self.addEventListener('push', (event) => {
  // event.dataはサーバーサイドから送られてきた通知データのこと
  const data = event.data?.json()
  if (!data) return

  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      data: {
        link: data.link,
      },
      image: data.image,
    })
  )
})

// 通知を押下したときのイベント
self.addEventListener('notificationclick', (event) => {
  const link = event.notification.data.link
  if (!link) return
  self.clients.openWindow(link)
})

キャッシュをしない設定はある?

ある。

サンプルを参考にゴニョゴニョします。

src/service-worker/sw.ts
import { cleanupOutdatedCaches } from 'workbox-precaching'
import { clientsClaim } from 'workbox-core'
import { NetworkOnly } from 'workbox-strategies'
import { registerRoute, setDefaultHandler } from 'workbox-routing'
import type { ManifestEntry } from 'workbox-build'

declare const self: ServiceWorkerGlobalScope
self.__WB_DISABLE_DEV_LOGS = true

cleanupOutdatedCaches()
// 全てのリクエスト(URL)に対してNetworkOnlyを設定
registerRoute(({ url }) => {
  ;(self.__WB_MANIFEST as Array<ManifestEntry>)
    .map((entry) => new URL(entry.url, self.location.origin).href)
    .includes(url.href)
}, new NetworkOnly())

self.skipWaiting()
clientsClaim()

setDefaultHandler(new NetworkOnly())

動作確認編

スマホ実機で動作確認をする際のハマりポイントを紹介します。

ベーシック認証をかけているとiOS Safariでアイコンが出ないし動作確認もできない

ベーシック認証なし ベーシック認証あり
myvketdev.png myvketstg.png

ベーシック認証ありだと起動後も真っ白。

設定はしているはずなのに検証環境(ベーシック認証あり)でアイコンが出ず、そこそこの時間悩んでしまいました。

厄介だったのが、Android Chromeではベーシック認証ありでもアイコンも内容表示もできていたことです。
Safari~~~~😇

iOS SafariのNotification APIはPWAインストール後にしか存在しない

通知許可をブラウザに求めるためNotification.requestPermission()を実行するのですが、iOS SafariではPWAインストール後でないとNotification APIが存在しません。

そのため、iOSであるかどうか + PWAで開いているかどうかのチェックを行い、iOSではPWAのときだけrequestPermission()を実行します。

if (!isIOS() || (isIOS() && isPwa())) {
    Notification.requestPermission((_permission) => {
        if (_permission === 'granted') {
            // サーバーサイドとの連携処理
        }
    })
}

Safari~~~~😇😇

今後の課題

マルチオリジンのPWA

今回の実装でMy Vketのマイルームをアプリっぽくすることができました。
触ってみるとよくわかるのですが、ブラウザ版と比べてかなり体験が良くなっています。

一方、ワールドへ遷移するとアプリ内ブラウザが立ち上がり、表示領域が狭くなってしまっています。

マイルーム ワールド
image.png image.png

これはマイルームとワールドでoriginが異なることが原因です。
今後、My Vketと関連サービスをアプリ内でシームレスに行き来できるよう、マルチオリジン対応(もしくは同一オリジンにまとめていく対応)を進めていきます。

参考:

キャッシュ

今回の実装ではキャッシュ対応はしませんでした。
適切なキャッシュをすることで3Dの描画速度を上げ、UXの向上を進めます。

まとめ

ブラウザメタバースというWebプッシュ通知の需要が高いサービスにPWAとプッシュ通知を実装しました。
これによりアプリと同等の操作感と通知機能が得られ、より良いサービスになったと思います。

今後も改善を進め、ネイティブアプリに負けないUXを提供できるよう頑張ります。

(ぞい)

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?