sensorkit

sensorkit

熱門

使用 SensorKit 為獲得核准的研究計畫擷取研究級感測器資料。適用於應用程式需要設定 SensorKit entitlement、取得「研究感測器與使用資料」授權,或存取環境光、運動紀錄、裝置使用狀況、鍵盤計量、造訪地點、語音、臉部、手腕溫度、心電圖 (ECG)、光血流波 (PPG)、聲學設定或睡眠期間資料等情境。一般的運動與活動資料請導向 CoreMotion,健康紀錄與體能訓練資料則導向 HealthKit。

967星標
49分支
更新於 2026/7/31
SKILL.md
唯讀
名稱
sensorkit
描述

使用 SensorKit 為獲得核准的研究計畫擷取研究級感測器資料。適用於應用程式需要設定 SensorKit entitlement、取得「研究感測器與使用資料」授權,或存取環境光、運動紀錄、裝置使用狀況、鍵盤計量、造訪地點、語音、臉部、手腕溫度、心電圖 (ECG)、光血流波 (PPG)、聲學設定或睡眠期間資料等情境。一般的運動與活動資料請導向 CoreMotion,健康紀錄與體能訓練資料則導向 HealthKit。

SensorKit

請選擇確切的 SRSensor 並確認其個別可用性。一般運動/活動功能請使用 CoreMotion,健康紀錄與體能訓練請使用 HealthKit。

目錄

概述與需求

SensorKit 能讓研究類應用程式在 iPhone 與 Apple Watch 上記錄並擷取感測器資料。此框架有以下需求:

  1. 經 Apple 核准的研究計畫 -- 請至 researchandcare.org 提交計畫申請。
  2. SensorKit entitlement -- Apple 僅會為已核准的研究計畫簽發 com.apple.developer.sensorkit.reader.allow
  3. 手動佈署描述檔 (Manual provisioning profile) -- Xcode 需要啟用 SensorKit 功能的明確 App ID。
  4. 使用者授權 -- 系統會顯示「研究感測器與使用資料」授權頁面,供使用者按感測器逐一批准。
  5. 延遲擷取 -- 請圍繞規範的資料保存期來設計資料擷取時機。

應用程式最多可存取使用中感測器過去 7 天內錄製的資料。

Entitlements

將 SensorKit reader entitlement 新增至 .entitlements 檔案中。請僅列出 Apple 已為該研究計畫核准的感測器。常見的 entitlement 值包括:

<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>

在為每個核准的感測器選擇確切的 entitlement 字串和 NSSensorKitUsageDetail 鍵值時,請載入Entitlement 與 Usage-Detail 目錄。請對照個別 SRSensor 頁面重新核對特殊感測器。

進行手動簽署時,請將 Code Signing Entitlements 設定為該 entitlements 檔案、Code Signing Identity 設定為 Apple Developer、Code Signing Style 設定為 Manual,並將 Provisioning Profile 設定為包含 SensorKit 能力的明確描述檔。

Info.plist 設定

需要設定以下三個鍵值:

<!-- 顯示於授權頁面的研究目的描述 -->
<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>

Requiredtrue 且使用者拒絕授權該感測器,系統會提醒使用者該研究需要此感測器,並提供重新考慮的機會。

請為每個請求的感測器使用確切的 usage-detail 字典。如需對映上述運動和環境光範例以外的感測器,請載入 Entitlement 與 Usage-Detail 目錄

授權

請針對您的研究計畫所需的感測器請求授權。系統會在首次請求時顯示「研究感測器與使用資料」授權頁面。

import SensorKit

let reader = SRSensorReader(sensor: .ambientLightSensor)

// 一次請求多個感測器的授權
SRSensorReader.requestAuthorization(
    sensors: [.ambientLightSensor, .accelerometer, .keyboardMetrics]
) { error in
    if let error {
        print("Authorization request failed: \(error)")
    }
}

初次檢查與 Delegate 狀態變更時請使用同一個狀態處理函式:

private func applyAuthorizationStatus(
    _ status: SRAuthorizationStatus,
    to reader: SRSensorReader
) {
    switch status {
    case .authorized:
        reader.startRecording()
    case .denied:
        reader.stopRecording()
        // 引導使用者至「設定」>「隱私權與安全性」>「研究感測器與使用資料」。
    case .notDetermined:
        break // 先請求授權。
    @unknown default:
        break
    }
}

applyAuthorizationStatus(reader.authorizationStatus, to: reader)

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

可用感測器

請載入感測器目錄,將各個 SRSensor 對映至其對應的樣本型別。請僅請求研究計畫已獲核准的感測器,並再次確認所選感測器的可用性及 usage-detail 鍵值。

SRSensorReader

SRSensorReader 是存取感測器資料的核心類別。每個實例均負責讀取單一感測器的資料。

import SensorKit

// 為單一感測器建立 reader
let lightReader = SRSensorReader(sensor: .ambientLightSensor)
let keyboardReader = SRSensorReader(sensor: .keyboardMetrics)

// 指派 delegate 以接收回呼
lightReader.delegate = self
keyboardReader.delegate = self

Reader 透過 SRSensorReaderDelegate 進行通訊。在接線完整的授權、錄製、裝置擷取與樣本擷取生命週期時,請載入Delegate 方法目錄

錄製與擷取資料

開始與停止錄製

// 開始錄製 -- 只要有任何應用程式使用,感測器就會保持作用狀態
reader.startRecording()

// 停止錄製 -- 當沒有任何應用程式或系統程序使用時,框架即會停用感測器
reader.stopRecording()

擷取資料

建立包含時間範圍與目標裝置的 SRFetchRequest,然後傳遞給 reader:

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

reader.fetch(request)

透過 delegate 接收結果:

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  // 回傳 true 以繼續接收結果
}

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 轉型為該 reader 感測器對應的樣本結構。某些串流每個結果回傳單一物件,而錄製的運動、ECG、PPG 和環境氣壓串流則可能回傳錄製樣本的陣列。

資料保存期

SensorKit 對新錄製的資料設有 24 小時的保存期。若擷取請求的時間範圍重疊此保存期,將不會回傳任何結果。請圍繞此延遲機制來設計資料收集工作流程。

SRDevice

SRDevice 用於識別感測器樣本的硬體來源。可用來區分來自 iPhone 或 Apple Watch 的資料。

// 取得當前裝置
let currentDevice = SRDevice.current
print("Model: \(currentDevice.model)")
print("System: \(currentDevice.systemName) \(currentDevice.systemVersion)")

// 擷取感測器所有可用的裝置
reader.fetchDevices()

透過 delegate 處理擷取到的裝置:

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 屬性

屬性 型別 說明
model String 使用者自訂的裝置名稱
name String 框架定義的裝置名稱
systemName String 作業系統名稱 (iOS, watchOS)
systemVersion String 作業系統版本
productType String 硬體識別碼
current SRDevice 執行中裝置的類別屬性

常見錯誤

切勿:在未設定 entitlement 的情況下嘗試使用 SensorKit

在建構正式環境的 reader 之前,請先取得 Apple 的研究計畫核准、感測器特定的 entitlement 值以及對應的手動佈署描述檔。

切勿:預期能立即存取資料

請遵循資料保存期;擷取時間涵蓋保存期並不代表錄製失敗。

切勿:在擷取前忘記設定 Delegate

請在呼叫 startRecording()fetch(_:) 前指派 delegate;結果與失敗訊息均透過 delegate 回呼接收。

切勿:遺漏各感測器在 Info.plist 的使用細節

請為每個請求的感測器新增確切的 Info.plist 設定 usage-detail 項目。

切勿:忽略 SRError 錯誤碼

請至少區分 .invalidEntitlement.noAuthorization.dataInaccessible.fetchRequestInvalid.promptDeclined 以及未來未知的錯誤碼。完整 switch 與回呼接線請載入 完整 Delegate 實作

審查檢核表

  • [ ] 開發前已取得 Apple 核准的研究計畫
  • [ ] com.apple.developer.sensorkit.reader.allow entitlement 僅列出需要的感測器
  • [ ] 具有明確 App ID 和 SensorKit 功能的手動佈署描述檔
  • [ ] Info.plist 中含有清楚研究目的的 NSSensorKitUsageDescription
  • [ ] Info.plist 中含有有效隱私權政策 URL 的 NSSensorKitPrivacyPolicyURL
  • [ ] 為每個請求的感測器提供 NSSensorKitUsageDetail 條目
  • [ ] 為核心與選用感測器適當設定 Required 鍵值
  • [ ] 錄製前已請求授權,擷取前已確認狀態
  • [ ] 呼叫 startRecording()fetch(_:) 前已指派 Delegate
  • [ ] 擷取請求的時間範圍已考慮 24 小時保存期

<!-- truncated for translation batch; full body continues in source -->