💎

Rails8 PWAを構築 + オフライン時のハンドリング

に公開

1.はじめに

スマートフォンやタブレットが普及し、Webアプリケーションもより"アプリっぽい"体験が求められるようになっています。そこで注目されているのが「PWA(Progressive Web App)」です。PWAはWeb技術だけで、ネイティブアプリのような機能やユーザー体験を実現できます。

本記事では、RailsでPWAを構築する方法をステップバイステップで解説します。特にホーム画面追加とオフライン対応に焦点を当てます。

2.PWAとは

PWA(Progressive Web App)とは、Webサイトをアプリのように使えるようにする技術群のことです。主な特徴は次のとおりです:

  • ホーム画面に追加できる
  • オフラインでも使える
  • プッシュ通知が使える
  • 高速でスムーズな操作性

これらを実現するために、PWAではいくつかの技術を組み合わせます。その中でも重要なのが、Service Worker と Web App Manifest です。

3.用語解説

Service Worker

  • ブラウザの裏で動作するJavaScript。
  • キャッシュ制御やオフライン対応、通知受信などを担う。

Web App Manifest

  • アプリ名・アイコン・起動URLなどを定義するJSONファイル。
  • ホーム追加や見た目設定に使われる。

4.Rails8でのPWA実装手順

こちらは、既存のrails new で作成されるデフォルトファイルを参考に作っていきます。
必要なファイルは下記です

  • 新規作成(といっても、rails new 時にすでに作成されています)
    • manufest.json
    • service-worer.js
  • コード追加
    • routes.rb
    • javascript/application.js
    • views/layouts/application.html.erb

manufest.json

Webアプリの名前・アイコン・起動方法などをブラウザに伝えるための設定ファイルです。
nameにはrails new 時のアプリ名が入ります
今回は rails new PwaApp としたので、PwaAppが名前に入っています

app/views/pwa/manifest.json.erb

{
  "name": "PwaApp",
  "icons": [
    {
      "src": "/icon.png",
      "type": "image/png",
      "sizes": "512x512"
    },
    {
      "src": "/icon.png",
      "type": "image/png",
      "sizes": "512x512",
      "purpose": "maskable"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "scope": "/",
  "description": "PwaApp.",
  "theme_color": "red",
  "background_color": "red"
}

service-worer.js

app/views/pwa/service-worker.js

self.addEventListener('install', function(event) {
  console.log('Service Worker installing.');
});

self.addEventListener('activate', function(event) {
  console.log('Service Worker activated.');
});

self.addEventListener('fetch', function(event) {
  console.log('Fetching:', event.request.url);
});

後ほどおまけで使用しますが、現在はログを確認するために追加しました。

application.html.erb

下記行を追加(コメントアウトされているので、コメントアウトを外す)

    <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>

これで準備完了です。
それっぽくするために、

bin/rails g conntroller home
bin/rails g scaffold post title:string body:text

で簡単なホーム画面と投稿(post)のCRUD画面を作成しました。

早速サーバーを起動しましょう

URLバーの右側にアプリのインストール画面が現れるので、インストールを押下

アプリのような画面が表示されました

また、アプリのようにアイコンを保存することができ、ランチャーでも起動することができます

5.おまけ オフライン対応

service_wokerの機能を使って、静的ページをブラウザにキャッシングして、オフライン時にオフライン用の画面を表示するようにしてみましょう

オフラインページ(静的ページ)

public/offline.html

<!doctype html>
<html lang="ja">
    <head>
        <title>オフライン</title>
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1, width=device-width">
        <meta name="robots" content="noindex, nofollow">
    </head>

    <body>
        <main>
            <article>
                <p>オフラインページ</p>
            </article>
        </main>
    </body>
</html>

service-worker.jsの追記

public/service-worker.js

// キャッシュ名を定義(バージョン更新時には変更)
const CACHE_NAME = "my-site-cache-v1";

// キャッシュに保存したいファイルのパス一覧
const urlsToCache = ["/","/offline.html"];

// Service Worker のインストール時にキャッシュを事前保存
self.addEventListener("install", event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      // 指定したリソースをキャッシュに追加
      return cache.addAll(urlsToCache);
    })
  );
});

// 各リクエスト時にキャッシュから返すか、ネットワークから取得する
self.addEventListener("fetch", event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      // キャッシュに一致するレスポンスがあれば返す
      // なければ通常通りネットワーク経由で取得
      return response || fetch(event.request).catch(() => {
        // 通信エラー(オフライン等)の場合、ナビゲーション要求なら404ページを返す
        if (event.request.mode === "navigate") {
          return caches.match("/offline.html");
        }
      });
    })
  );
});

サーバー起動時

サーバー停止時

ちなみに、キャッシュストレージに静的ページを登録していない場合は、下記のようなエラーページが表示されます。

6. まとめ

RailsでもPWAは意外と簡単に実装できます。

  • manifest.json と service-worker.js を配置するだけ
  • 静的ファイルを public/ に置くだけでキャッシュ対応可能
  • 最小構成でも「ホーム画面に追加」「オフライン対応」は十分実現できる

今後は Push通知やバックグラウンド同期などもキャッチアップしてみようと思います。

参考記事

https://zenn.dev/minedia/articles/rails8-minimum-pwa
https://blog.codeminer42.com/everything-you-need-to-ace-pwas/

Discussion