You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:42:02