ハイライト
watchOS 8 では Always-On Display が文字盤からサードパーティ App まで拡張されました。
isLuminanceReduced環境値とTimelineViewと組み合わせることで、手首を下げた状態でも表示を保ちながら、更新頻度を制御しプライバシーを守れます。
主要内容
Apple Watch App を開発したとします。ユーザーが手首を上げれば、すべて正常に動きます。しかし手首を下げると、画面が真っ暗になるか、ぼやけた UI が表示される——これが watchOS 7 の挙動でした。
watchOS 8 がこれを変えます。
watchOS 8 から、サードパーティ App も Always-On 状態で表示を維持できます。Apple Watch Series 5 以降が対応しています。手首を上げなくても、ちらっと App の内容を確認できます。
ここで 2 つの問題がすぐに浮かびます。
1 つ目、低輝度で UI をどう設計するか。
2 つ目、手首を下げたときにコンテンツをどう更新するか。
Apple は API の一式で両方に答えています。
詳細
isLuminanceReduced: Always-On 状態の検知
(02:40)
watchOS 8 では新しい SwiftUI 環境プロパティが導入されました。
@Environment(\.isLuminanceReduced) var isLuminanceReduced
時計が Always-On 状態に入ると、システムは画面輝度を自動的に下げます。isLuminanceReduced が true になり、App はそれに応じて UI を調整できます。
Xcode Preview でもこの状態をシミュレートできます。
ContentView().environment(\.isLuminanceReduced, true)
(03:00)
キーポイント:
@Environmentはシステム環境から状態を読み取る- 手首を下げて低輝度のとき
isLuminanceReducedはtrue - Preview とシミュレーターでシミュレート可能。シミュレーターウィンドウに wrist-down 用のボタンがある
セッションでは 3 つの実 App のデザイン例が示されました。
Fitbod は界面構造の一貫性を保ち、副次テキストだけをさらに暗くします。
AnyList はリスト項目の背景色を外し、「ギリシャヨーグルトの味」「牛乳の数量」などの副次情報を暗くし、主タイトルを際立たせます。
Pandora はアルバムアートを外して暗いアウトラインに替え、下部コントロールバーも暗くし、曲名とアーティスト名だけをはっきり表示します。
(03:50)
機密情報も扱う必要があります。銀行残高や個人の健康データは、手首を下げたときにぼかすか非表示にすべきです。SwiftUI の .redacted(reason:) modifier は widget と同じモデルを共有します。
アニメーションは最初のフレームか静止状態にリセットし、途中で止めないでください。
TimelineView: 手首を下げたときの更新リズム
(06:59)
アクティブなセッションがある App(workout やオーディオ再生など)は、手首を下げたとき UI を最大 1 秒に 1 回まで更新できます。アクティブセッションがない App は最大 1 分に 1 回です。
SwiftUI の TimelineView がこの定時更新を担当します。schedule を受け取り、時間に沿って View を構築します。
watchOS 8 には 5 種類の組み込み schedule があります。
EveryMinute: 毎分更新。システム時計に合わせ、ちょうど分のタイミングで発火Periodic: カスタム間隔(例: 2 分ごと)。システム時計には合わせないExplicit: 指定した時刻に更新。精密なタイミング向けCustom: 混合戦略(例: 59 分は毎分、最後 60 秒は毎秒)Animation: アニメーション専用
(07:18)
典型的な TimelineView の使い方:
TimelineView(.everyMinute) { context in
Text(context.date, style: .time)
}
キーポイント:
TimelineViewのクロージャは現在日時を含むTimelineViewContextを受け取る- 非アクティブセッションで 1 分より速い更新を要求しても試行はされるが保証されない
- デフォルトでは 2 分後に文字盤に戻るが、ユーザーは設定で変更可能。App は長時間表示に備えるべき
HealthKit バックグラウンド配信
(08:58)
watchOS 8 では iOS にあった HealthKit バックグラウンド配信が Watch App にも来ました。設定は iOS と同じです。
let query = HKObserverQuery(
sampleType: heartRateType,
predicate: nil
) { query, completionHandler, error in
fetchLatestHeartRateData()
completionHandler()
}
healthStore.execute(query)
healthStore.enableBackgroundDelivery(
for: heartRateType,
frequency: .immediate
) { success, error in
// バックグラウンド配信を有効化
}
(09:13)
キーポイント:
HKObserverQueryはデータ変化を監視するenableBackgroundDeliveryでバックグラウンド起動を有効化- 通常の App は最大 1 時間に 1 回まで起動
- アクティブな文字盤に complications があれば最大 1 時間に 4 回
- すべての起動回数はバックグラウンド更新予算にカウントされ、上限は 1 時間 4 回
- 転倒、低酸素、心拍異常など重要なデータ型は即時配信
- その他のデータ型は 1 時間以上の間隔で配信
HealthKit 認可フォームも更新され、App が Apple Watch でより頻繁に健康データにアクセスすることをユーザーに伝えます。
Bluetooth デバイスのバックグラウンド接続
(10:49)
watchOS 4 から Bluetooth デバイスの Apple Watch 直結が可能でした。watchOS 8 の新機能は、バックグラウンド更新中の接続です。
complications が Bluetooth アクセサリから継続的にデータを取得できます。例: Qardio の医療用 ECG、Sonra Watch のサッカー選手向け GPS トラッカー。
バックグラウンド更新は 1 時間最大 4 回の接続機会を提供し、これもバックグラウンド更新予算にカウントされます。
Apple は WKRefreshBackgroundTask に expiration handler を追加しました。
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
task.expirationHandler = {
// 時間切れ前に接続をクリーンアップ
}
// Bluetooth デバイスに接続してデータ取得
}
}
(12:35)
キーポイント:
- Bluetooth の初回接続は App がフォアグラウンドである必要がある。バックグラウンドでは新規接続不可
- 接続確立後は、バックグラウンド更新中に再接続してデータ取得可能
- データ処理は短時間で完了する必要がある
expirationHandlerで時間切れ前にきれいに終了できる
位置情報ベースのリージョン通知
(13:15)
watchOS 8 は UNLocationNotificationTrigger をサポートし、指定リージョンへの入退場時に通知できます。
let center = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)
let region = CLCircularRegion(
center: center,
radius: 200,
identifier: "gym"
)
region.notifyOnEntry = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
let content = UNMutableNotificationContent()
content.title = "ジムに到着"
content.body = "トレーニングの記録を始めましょう"
let request = UNNotificationRequest(
identifier: "gym-entry",
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
(13:40)
キーポイント:
CLCircularRegionで円形ジオフェンスを定義UNLocationNotificationTriggerはリージョンに基づいて通知を発火- 「使用中」の位置情報許可が必要
- 最初は静的な通知。タップ後に動的コンテンツを表示しプライバシーを保護
- リージョン半径は数百メートル程度を推奨。ユーザーが明確に関心を持つ場所だけ設定
Core Location の location button も watchOS に登場。ボタンタップで一度限りの位置許可が得られ、毎回ダイアログは出ません。
その他のシステム強化
(15:18)
watchOS 8 には注目すべき改善もあります。
- 睡眠時の呼吸数: Apple Watch が睡眠中の呼吸数を測定。App は HealthKit 経由で読み取り可能
- AssistiveTouch: ジェスチャー認識で上肢に差のあるユーザーが操作しやすく
- 大きなアクセシビリティ文字: より大きなアクセシビリティ文字サイズ
- 大タイトル: スクロール View 上部で iOS 風の大タイトルをサポート
- テキスト入力の改善: App ごとの Scribble/音声入力の好みを記憶し、素早く切り替え
- 検索 API:
searchablemodifier でカスタム検索候補をサポート - リストのスワイプ操作: リスト項目にカスタムスワイプ操作(お気に入りなど)
- Button roles: ボタン種別(destructive など)の区別、prominence と追加の触覚フィードバック
- Canvas API: SwiftUI Canvas が watchOS に。GPU によるプログラム描画をサポート
重要ポイント
-
手首を下げても見えるタイマー App を作る。
TimelineView+EveryMinuteschedule で、料理や運動中に手首を下げても残り時間を表示。最後 60 秒は毎秒更新に切り替え。入口 API:TimelineView+Customschedule。 -
Bluetooth 血糖計の complication 連携を作る。 バックグラウンド更新中に血糖計に接続し、1 時間に 1 回 complication を更新。
expirationHandlerで接続を確実に閉じる。入口 API:CBCentralManager+WKRefreshBackgroundTask。 -
位置情報ベースのフィットネスリマインダー App を作る。 ジム到着でトレーニング記録開始、退出で保存を促す。
UNLocationNotificationTriggerで 200 m のジオフェンス。入口 API:CLCircularRegion+UNLocationNotificationTrigger。 -
睡眠呼吸モニタリング dashboard を作る。 watchOS 8 の睡眠呼吸数データで、毎朝の推移を表示。HealthKit バックグラウンド配信と組み合わせ、手動更新不要。入口 API:
HKQuantityType(.respiratoryRate)+HKObserverQuery。 -
Always-On 向けの音楽再生 UI を作る。 手首を下げたらアルバムアートと再生コントロールを隠し、曲名とアーティスト名だけ表示。
isLuminanceReducedで状態ごとの UI 階層を制御。入口 API:@Environment(\.isLuminanceReduced)+.redacted(reason:)。
関連セッション
- Build a workout app for Apple Watch — SwiftUI と HealthKit で workout app をゼロから構築。TimelineView と Always-On の実装コードを含む
- Connect Bluetooth devices to Apple Watch — Bluetooth バックグラウンド接続の完全実装を深掘り
- There and back again: Data transfer on Apple Watch — Watch Connectivity と URLSession を含む Apple Watch データ通信戦略
- What’s new in SwiftUI — TimelineView のさらなる使い方を含む SwiftUI の更新
コメント
GitHub Issues · utterances