mcpField
S
sensorkit

sensorkit

github.com/dpearson2699开发1.2k3.1k 次安装13 周前更新
代码仓库

关于 sensorkit

sensorkit 是面向 Apple 研究应用开发者的 SensorKit 指南,涵盖授权、录制和读取传感器数据。它强调研究审批、传感器级配置与延迟读取要求。

主要功能

  1. 01配置研究授权与传感器权限
  2. 02使用 SRSensorReader 录制数据
  3. 03按时间范围和设备获取数据
  4. 04处理设备信息与委托回调
  5. 05遵循数据保留期要求

适用场景

  1. 01为获批研究配置传感器读取
  2. 02从 iPhone 或 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。

目录

概览和要求

SensorKit 支持研究类应用录制和获取 iPhone 与 Apple Watch 上的传感器数据。该框架要求:

  1. 获得 Apple 批准的研究项目 —— 在 researchandcare.org 提交提案。
  2. SensorKit entitlement —— Apple 仅会为获批项目授予 com.apple.developer.sensorkit.reader.allow。
  3. 手动配置描述文件 —— 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>

如果 Required 为 true,且用户拒绝授权该传感器,系统会提示用户研究项目需要该传感器,并提供重新考虑的机会。

对每个请求的传感器使用准确的 usage-detail 字典。映射超出上述运动和环境光示例的传感器时,请加载 Entitlement 和 Usage-Detail 目录。

授权

为研究项目所需的传感器请求授权。首次请求时,系统会显示“研究传感器与使用数据”表单。

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 映射到其样本类型。仅请求研究项目获批的传感器,并重新确认所选传感器是否可用,以及其 usage-detail 键是否准确。

SRSensorReader

SRSensorReader 是访问传感器数据的核心类。每个实例只读取一个传感器的数据。

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

reader 通过 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,然后将其传递给 reader:

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 转换为与 reader 传感器对应的样本结构。某些数据流会为每个结果返回一个对象,而录制的运动、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 中包含 NSSensorKitPrivacyPolicyURL,且隐私政策 URL 有效
  • 为每个请求的传感器添加 NSSensorKitUsageDetail 条目
  • 根据传感器是必需还是可选,适当设置 Required 键
  • 在录制前请求授权,并在获取前检查状态
  • 在调用 startRecording() 或 fetch(_:) 前指定委托
  • 获取请求的时间范围已考虑 24 小时的数据保留期
  • 在所有失败委托方法中处理 SRError 代码
  • 获取数据前使用 fetchDevices() 发现可用设备
  • 数据收集完成后调用 stopRecording()
  • sensorReader(_:fetching:didFetchResult:) 返回 true 以继续,或返回 false 以停止

参考资料

评论

支持 Markdown