WWDC Quick Look 💓 By SwiftGGTeam
What's new in web apps

What's new in web apps

元の動画を見る

ハイライト

macOS Sonoma では任意の Web サイトを Dock に独立 Web App として追加でき、専用ウィンドウ、Cookie、システムレベルの権限管理を持ちます。Web App Manifest でツールバー、リンク遷移範囲、display モードを制御でき、Web Push 通知と Badge は macOS と iOS/iPadOS のホーム画面 Web App の両方で利用できます。

主要内容

macOS 上の Web App

macOS Sonoma で Web App 機能が追加されました。Safari で任意のサイトを閲覧中に File >「Dock に追加…」を選ぶと、Dock に独立アプリとして表示されます。追加時に Safari の Cookie がコピーされるため、通常はログイン状態が維持されます。

Web App には簡素化されたツールバーがあり、サイトのテーマカラーがツールバー背景に溶け込みます。Stage Manager、Mission Control、Command+Tab 切り替えなどネイティブ macOS アプリと同様に動作し、Dock、Launchpad、Spotlight から起動できます。

権限管理もネイティブアプリと同様です。カメラ、マイク、位置情報はシステム権限ダイアログで要求され、システム設定 > プライバシーとセキュリティで管理します。

00:51

Web App Manifest によるカスタマイズ

Web App Manifest で挙動を制御します。HTML の head に追加:

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

主要フィールド:

  • name:Web App 名(ページタイトルを置換)
  • display:表示モード、standalone はツールバーなし
  • start_url:初回起動時に読み込む URL
  • scope:Web App 内で開くリンクの範囲
  • id:同一ドメイン内の Web App を区別する一意 ID

05:01

display: standalone の効果はプラットフォームで異なります:

  • macOS:ツールバー非表示
  • iOS/iPadOS:ホーム画面 Web App として独立 Cookie とストレージ、ブラウザと分離

iOS で Web Push と Badge を使うには standalone モードが必須です。

リンク遷移と Scope

Web App には関連 scope があります。scope 内リンクは Web App 内、外はデフォルトブラウザで開きます。デフォルト scope は Web App 作成時のページの host です。

manifest の scope でさらに限定可能。例: example.com/browserpets にすると example.com/webkittens はデフォルトブラウザで開きます。

iOS のホーム画面 Web App では scope 外リンクは Safari View Controller で開きます。

05:53

Web App 作成時に Safari の Cookie がコピーされるため、通常はログイン状態が維持されます。local storage はコピーされません。認証状態が Cookie と local storage に分散している場合、再ログインが必要です。

認証状態は Cookie に保存することを推奨します。OAuth フローは通常 Web App 内で開きます(ヒューリスティック)。問題があれば Apple に報告してください。window.open で開くリンクは常に Web App 内で、scope 制限を受けません。

メールの自動ログインリンクはデフォルトブラウザで開き、既存 Web App には自動ログインしません。メールにワンタイムコードを提供する代替も検討できます。Passkey 認証がより推奨されます。

07:37

Web Push 通知と Badge

macOS の Web App は標準 Web Push をサポートします。Web 標準に沿って Web Push を実装済みなら、macOS Web App で追加作業なしに利用できます。

通知には Safari ではなく Web App のアイコンが表示され、正しいコンテキストをユーザーに示します。

サウンドはプラットフォーム慣例に従い、iOS/iPadOS ではデフォルト ON、macOS ではデフォルト OFF。Notification API の silent で上書き:

new Notification("New message", { silent: false });

Badge は macOS Web App と iOS ホーム画面 Web App の両方でサポート。通知許可時に Badge 権限も付与されます。Web App 起動中およびバックグラウンド push 処理中に更新可能です。

10:00

フォーカスモード統合

Web App 通知はフォーカスモードと統合されます。ユーザーはフォーカスモードごとに異なる通知設定ができ、デバイス間で同期されます。

manifest の id で同一ドメイン内の異なる Web App を区別。shop と forums なら id: "shop"id: "forums" で通知設定を分けられます。

フォーカス設定は name + id で同期。同一サイトの複数インスタンス(「Forums」と「Forums - Work」など)を作成し、通知とフォーカス設定を個別にできます。

12:54

新しい Web API

Safari 17 で追加された API:

  • User Activation API:ユーザーが一時的または持続的なアクティベーションを行ったかをサイトに通知(通知権限要求前など)
  • Fullscreen API:Safari 16.4 で macOS/iPadOS のプレフィックスを削除、標準 Fullscreen API をサポート
  • Screen Orientation APItypeangle プロパティと onchange イベントの初期サポート

14:29

詳細

Web App Manifest 完全例

{
  "name": "Browser Pets",
  "short_name": "Pets",
  "description": "A virtual pet community",
  "start_url": "/browserpets",
  "scope": "/browserpets",
  "display": "standalone",
  "theme_color": "#FF6B6B",
  "background_color": "#FFFFFF",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192" },
    { "src": "/icon-512.png", "sizes": "512x512" }
  ],
  "id": "browserpets"
}

キーポイント:

  • name は Dock/ホーム画面表示、short_name はスペース不足時
  • start_url は初回起動ページ
  • scope は Web App 内リンク範囲、manifest URL のサブディレクトリである必要あり
  • display: standalone は macOS でツールバー非表示、iOS で独立ホーム画面 Web App
  • theme_color は macOS ツールバー背景に影響
  • id はフォーカスモード同期用に同一ドメイン内 Web App を区別

HTML で Manifest を参照

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="theme-color" content="#FF6B6B">
  <title>Browser Pets - Your Virtual Pet Community</title>
  <link rel="manifest" href="/manifest.json">
  <link rel="apple-touch-icon" href="/icon-192.png">
</head>
<body>
  <!-- ページ内容 -->
</body>
</html>

キーポイント:

  • theme-color meta と manifest の theme_color を一致させる
  • apple-touch-icon は iOS ホーム画面アイコン
  • manifest link は head 内に配置

Web Push と Service Worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(registration => console.log('SW registered'))
    .catch(err => console.error('SW registration failed', err));
}

async function subscribeToPush() {
  const registration = await navigator.serviceWorker.ready;
  
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(
      'BEl62i...'
    )
  });
  
  await fetch('/api/push-subscription', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(subscription)
  });
}

キーポイント:

  • Web Push には Service Worker が必要
  • pushManager.subscribe はユーザーアクティベーション(クリック等)が必要
  • applicationServerKey はサーバーの VAPID 公開鍵
  • userVisibleOnly: true は各 push で通知を表示

Service Worker で push 処理:

// sw.js
self.addEventListener('push', event => {
  const data = event.data?.json() ?? {};
  
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icon-192.png',
      badge: '/badge-72.png',
      silent: false,
      data: { url: data.url }
    })
  );
});

self.addEventListener('notificationclick', event => {
  event.notification.close();
  event.waitUntil(
    clients.openWindow(event.notification.data.url)
  );
});

キーポイント:

  • showNotification で Service Worker からシステム通知を表示
  • silent: false で macOS デフォルト静音を上書き
  • badge は通知エリアと Dock バッジ用の小アイコン
  • notificationclick で通知タップ時の挙動を処理

Badge API の利用

navigator.setAppBadge(42);
navigator.clearAppBadge();

self.addEventListener('push', event => {
  event.waitUntil(
    getUnreadCount().then(count => {
      return self.navigator.setAppBadge(count);
    })
  );
});

キーポイント:

  • navigator.setAppBadge() で Dock(macOS)またはホーム画面アイコン(iOS)のバッジを設定
  • 通知許可時に Badge 権限も自動付与
  • Service Worker では self.navigator.setAppBadge() で更新
  • フォアグラウンド・バックグラウンド両方で更新可能

認証状態管理

// 推奨: 認証状態を Cookie に保存
// Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax

// 避ける: localStorage に認証状態を分散
// Web App 作成時に localStorage はコピーされない

async function checkAuthState() {
  const sessionCookie = document.cookie.match(/session=([^;]+)/);
  const localToken = localStorage.getItem('auth_token');
  
  if (sessionCookie && !localToken) {
    const response = await fetch('/api/refresh-token', {
      credentials: 'include'
    });
    const data = await response.json();
    localStorage.setItem('auth_token', data.token);
  }
}

キーポイント:

  • Web App 作成時は Cookie のみコピー、localStorage は非コピー
  • Cookie のみ認証のサイトは通常ログイン維持
  • 混合認証は初回読み込みで検出し再認証を案内
  • Passkey はメールリンクより安全で便利

重要ポイント

1. Web App Manifest をサイトに追加

  • やること:既存サイトに manifest.json を追加し、namedisplay: standalonetheme_coloricons を設定
  • 価値:macOS ユーザーが Dock に独立アプリとして追加、iOS ユーザーがホーム画面 Web App 体験を得られる
  • 始め方:manifest.json を作成、HTML head に link を追加、Safari の「Dock に追加」「ホーム画面に追加」をテスト

2. Web Push 通知を実装

  • やること:Web App にプッシュ通知(新着メッセージ、Badge 更新)を追加
  • 価値:Web App 未起動でも再エンゲージ、Badge で未読数を表示
  • 始め方:Service Worker 登録、Push API 購読、サーバー側 Web Push 統合、プラットフォーム別 silent 処理

3. 従来認証を Passkey に置き換え

  • やること:パスワードやメールリンクから Passkey ログインへ移行
  • 価値:Web App とブラウザ両方で動作、Cookie コピー制限の影響を受けにくく、メールコードより安全
  • 始め方:Web Authentication API を統合。WWDC22「Meet passkeys」を参照

4. 同一サイトの複数機能を独立 Web App 化

  • やること:店舗とフォーラムなど独立機能ごとに id で区別した Web App を作成
  • 価値:機能ごとに通知とフォーカス設定を分け、ネイティブアプリに近い体験
  • 始め方:機能ごとに独立 manifest、idscope を設定、リンクが正しい Web App で開くことを確認

5. Scope 外リンクを適切に処理

  • やること:scope 境界を明確にし、外部リンク(サードパーティログイン等)が正しく遷移するよう設計
  • 価値:scope 外はデフォルトブラウザで開くため、OAuth 誤判定でログイン不能になる可能性
  • 始め方:外部リンクと OAuth フローをテスト。Web App 内で開く必要がある場合は window.open() を使用例。誤判定 OAuth ドメインは Apple に報告

関連セッション

コメント

GitHub Issues · utterances