mcpField
S
sensorkit

sensorkit

github.com/dpearson2699開発1.2k3.1k 回インストール13 週間前に更新
リポジトリ

sensorkit について

sensorkitは、SensorKitを使うAppleの研究アプリ開発者向けガイドです。センサーごとの承認と設定、記録、遅延を伴うデータ取得を扱います。

主な機能

  1. 01研究承認とセンサーアクセスの設定
  2. 02SRSensorReaderによるデータ記録
  3. 03期間とデバイスを指定したデータ取得
  4. 04デバイス情報とデリゲートコールバックの処理
  5. 05データ保持期間への対応

活用シーン

  1. 01承認済み研究でのセンサー読み取り設定
  2. 02iPhoneまたはApple Watchからのサンプル収集
  3. 03取得遅延を考慮したデータ収集の設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill sensorkit

インストール後、Claude Code で/sensorkitを入力して読み込まれたか確認します。

同梱ファイルevals/evals.jsonreferences/sensorkit-patterns.md

詳細

SensorKit

正確な SRSensor を選択し、個別に利用可能か確認してください。一般的なモーション/アクティビティ機能には CoreMotion を、健康記録やワークアウトには HealthKit を使用します。

Contents

概要と要件

SensorKit を使うと、研究用アプリで iPhone と Apple Watch のセンサーデータを記録、取得できます。このフレームワークには、次の要件があります。

  1. Apple が承認した研究調査 -- researchandcare.org から提案を提出します。
  2. SensorKit エンタイトルメント -- Apple は、承認された研究調査に限り com.apple.developer.sensorkit.reader.allow を付与します。
  3. 手動プロビジョニングプロファイル -- Xcode では、SensorKit 機能を有効にした明示的な App ID が必要です。
  4. ユーザー認証 -- システムが「研究用センサーと使用データ」シートを表示し、ユーザーはセンサーごとに承認します。
  5. 遅延取得 -- 正式なデータ保持期間を考慮して、取得のタイミングを設計します。

アプリは、アクティブなセンサーについて、過去に記録されたデータを最大 7 日分取得できます。

エンタイトルメント

.entitlements ファイルに SensorKit リーダーのエンタイトルメントを追加します。Apple が研究調査用に承認したセンサーのみを記載してください。一般的なエンタイトルメント値は次のとおりです。

<key>com.apple.developer.sensorkit.reader.allow</key>
<array>
    <string>ambient-light-sensor</string>
    <string>motion-accelerometer</string>
    <string>device-usage</string>
    <string>keyboard-metrics</string>
</array>

承認された各センサーの正確なエンタイトルメント文字列と NSSensorKitUsageDetail キーを選ぶ際は、エンタイトルメントと使用詳細のカタログを確認してください。特殊なセンサーについては、個別の SRSensor ページで再確認してください。

手動署名では、Code Signing Entitlements をエンタイトルメントファイルに、Code Signing Identity を Apple Developer に、Code Signing Style を Manual に設定します。また、SensorKit 機能を持つ明示的なプロファイルをプロビジョニングプロファイルに設定してください。

Info.plist の設定

次の 3 つのキーが必要です。


<key>NSSensorKitUsageDescription</key>
<string>This study monitors activity patterns for sleep research.</string>


<key>NSSensorKitPrivacyPolicyURL</key>
<string>https://example.com/privacy-policy</string>


<key>NSSensorKitUsageDetail</key>
<dict>
    <key>SRSensorUsageMotion</key>
    <dict>
        <key>Description</key>
        <string>Measures physical activity levels during the study.</string>
        <key>Required</key>
        <true/>
    </dict>
    <key>SRSensorUsageAmbientLightSensor</key>
    <dict>
        <key>Description</key>
        <string>Records ambient light to assess sleep environment.</string>
    </dict>
</dict>

Required が true で、ユーザーがそのセンサーへのアクセスを拒否した場合、システムは研究調査にそのセンサーが必要であることをユーザーに警告し、考え直す機会を提示します。

要求するセンサーごとに、正確な使用詳細の辞書を使用してください。上記のモーションと環境光の例以外のセンサーを対応付ける際は、エンタイトルメントと使用詳細のカタログを確認してください。

認証

研究調査に必要なセンサーの認証をリクエストします。初回のリクエスト時に、システムが「研究用センサーと使用データ」シートを表示します。

import SensorKit

let reader = SRSensorReader(sensor: .ambientLightSensor)

// Request authorization for multiple sensors at once
SRSensorReader.requestAuthorization(
    sensors: [.ambientLightSensor, .accelerometer, .keyboardMetrics]
) { error in
    if let error {
        print("Authorization request failed: \(error)")
    }
}

初回の確認とデリゲートの変更には、同じステータスハンドラーを使用します。

private func applyAuthorizationStatus(
    _ status: SRAuthorizationStatus,
    to reader: SRSensorReader
) {
    switch status {
    case .authorized:
        reader.startRecording()
    case .denied:
        reader.stopRecording()
        // Direct the user to Settings > Privacy > Research Sensor & Usage Data.
    case .notDetermined:
        break // Request authorization first.
    @unknown default:
        break
    }
}

applyAuthorizationStatus(reader.authorizationStatus, to: reader)

func sensorReader(_ reader: SRSensorReader, didChange authorizationStatus: SRAuthorizationStatus) {
    applyAuthorizationStatus(authorizationStatus, to: reader)
}

利用可能なセンサー

各 SRSensor とそのサンプル型を対応付けるには、センサーカタログを確認してください。研究調査で承認されたセンサーのみをリクエストし、選択したセンサーが利用可能か、使用詳細キーが正しいかを再確認してください。

SRSensorReader

SRSensorReader は、センサーデータにアクセスするための中心的なクラスです。各インスタンスは 1 つのセンサーから読み取ります。

import SensorKit

// Create a reader for one sensor
let lightReader = SRSensorReader(sensor: .ambientLightSensor)
let keyboardReader = SRSensorReader(sensor: .keyboardMetrics)

// Assign delegate to receive callbacks
lightReader.delegate = self
keyboardReader.delegate = self

リーダーは SRSensorReaderDelegate を介して通信します。認証、記録、デバイス取得、サンプル取得のライフサイクル全体を実装する際は、デリゲートメソッドカタログを確認してください。

データの記録と取得

記録の開始と停止
// Begin recording -- sensor stays active as long as any app has a stake
reader.startRecording()

// Stop recording -- framework deactivates the sensor when
// no app or system process is using it
reader.stopRecording()
データの取得

時間範囲と対象デバイスを指定して SRFetchRequest を作成し、リーダーに渡します。

let request = SRFetchRequest()
request.device = SRDevice.current
request.from = SRAbsoluteTime(CFAbsoluteTimeGetCurrent() - 86400 * 2)  // 2 days ago
request.to = SRAbsoluteTime.current()

reader.fetch(request)

結果はデリゲートを介して受け取ります。

func sensorReader(
    _ reader: SRSensorReader,
    fetching request: SRFetchRequest,
    didFetchResult result: SRFetchResult<AnyObject>
) -> Bool {
    let timestamp = result.timestamp

    switch reader.sensor {
    case .ambientLightSensor:
        if let sample = result.sample as? SRAmbientLightSample {
            let lux = sample.lux
            let chromaticity = sample.chromaticity
            let placement = sample.placement
            processSample(lux: lux, chromaticity: chromaticity, at: timestamp)
        }
    case .keyboardMetrics:
        if let sample = result.sample as? SRKeyboardMetrics {
            let words = sample.totalWords
            let speed = sample.typingSpeed
            processKeyboard(words: words, speed: speed, at: timestamp)
        }
    case .deviceUsageReport:
        if let sample = result.sample as? SRDeviceUsageReport {
            let wakes = sample.totalScreenWakes
            let unlocks = sample.totalUnlocks
            processUsage(wakes: wakes, unlocks: unlocks, at: timestamp)
        }
    default:
        break
    }

    return true  // Return true to continue receiving results
}

func sensorReader(_ reader: SRSensorReader, didCompleteFetch request: SRFetchRequest) {
    print("Fetch complete for \(reader.sensor)")
}

func sensorReader(
    _ reader: SRSensorReader,
    fetching request: SRFetchRequest,
    failedWithError error: any Error
) {
    print("Fetch failed: \(error)")
}

result.sample を、リーダーのセンサーに対応するサンプル型にキャストします。ストリームによっては結果ごとにオブジェクトが 1 つ返されますが、記録されたモーション、ECG、PPG、環境圧力のストリームでは、記録済みサンプルの配列が返されることがあります。

データ保持期間

SensorKit は新しく記録されたデータに 24 時間の保持期間 を課します。この期間と重なる時間範囲のフェッチリクエストは結果を返しません。この遅延を考慮してデータ収集ワークフローを設計してください。

SRDevice

SRDevice はセンサーサンプルのハードウェアソースを識別します。これを使用して、iPhone からのデータと Apple Watch からのデータを区別します。

// Get the current device
let currentDevice = SRDevice.current
print("Model: \(currentDevice.model)")
print("System: \(currentDevice.systemName) \(currentDevice.systemVersion)")

// Fetch all available devices for a sensor
reader.fetchDevices()

フェッチしたデバイスはデリゲートを通じて処理します:

func sensorReader(_ reader: SRSensorReader, didFetch devices: [SRDevice]) {
    for device in devices {
        let request = SRFetchRequest()
        request.device = device
        request.from = SRAbsoluteTime(CFAbsoluteTimeGetCurrent() - 86400)
        request.to = SRAbsoluteTime.current()
        reader.fetch(request)
    }
}

func sensorReader(_ reader: SRSensorReader, fetchDevicesDidFailWithError error: any Error) {
    print("Failed to fetch devices: \(error)")
}
SRDevice のプロパティ
プロパティ型説明
modelStringユーザー定義のデバイス名
nameStringフレームワーク定義のデバイス名
systemNameStringOS 名 (iOS、watchOS)
systemVersionStringOS バージョン
productTypeStringハードウェア識別子
currentSRDevice実行中のデバイス用のクラスプロパティ

よくある間違い

やってはいけない: エンタイトルメントなしで SensorKit を使用しようとする

本番用のリーダーを構築する前に、Apple の研究承認、センサー固有のエンタイトルメント値、および対応する手動プロビジョニングプロファイルを取得してください。

やってはいけない: データにすぐアクセスできると期待する

データ保持期間を適用してください。保持期間と重なるフェッチは、記録が失敗した証拠にはなりません。

やってはいけない: フェッチ前にデリゲートの設定を忘れる

startRecording() または fetch(_:) の前にデリゲートを割り当ててください。結果と失敗はデリゲートコールバックを通じて届きます。

やってはいけない: センサーごとの Info.plist 使用目的の詳細を省略する

リクエストするすべてのセンサーに対して、正確な Info.plist 構成の使用目的の詳細エントリを追加してください。

やってはいけない: SRError コードを無視する

少なくとも .invalidEntitlement、.noAuthorization、.dataInaccessible、.fetchRequestInvalid、.promptDeclined、および未知の将来のコードを区別してください。完全な switch とコールバックの配線については、完全なデリゲート実装を参照してください。

レビューチェックリスト

  • 開発前に Apple 承認の研究調査が整っている
  • com.apple.developer.sensorkit.reader.allow エンタイトルメントに必要なセンサーのみが列挙されている
  • 明示的な App ID と SensorKit 機能を備えた手動プロビジョニングプロファイル
  • Info.plist に明確な研究目的を記載した NSSensorKitUsageDescription
  • Info.plist に有効なプライバシーポリシー URL を記載した NSSensorKitPrivacyPolicyURL
  • リクエストするすべてのセンサーに対する NSSensorKitUsageDetail エントリ
  • 必須センサーとオプションセンサーに対して Required キーが適切に設定されている
  • 記録前に認証をリクエストし、フェッチ前にステータスを確認している
  • startRecording() または fetch(_:) を呼び出す前にデリゲートが割り当てられている
  • フェッチリクエストの時間範囲が 24 時間のデータ保持期間を考慮している
  • すべての失敗デリゲートメソッドで SRError コードが処理されている
  • フェッチ前に利用可能なデバイスを検出するために fetchDevices() が使用されている
  • データ収集が完了したときに stopRecording() が呼び出されている
  • sensorReader(_:fetching:didFetchResult:) が継続する場合は true を、停止する場合は false を返す

参照

コメント