WWDC Quick Look 💓 By SwiftGGTeam
Bring your Live Activity to Apple Watch

Bring your Live Activity to Apple Watch

元の動画を見る

ハイライト

iOS 18 と watchOS 11 により、iPhone で作成した Live Activity が Apple Watch の Smart Stack に自動表示されるようになりました。watchOS 版の個別開発も追加 push token の管理も不要で、システムが更新を自動同期します。


主要内容

app はすでに iPhone で Live Activity をサポートしています——Lock Screen、StandBy、Dynamic Island に表示されています。しかしユーザーが手首に着けている Apple Watch はどうでしょうか?それ以前、Live Activity を Watch に表示するには watchOS 版を別途開発し、独立した push token を管理し、2 つの更新ロジックを扱う必要がありました。多くのチームにとって、そのコストは見合いませんでした。

watchOS 11 がこれを変えます。アップグレード後、既存の iOS Live Activity が Apple Watch の Smart Stack に自動表示されます。システムは Dynamic Island の compact leading と compact trailing ビューを使って表示し、上に app 名を表示します。更新も Watch へ自動同期されます——追加 push token も新しいコードも不要です。

ただしこれは出発点に過ぎません。compact ビューは iPhone の Dynamic Island 向けに設計されており、そのまま Watch に持っていくと文字が切れたり情報密度が不足したりします。Apple は supplementalActivityFamilies modifier を提供し、Smart Stack 専用のレイアウトを用意できます。activityFamily 環境値で iPhone Lock Screen(.medium)と Apple Watch Smart Stack(.small)を区別し、それぞれに最適な UI を提供します。

詳細

自動表示の仕組み

watchOS 11 にアップグレードすると、iOS Live Activity の compact leading と compact trailing ビューが Smart Stack に自動表示されます(01:35)。iOS 側で alerting update を受信したとき、ユーザーが Watch フェイスを見ていると、システムは Smart Stack を自動展開して更新を表示します(02:19)。app がフォアグラウンドの場合、画面下部に compact ビューを含む banner が表示されます(02:34)。Live Activity をタップすると全画面表示に入り、デフォルトで「iPhone で開く」オプションが提供されます。

Smart Stack カスタムビューの追加

まず ActivityConfigurationsupplementalActivityFamilies modifier を追加します(04:15):

struct DeliveryLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(
            for: DeliveryActivityAttributes.self
        ) { context in
            DeliveryActivityContent(context: context)
        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    DeliveryExpandedLeadingView(context: context)
                }
                DynamicIslandExpandedRegion(.trailing) {
                    DeliveryExpandedTrailingView(context: context)
                }
                DynamicIslandExpandedRegion(.bottom) {
                    DeliveryExpandedBottomView(context: context)
                }
            } compactLeading: {
                DeliveryCompactLeading(context: context)
            } compactTrailing: {
                DeliveryCompactTrailing(context: context)
            } minimal: {
                DeliveryMinimal(context: context)
            }
        }
        .supplementalActivityFamilies([.small])
    }
}

キーポイント:

  • .supplementalActivityFamilies([.small]) で Smart Stack 用カスタムビューを提供することを宣言します
  • 追加後、Smart Stack は compact ビューを使わず、ActivityConfiguration の content closure 内のビューを表示します
  • この modifier がない場合、Smart Stack はデフォルトで compact leading + compact trailing を表示します

次に、content ビューで activityFamily 環境値を使ってレイアウトを分岐します(04:49):

struct DeliveryActivityContent: View {
    @Environment(\.activityFamily) var activityFamily
    var context: ActivityViewContext<DeliveryActivityAttributes>

    var body: some View {
        switch activityFamily {
        case .small:
            DeliverySmallContent(context: context)
        case .medium:
            DeliveryMediumContent(context: context)
        @unknown default:
            DeliveryMediumContent(context: context)
        }
    }
}

キーポイント:

  • .small は Apple Watch Smart Stack に対応します
  • .medium は iPhone Lock Screen に対応します
  • @unknown default は将来追加される可能性のある family タイプを処理します

Smart Stack から Watch App を起動

Watch App がある場合、Live Activity タップ時に iPhone ではなく Watch 側 app を直接開くよう設定できます。Watch app target の Build Settings で Info.plist セクションを見つけ、Supports Launch for Live Activity Attribute Types キーを追加します(05:35)。値が空の場合はすべての Live Activity に適用され、具体的な ActivityAttributes 型名を指定することもできます。

Always On Display への対応

手首を下げると、システムは自動的にダーク配色に切り替え、輝度を下げます。isLuminanceReduced 環境値で明るい要素を調整します(08:37):

struct DeliveryGauge: View {
    @Environment(\.isLuminanceReduced) private var isLuminanceReduced
    var context: ActivityViewContext<DeliveryActivityAttributes>

    var body: some View {
        Gauge(value: context.state.progressPercent) {
            GaugeLabel(context: context)
        }
        .tint(isLuminanceReduced ? .gaugeDim : .gauge)
    }
}

キーポイント:

  • isLuminanceReducedtrue のとき、手首が下がり Always On モードが有効です
  • このとき明るい要素の輝度を下げるか削除し、可読性を保ちます
  • セマンティックカラー(.primary など)は現在の配色に自動適応します

Live Activity にライト外観が必要な場合、.small 分岐で .preferredColorScheme(.light) を設定します(08:57)。Always On モードではシステムが自動的にダークに切り替え、輝度を下げます。

更新頻度とネットワーク

更新同期は自動で、追加 push token は不要です(06:51)。ただし同期頻度には予算制限があり、iOS と同様のしきい値があります。現在の push 更新が iOS 予算内であれば、Apple Watch 側も追加調整は不要です。

ネットワークが制限されている場合、システムは Start、End、alerting update を優先します(08:01)。Smart Stack は「最終接続」時刻を表示し、情報が最新でない可能性を示します。手首を上げると、Live Activity は利用可能な最新データに更新されます。


重要ポイント

  • 何を作るか:既存 compact ビューの情報密度を確認します。なぜ価値があるか:compact ビューはゼロコストで Watch に表示されます。静的アイコンや app 名だけだと、Watch 上では無意味な情報になります。どう始めるか:Live Activity を一通り動かし、compact leading/trailing が動的に変化する重要データを表示しているか確認します。

  • 何を作るかsupplementalActivityFamilies で Smart Stack 用カスタムビューを提供します。なぜ価値があるか:compact ビューは Dynamic Island 向けで、Watch ではスペースと操作パターンが異なります。カスタムビューでより多くの情報とより良い体験を提供できます。どう始めるかActivityConfiguration.supplementalActivityFamilies([.small]) を追加し、@Environment(\.activityFamily) でレイアウトを分岐。.small 向けの簡略版から始めます。

  • 何を作るか:Watch App の直接起動を設定します。なぜ価値があるか:Watch 上の Live Activity タップ時、デフォルトは iPhone app にジャンプし、Watch 側の利用ループが途切れます。どう始めるか:Watch app target の Info.plist に Supports Launch for Live Activity Attribute Types を追加。値を空にすれば全タイプに適用されます。

  • 何を作るか:Always On Display に対応します。なぜ価値があるか:手首を下げるとダークモードに切り替わり輝度が下がるため、明るい要素は眩しく、焼き付きの原因にもなります。どう始めるか@Environment(\.isLuminanceReduced) で状態を検出し、Gauge やプログレスバーなどの輝度を下げます。


関連セッション

コメント

GitHub Issues · utterances