如何让 WidgetKit 上的现有条目保持刷新?

pas*_*tgt 7 ios swift healthkit widgetkit swiftui

我正在HealthKit我的小部件中使用数据。如果手机被锁定,则无法获取HealthKit数据,除非手机解锁。但是,即使手机被锁定,我的小部件时间线也会尝试更新。

是否有可能以某种方式返回空完成,因此它将保持当前小部件数据不变?

这是我的代码:

struct Provider: IntentTimelineProvider {
    private let healthKitService = HealthKitService()
    func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let currentDate = Date()
        let refreshDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
        
        healthKitService.getHeartRate() { data, error in
            
            //Create an empty entry
            var entry = SimpleEntry(date: currentDate, configuration: ConfigurationIntent(), data: nil)

            //If no errors, set data
            if(error == nil) {
                entry.data = data
            } else {
                print(error) //this runs when a locked phone does the widget update
            }
            
            //Return response
            let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
            completion(timeline)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我能做的就是存储条目数据并将UserDefaults其加载到错误路径中?我不确定这是否是一个好的解决方案。

paw*_*222 3

主要问题是函数中没有状态getTimeline这与如何在小部件 iOS14 中刷新多个计时器?中的问题类似。- 您需要某种方式在外部存储信息getTimeline

正如您已经提到的,一个可能的解决方案是将最后一个条目存储在UserDefatuls.

但是,您也可以尝试创建自己的EntryCache

class EntryCache {
    var previousEntry: SimpleEntry?
}
Run Code Online (Sandbox Code Playgroud)
struct SimpleEntry: TimelineEntry {
    let date: Date
    var previousDate: Date?
}
Run Code Online (Sandbox Code Playgroud)
struct IntentProvider: IntentTimelineProvider {
    private let entryCache = EntryCache()
    
    // ...

    // in this example I'm testing if the `previousDate` is loaded correctly from the cache
    func getTimeline(for configuration: TestIntentIntent, in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        let currentDate = Date()
        let entry = SimpleEntry(date: currentDate, previousDate: entryCache.previousEntry?.date)
        
        let refreshDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
        let refreshEntry = SimpleEntry(date: refreshDate, previousDate: entryCache.previousEntry?.date)

        let timeline = Timeline(entries: [entry, refreshEntry], policy: .atEnd)

        // store the `entry` in the `entryCache`
        entryCache.previousEntry = entry
        completion(timeline)
    }
}
Run Code Online (Sandbox Code Playgroud)

笔记

我没有找到任何关于何时TimelineProvider可以重新创建的信息。在我的测试中,Widget 在Provider每次刷新时都使用相同的内容,但更安全的做法是假设 Provider 可能会在将来的某个时刻重新初始化。那么,理论上,对于一个刷新周期,previousEntry将为nil