SensorKit中仅didFetchResult代理方法未触发求助
SensorKit中
didFetchResult代理方法未触发排查方案 问题描述
已成功获取SensorKit全部权限,能正常获取设备信息,sensorReader(_:didFetch:)、sensorReader(_:didCompleteFetch:)等代理方法均可正常调用,但用于获取照度值的sensorReader(_:fetching:didFetchResult:)始终未被触发。已确认权限配置正常,设备内存在可共享的照度数据。
相关代码
import Foundation import SensorKit import UIKit final class SensorKitManager: NSObject, ObservableObject, SRSensorReaderDelegate { static let shared = SensorKitManager() private let ambientReader = SRSensorReader(sensor: .ambientLightSensor) private var availableDevices: [SRDevice] = [] @Published var ambientLightData: [AmbientLightDataPoint] = [] var isFetching = false var isRecordingAmbientLight = false private override init() { super.init() setupReaders() checkAndRequestAuthorization() } private func setupReaders() { ambientReader.delegate = self } // MARK: - 권한 설정 func requestAuthorization() { SRSensorReader.requestAuthorization(sensors: [.ambientLightSensor]) { [weak self] error in DispatchQueue.main.async { guard let self = self else { print("권한 요청 중단") return } if let error = error { print("권한 요청 실패: \(error.localizedDescription)") if (error as NSError).code == SRError.promptDeclined.rawValue { print("사용자 권한 거부") } } else { print("권한 요청 성공") self.startRecordingAmbientLightData() } } } } func checkAndRequestAuthorization() { let status = ambientReader.authorizationStatus switch status { case .authorized: print("조도 센서 접근 허용됨") startRecordingAmbientLightData() case .notDetermined: print("조도 센서 접근 미결정, 권한 요청 시작") requestAuthorization() case .denied: print("조도 센서에 대한 접근 거부 또는 제한됨") // 필요한 경우 사용자에게 설정에서 권한을 변경하도록 안내하는 로직 @unknown default: print("알 수 없는 권한 상태입니다.") } } // MARK: - 조도 값 로직 func startRecordingAmbientLightData() { guard !isRecordingAmbientLight else { print("이미 조도 데이터 기록 중입니다.") return } print("조도 데이터 기록 시작") isRecordingAmbientLight = true ambientReader.startRecording() fetchAmbientDeviceData() fetchAmbientLightData() triggerDummyFetchResult() } private func fetchAmbientDeviceData() { print("디바이스 정보 페치 시작") let fetchRequest = SRFetchRequest() // 48시간 전부터 24시간 전까지의 데이터 요청 (대기 기간을 벗어난 데이터) let fromDate = Date().addingTimeInterval(-72 * 60 * 60) let toDate = Date().addingTimeInterval(-24 * 60 * 60) fetchRequest.from = SRAbsoluteTime(fromDate.timeIntervalSinceReferenceDate) fetchRequest.to = SRAbsoluteTime(toDate.timeIntervalSinceReferenceDate) if availableDevices.isEmpty { print("디바이스 없음") ambientReader.fetchDevices() } else { for device in availableDevices { print("데이터 페치 시작 (디바이스: \(device))") fetchRequest.device = device ambientReader.fetch(fetchRequest) print("페치 요청 보냄 (디바이스: \(device))") } } } func fetchAmbientLightData() { guard !isFetching else { print("이미 페치 중입니다.") return } isFetching = true let fetchRequest = SRFetchRequest() let now = Date() let fromDate = now.addingTimeInterval(-72 * 24 * 60 * 60) let toDate = now.addingTimeInterval(-24 * 60 * 60) // 24시간 전 fetchRequest.from = SRAbsoluteTime(fromDate.timeIntervalSinceReferenceDate) fetchRequest.to = SRAbsoluteTime(toDate.timeIntervalSinceReferenceDate) fetchRequest.device = SRDevice.current ambientReader.fetch(fetchRequest) } private func displayAmbientLightData(sample: SRAmbientLightSample) { print("조도: \(sample.lux.value) lux") // ambientLightData 배열의 전체 내용을 추가로 출력 print("현재 ambientLightData 내용:") for data in ambientLightData { print("Timestamp: \(data.timestamp), Lux: \(data.lux)") } } // MARK: - Dummy Test func triggerDummyFetchResult() { print("더미 데이터로 didFetchResult 호출 테스트") // 실제로 데이터가 패치될 수 있는지 테스트할 수 있는 요청을 만들고 호출 fetchAmbientLightData() // 실 데이터를 통해 패치 시도 } // MARK: - SRSensorReaderDelegate 메서드 func sensorReader(_ reader: SRSensorReader, didFetch devices: [SRDevice]) { print("장치 페치 : \(devices.count)개") availableDevices = devices for device in devices { print("페치된 장치: \(device)") } if !devices.isEmpty { fetchAmbientDeviceData() } } func sensorReader(_ reader: SRSensorReader, didCompleteFetch fetchRequest: SRFetchRequest) { print("데이터 페치 완료") isFetching = false // 현재 시간 기준으로 24시간 전과 72시간 전 시간 계산 let now = Date() let twentyFourHoursAgo = now.addingTimeInterval(-24 * 60 * 60) let seventyTwoHoursAgo = now.addingTimeInterval(-72 * 60 * 60) // 페치된 데이터 중 24시간에서 72시간 사이의 데이터만 필터링 let filteredData = ambientLightData.filter { dataPoint in dataPoint.timestamp >= seventyTwoHoursAgo && dataPoint.timestamp <= twentyFourHoursAgo } if filteredData.isEmpty { print("24시간에서 72시간 사이의 조도 데이터가 없습니다.") // 필요한 경우 기본 데이터 추가 DispatchQueue.main.async { let defaultTimestamp = twentyFourHoursAgo let dataPoint = AmbientLightDataPoint(timestamp: defaultTimestamp, lux: 10.0) self.ambientLightData.append(dataPoint) print("ambientLightData에 추가된 기본 조도 값: 0.0 lux, Timestamp: \(defaultTimestamp)") } } else { // 필터링된 데이터로 ambientLightData 업데이트 DispatchQueue.main.async { self.ambientLightData = filteredData print("24시간에서 72시간 사이의 조도 데이터 \(filteredData.count)개를 추가했습니다.") // 데이터 로깅 for dataPoint in filteredData { print("추가된 조도 값: \(dataPoint.lux) lux, Timestamp: \(dataPoint.timestamp)") } } } } func sensorReader(_ reader: SRSensorReader, fetching fetchRequest: SRFetchRequest, didFetchResult result: SRFetchResult<AnyObject>) -> Bool { print("sensorReader(_:fetching:didFetchResult:) 메서드 호출됨") if let ambientSample = result.sample as? SRAmbientLightSample { print("Lux value: \(ambientSample.lux.value)") // 추가 로직: 중복된 데이터가 있는지 확인하고 없을 때만 추가 let timestamp = Date(timeIntervalSinceReferenceDate: result.timestamp.rawValue) if !ambientLightData.contains(where: { $0.timestamp == timestamp }) { let dataPoint = AmbientLightDataPoint(timestamp: timestamp, lux: Float(ambientSample.lux.value)) ambientLightData.append(dataPoint) print("ambientLightData에 추가된 조도 값: \(ambientSample.lux.value) lux, Timestamp: \(timestamp)") } else { print("중복된 데이터이므로 추가하지 않음: Timestamp: \(timestamp)") } // 데이터 출력 self.displayAmbientLightData(sample: ambientSample) } return true } func sensorReader(_ reader: SRSensorReader, fetching fetchRequest: SRFetchRequest, failedWithError error: Error) { print("페치 요청 실패: \(error.localizedDescription)") } func sensorReaderWillStartRecording(_ reader: SRSensorReader) { print("레코딩 시작됨") } func sensorReaderDidStopRecording(_ reader: SRSensorReader) { print("조도 데이터 기록 중단됨") isRecordingAmbientLight = false } func sensorReader(_ reader: SRSensorReader, startRecordingFailedWithError error: Error) { print("레코딩 시작 실패: \(error.localizedDescription)") } }
排查方向及解决方案
1. 检查时间范围是否存在有效数据
你当前请求的是72小时前到24小时前的历史数据,但SensorKit的照度数据可能存在延迟同步的情况,或者该时间段内设备没有生成可共享的照度数据。
- 临时修改时间范围为最近1小时内(比如
fromDate = now.addingTimeInterval(-60*60),toDate = now),测试didFetchResult是否触发。 - 确认设备在目标时间段内处于活跃状态(未关机、未处于低功耗模式),因为低功耗模式下传感器数据采集可能暂停。
2. 检查SRFetchRequest的设备参数
在fetchAmbientLightData中你指定了fetchRequest.device = SRDevice.current,但如果当前设备的历史数据未同步到健康数据库,或者你需要获取其他设备的数据,可能导致无数据返回。
- 尝试遍历
availableDevices中的所有设备发起请求,而不是仅使用SRDevice.current。 - 在
sensorReader(_:didFetch:)回调中,确保对每个设备都执行了ambientReader.fetch(fetchRequest)。
3. 验证代理方法签名是否正确
虽然你已实现代理方法,但需确认签名完全匹配:
- 检查方法参数类型:
SRFetchResult<AnyObject>在部分iOS版本中可能需要改为SRFetchResult<SRAmbientLightSample>(更具体的类型),尝试修改后测试。 - 确保方法返回值为
Bool,且返回true表示继续接收后续结果,返回false会终止数据传输。
4. 检查数据共享设置
即使SensorKit权限已授权,还需确保设备的健康数据共享设置已开启:
- 前往「设置」>「隐私与安全性」>「健康」>「数据与访问权限」,确认你的应用已被允许读取照度数据。
- 检查「健康」应用中是否存在照度数据记录,若没有,先让设备采集一段时间数据后再测试。
5. 避免重复发起请求导致冲突
在startRecordingAmbientLightData中,你连续调用了fetchAmbientDeviceData()、fetchAmbientLightData()、triggerDummyFetchResult(),其中triggerDummyFetchResult()又会调用fetchAmbientLightData(),可能导致多个请求冲突,部分请求被忽略。
- 调整请求发起逻辑,确保同一时间只有一个
fetch请求在执行,或者等待前一个请求完成后再发起下一个。
内容的提问来源于stack exchange,提问作者Sijong Kim
相关产品推荐
相关产品推荐

