WWDC Quick Look 💓 By SwiftGGTeam
What's new in watchOS 26

What's new in watchOS 26

元の動画を見る

ハイライト

watchOS 26 により、Controls、設定可能な widget、RelevanceKit の 3 つが Apple Watch に登場し、Watch app を開発しなくても iPhone 上の機能を手首からトリガーできるようになりました。


主要内容

watchOS には以前からある課題がありました。ユーザーの手首には Smart Stack がありますが、開発者がそこに提示できる内容には限界がありました。Timeline widget は一度に 1 つのタイムライン上のコンテンツしか表示できず、「9 時の瞑想と 10 時の 3 つのビーチアクティビティ」のようなシナリオでは、3 つのイベントが 1 枚のカードに押し込まれて切り捨てられてしまいます。iOS 16 から存在する Controls というショートカット操作機能は、Apple Watch ではずっと利用できませんでした。その結果、開発者は独立した Watch app を開発するコストを払うか、手首というプレースメントを諦めるかの選択を迫られていました。

watchOS 26 はこの課題に 3 つのレイヤーで応えます。第 1 のレイヤーは、iOS 上の Controls をそのまま Apple Watch に持ってくることです。ユーザーは iPhone app の control を Watch の Control Center、Smart Stack、または Action Button に配置でき、タップするだけでアクションが iPhone 上で実行されます。Watch 側のコードを追加で書く必要はありません(講演者の Anne が 04:44 で明言しています)。第 2 のレイヤーは、widget と control の両方が Watch 上で設定可能になり、iOS と同じ AppIntent フローを使用するようになったことです。第 3 のレイヤーは、新しいフレームワークである RelevanceKit と Relevant Widget です。「ビーチのような POI に存在するとき」「あるイベントの時刻付近」のように widget が関連する条件を宣言すると、システムが Smart Stack に複数のカードを同時に提案し、各カードが各イベントに対応するようになります。これにより Timeline widget の単一タイムライン制限を完全に回避できます。

さらに、下層にはもう一つ重要な変更があります。Apple Watch Series 9 と Ultra 2 が watchOS 26 で正式に arm64 に移行しました。Xcode 14 からすでにサポートされており、シミュレーターも常に arm64 だったため、ほとんどの app は Standard Architectures を使うだけで直接ビルドできます。注意が必要なのは、Float/Int の変換やポインタ演算のような低レベルコードだけです。


詳細

古い Timeline widget を watchOS 26 で設定可能にする(06:53

// In the AppIntentTimelineProvider
func recommendations() -> [AppIntentRecommendation<BeachConfigurationIntent>] {
  if #available(watchOS 26, *) {
    // Return an empty array to allow configuration of the widget in watchOS 12+
    return []
  } else {
    // Return array of recommendations for preconfigured widgets before watchOS 12
    return recommendedBeaches
  }
}

ポイント:

  • recommendations()AppIntentTimelineProvider のメソッドです。watchOS 26 以前は事前設定項目の配列を返す必要があり、ユーザーはパラメータを変更できませんでした。
  • watchOS 26 以降、空の配列 [] を返すことで、システムに「この widget はユーザーが文字盤や Smart Stack で自分で設定するもの」と伝えます。その後、ユーザーが widget を編集すると AppIntent 設定ページが表示されます。
  • if #available(watchOS 26, *) 分岐を追加することで、同じコードで旧システムと互換性を保ち、古いユーザーが設定できない widget を見ないようにできます。

Control も設定に対応させる(07:46

struct ConfigurableMeditationControl: ControlWidget {
  var body: some ControlWidgetConfiguration {
    AppIntentControlConfiguration(
      kind: WidgetKinds.configurableMeditationControl,
      provider: Provider()
    ) { value in
      // Provide the control's content
    }
    .displayName("Ocean Meditation")
    .description("Meditation with optional ocean sounds.")
    .promptsForUserConfiguration()
  }
}

ポイント:

  • AppIntentControlConfiguration は iOS 上と完全に同じ名前で、Watch 側で API を変える必要はありません。
  • providerAppIntentControlValueProvider に準拠する必要があり、previewValue(configuration:)currentValue(configuration:) を提供します。前者は「control を追加」パネル上のプレビュー用、後者は実際の実行時の値取得用です。
  • .promptsForUserConfiguration() を使うと、ユーザーが初めてこの control を追加するときに設定 UI が表示されます。例えば「瞑想時に波の音を再生するかどうか」をユーザーに選択させることができます。

RelevanceKit で widget に POI カテゴリをタグ付けする(10:53

func relevance() async -> WidgetRelevance<Void> {
  guard let context = RelevantContext.location(category: .beach) else {
    return WidgetRelevance<Void>([])
  }
  return WidgetRelevance([WidgetRelevanceAttribute(context: context)])
}

ポイント:

  • RelevantContext.location(category:) は MapKit の POI カテゴリを受け取ります。サポートされていないカテゴリを渡すと nil が返されるので、先に guard でチェックする必要があります。
  • 返された WidgetRelevanceWidgetRelevanceAttribute(context:) を含めることで、システムに「ユーザーがこの種類の場所にいるとき、この widget を Smart Stack の一番上に推してください」と伝えます。
  • カテゴリには grocery store、cafe、beach などが含まれており、自分で GeoFence を書く必要はありません。

Relevant Widget:Smart Stack に複数のカードを一度に提供する(14:37

struct BeachEventRelevanceProvider: RelevanceEntriesProvider {
  let store: BeachEventStore

  func relevance() async -> WidgetRelevance<BeachEventConfigurationIntent> {
    // Associate configuration intents with RelevantContexts
    let attributes = events.map { event in
      WidgetRelevanceAttribute(
        configuration: BeachEventConfigurationIntent(event: event),
        context: .date(interval: event.date, kind: .default)
      )
    }

    return WidgetRelevance(attributes)
  }
}

ポイント:

  • RelevanceEntriesProviderAppIntentTimelineProvider に代わる新しいプロトコルで、Relevant Widget 専用です。
  • relevance() は各 BeachEventConfigurationIntent 設定と、それに対応する RelevantContext(ここではイベント発生の時間区间)を WidgetRelevanceAttribute としてパッケージ化します。
  • 同時に複数の attribute がヒットすると、システムは Smart Stack に同時に複数のカードを提案します。イベントごとに 1 枚のカードになり、Timeline widget の単一カード切り捨て問題を回避します(12:48 の 3 イベントが 1 枚のカードに押し込まれる比較デモを参照)。

RelevanceConfiguration で widget を組み立て、重複表示を回避する(17:31

struct BeachEventWidget: Widget {
  private let model = BeachEventStore.shared

  var body: some WidgetConfiguration {
    RelevanceConfiguration
      kind: "BeachWidget
      provider: BeachEventRelevanceProvider(store: model)
    ) { entry in
      BeachWidgetView(entry: entry)
    }
    .configurationDisplayName("Beach Events")
    .description("Events at the beach")
    .associatedKind(WidgetKinds.beachEventsTimeline)
  }
}

ポイント:

  • RelevanceConfiguration は新しい widget 設定タイプで、AppIntentConfiguration と同等の位置づけですが、RelevanceEntriesProvider によって駆動される点が異なります。
  • クロージャ内で entry を取得した後、SwiftUI ビューをレンダリングします。Timeline widget の書き方と同じです。
  • .associatedKind(WidgetKinds.beachEventsTimeline) が重要です。ユーザーがすでに同名の Timeline widget を Smart Stack に追加している場合、relevant widget が提案されたときにシステムはrelevant カードで timeline カードを置換し、1 つのイベントに対して 2 枚のカードが表示されるのを防ぎます。

重要ポイント

1. iPhone Control を優先的に Apple Watch に公開する: watchOS 26 以降、iPhone app の control はデフォルトでユーザーが Watch の Control Center、Smart Stack、Action Button に追加でき、Watch app なしでリモートトリガーできます。なぜやる価値があるか:投資はほぼゼロ、カバー範囲は広い——control の action が iPhone app を強制的にフォアグラウンドにしない限り(05:04)、自動的に表示されます。どう始めるか:既存の iOS controls を監査し、フォアグラウンド app のものを分離するか、バックグラウンド AppIntent に書き直します。

2. widget を watchOS 26 で設定可能にする: if #available(watchOS 26, *) 分岐を使い、recommendations() が空の配列を返すようにすると、ユーザーによるパラメータカスタマイズが可能になります。なぜやる価値があるか:Smart Stack の利用可能スロット数は限られているため、設定可能な widget により、ユーザーは 1 つのスロットを確保しながら複数のコンテンツ(例えば異なる場所の天気)をカバーできます。どう始めるか:既存の AppIntentTimelineProvider を見つけ、recommendations()#available 分岐を追加するだけです。

3. RelevantContext で精准な提案をする: widget の関連性を POI カテゴリ、時間区间、フィットネス/睡眠状態に紐付け、適切なタイミングで Smart Stack のトップにプッシュします。なぜやる価値があるか:ユーザーは widget の順序を能動的に調整する必要がなく、関連するコンテンツが自動的に浮かび上がるため、リテンションとアクティブ度が向上します。どう始めるか:relevance() メソッドで WidgetRelevanceAttribute の配列を返し、まずは RelevantContext.location(category:) から試してみます。

4. 複数イベントシナリオを Relevant Widget に改造する: カレンダー、注文、行程のような「同時点で競合する可能性がある」コンテンツは、RelevanceConfiguration + RelevanceEntriesProvider で Timeline widget に代えます。なぜやる価値があるか:Smart Stack は複数のカードを同時に提案し、イベントごとに 1 枚のカードになり、単一カードの切り捨てを回避します。どう始めるか:まず RelevanceEntriesProvider を書き、relevance() で各イベントに WidgetRelevanceAttribute を割り当て、.associatedKind(_) で古い Timeline widget に関連付けて重複を防ぎます。

5. ClockKit complication の最後の移行ウィンドウ: watchOS 26 の widget push updates は APNs 経由でプッシュされます。「complication だけが push 可能で widget は不可」という理由で ClockKit にとどまっていた app は、安心して移行できます(19:14 と「Go further with Complications in WidgetKit」を参照)。なぜやる価値があるか:ClockKit は永遠に存在するわけではなく、早めに移行するほど楽になります。どう始めるか:WidgetKit で複雑な機能ページを書き直し、APNs widget push チャネルと接続します。


関連セッション

  • Meet Liquid Glass — watchOS 26 全 app の新しいデザイン言語。toolbar と control スタイルの自動移行。
  • What’s new in widgets — widget push updates、Live Activities などクロスプラットフォーム widget 更新の総合エントリーポイント。
  • Go further with MapKit — watchOS 26 上の MapKit 経路とローカル検索の機能はここから提供されます。
  • Create icons with Icon Composer — Icon Composer を使って iOS/watchOS 両方の新しいアイコンを一度に生成。

コメント

GitHub Issues · utterances