WWDC Quick Look 💓 By SwiftGGTeam
Connect Bluetooth devices to Apple Watch

Connect Bluetooth devices to Apple Watch

元の動画を見る

ハイライト

watchOS 8 では Background App Refresh 中に Bluetooth デバイスへ接続できるようになり、complications がアクセサリから継続的にデータを取得して更新でき、ユーザーが App を開く必要がありません。

主要内容

以前の問題: Bluetooth は App フォアグラウンド時のみ

00:31

watchOS 8 以前、App が Bluetooth デバイスに接続できるのは 2 つの状態だけでした。フォアグラウンド実行中、またはバックグラウンドセッション中です。complication に Bluetooth デバイスの最新データを表示したい場合、毎回 App を開いて接続を確立する必要がありました。

多くのシーンでは不便でした。温度センサーなら、手首を上げただけで現在温度が見えてほしい——毎回 App を開くのではなく。

watchOS 8 の解決策: バックグラウンド更新中の Bluetooth 接続

00:57

watchOS 8 では Background App Refresh 中に Bluetooth アクセサリへ接続できます。2 つの中核能力があります。

  1. complications の更新: Bluetooth デバイスからデータを取得し、文字盤の complication を直接更新
  2. ローカル通知の送信: 例: Bluetooth デバイスの電池残量が低いときにユーザーへ通知

この機能を使うには、Info.plist の UIBackgroundModesbluetooth-central を追加するだけです。

接続のライフサイクル

02:04

Bluetooth 接続のライフサイクルはいくつかの段階に分かれます。

初回ペアリング(フォアグラウンド):

  • ユーザーが App を開く
  • App がスキャンして Bluetooth デバイスを発見
  • 初回接続を確立(ペアリングが必要な場合あり)
  • ユーザーが App を終了すると Bluetooth 接続は切断

バックグラウンド更新中(watchOS 8 で新規):

  • システムが Background App Refresh をトリガー
  • App がペア済みデバイスへの接続を要求
  • デバイスが advertisement を送信し、Apple Watch が接続
  • App がデータを取得し、完了後に能動的に切断

02:36

アクセサリ設計のベストプラクティス

02:55

Background App Refresh はいつでも起こり得るため、重要な更新があるときはできるだけ頻繁に advertisement すべきです。省電力のため advertisement が少ないと、バックグラウンド更新中に advertisement を受け取れず接続に失敗することがあります。

アクセサリ側でセンサーデータをバッファする戦略があります。バッファがほぼ満杯になったら advertisement 頻度を上げ、Apple Watch との再接続機会を増やします。

03:37

理想的な RF 条件では、少なくとも 2 秒に 1 回は advertisement することを推奨します。より厳しい RF 環境では、さらに頻繁に advertisement してください。

3 つのユーザーフロー

04:02

セッションでは 3 つの接続フローが示されました。

フロー 1: 正常な接続と切断

  • App がバックグラウンド更新中に接続を開始
  • advertisement を受信し接続を確立
  • データを取得し、cancelPeripheralConnection で能動的に切断
  • 最も理想的なフロー

フロー 2: アクセサリが近くにない、または advertisement していない

  • App が接続を開始するが advertisement を受信しない
  • expiration handler が呼ばれる
  • App が接続要求をキャンセルし、次のバックグラウンド更新に備える
  • 電力節約のため expiration handler より前に能動的に切断することを推奨

フロー 3: バックグラウンド更新終了前に切断できなかった

  • App が接続を確立するが、バックグラウンド更新終了前に切断できない
  • バックグラウンド更新終了時に Core Bluetooth が自動切断
  • 次のバックグラウンド更新で App は didDisconnectPeripheral コールバックを受信
  • App は再接続するか、次の更新を待つ

05:28

詳細

フォアグラウンドでのデバイス発見

07:35

ユーザーがフォアグラウンドで App を使うとき、デバイスを発見して接続します。

func centralManager(_ central: CBCentralManager,
                    didDiscover peripheral: CBPeripheral,
                    advertisementData: [String: Any],
                    rssi RSSI: NSNumber) {
    // 発見デバイスリストに追加してから接続
    central.connect(peripheral, options: nil)
}

キーポイント:

  • didDiscoverPeripheral は advertisement 検出時に呼ばれる
  • 発見したデバイスを配列に保存して後で使う
  • central.connect で接続要求を開始

バックグラウンド更新中の接続

07:55

Extension Delegate のバックグラウンドタスク処理で Bluetooth 接続を開始します。

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
    for task in backgroundTasks {
        if let refreshTask = task as? WKApplicationRefreshBackgroundTask {
            // バックグラウンド作業開始: Bluetooth デバイスに接続
            central.connect(peripheral, options: nil)

            refreshTask.expirationHandler = {
                // バックグラウンド時間が尽きる前に接続をキャンセル
                if let peripheral = self.bluetoothReceiver.connectedPeripheral {
                    self.central.cancelPeripheralConnection(peripheral)
                }
                refreshTask.setTaskCompletedWithSnapshot(false)
            }
        }
    }
}

キーポイント:

  • WKApplicationRefreshBackgroundTask 内で Bluetooth 接続を開始
  • expirationHandler は watchOS 8 の新 API。バックグラウンド時間が尽きる直前に呼ばれる
  • expiration handler 内で接続をキャンセルしタスク完了をマーク
  • 電力節約のため expiration handler より前に能動的に切断する

切断の処理

08:51

Bluetooth デバイスが切断されると、システムが delegate メソッドを呼びます。

func centralManager(_ central: CBCentralManager,
                    didDisconnectPeripheral peripheral: CBPeripheral,
                    error: Error?) {
    connectedPeripheral = nil
    delegate?.didCompleteDisconnection(from: peripheral)
}

キーポイント:

  • didDisconnectPeripheral はフォアグラウンド・バックグラウンド両方で呼ばれる
  • connectedPeripheral 参照をクリア
  • delegate に切断完了を通知

Extension Delegate でバックグラウンドタスクを完了

09:08

func didCompleteDisconnection(from peripheral: CBPeripheral) {
    if let refreshTask = currentRefreshTask {
        refreshTask.setTaskCompletedWithSnapshot(false)
        currentRefreshTask = nil
    }
}

キーポイント:

  • 現在 Background App Refresh 状態であることを確認
  • バックグラウンドタスクを完了としてマーク
  • これで Background App Refresh が終了し、App はサスペンド状態に戻る
  • 二重完了を避けるため currentRefreshTask を nil に

Info.plist の設定

WatchKit Extension の Info.plist に追加します。

<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
</array>

01:35

「Required background modes」および「App communicates using CoreBluetooth」と表示される場合もあります。

プラットフォーム別サポート設定の比較

01:50

プラットフォームごとに BLE のサポート設定は異なります。watchOS ではフォアグラウンド接続とバックグラウンド更新中の接続がサポートされています。バックグラウンドでの新規デバイススキャンは iOS のみで、watchOS ではバックグラウンドスキャンはできません。

初回デバイス設定の要件

09:26

Bluetooth デバイスの初回設定時は App がフォアグラウンドである必要があります。ユーザーが能動的に App を使い、新規デバイスをスキャンして初回接続を確立します。一度発見・ペアリングされれば、その後はバックグラウンド更新中に再接続できます。

App がフォアグラウンドを離れた後は新規デバイスのスキャンはできず、発見済みデバイスへの接続要求のみ可能です。接続してデータ取得後は、電力節約のため能動的に切断すべきです。

重要ポイント

  • Bluetooth 温度計 complication を作る: Bluetooth 温度センサーに接続し、文字盤 complication に現在温度を表示。バックグラウンド更新で 1 時間最大 4 回データ更新。入口 API: CBCentralManager + WKApplicationRefreshBackgroundTask

  • スポーツ GPS トラッカー連携 App を作る: Bluetooth GPS トラッカーに接続し、complication にペースと距離を表示。運動終了後は自動切断して省電力。入口 API: CBPeripheraldidUpdateValueFor + HKWorkoutSession

  • 医療機器データ監視 App を作る: Bluetooth パルスオキシメーターや血圧計に接続し、バックグラウンド更新中にデータを読み取り HealthKit に記録。異常時はローカル通知。入口 API: HKHealthStore.save + UNUserNotificationCenter

  • Bluetooth デバイス電池監視ツールを作る: バックグラウンド接続時に電池残量を読み取り、閾値以下で complication に警告アイコン。入口 API: 電池サービス UUID 0x180FCBCharacteristic

  • スマートホーム時計リモコンを作る: Bluetooth ドアロックや照明コントローラーに接続し、complication にデバイス状態を表示、タップで素早く制御。入口 API: CBPeripheral.writeValue + カスタム GATT サービス。

関連セッション

コメント

GitHub Issues · utterances