Как я могу обновить существующую запись в WidgetKit?

Я использую 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)
        }
    }
}

Что я могу сделать, так это сохранить данные входа в UserDefaults и загрузить их в маршрут ошибки? Я не уверен, что это хорошее решение.


person passatgt    schedule 31.12.2020    source источник


Ответы (1)


Основная проблема в том, что у вас нет состояния в функции getTimeline. Это аналогичная проблема, что и в Как обновить несколько таймеров в виджете iOS14? - вам нужен способ хранить информацию за пределами getTimeline .

Как вы уже упоминали, возможное решение - сохранить последнюю запись в UserDefatuls.

Однако вы также можете попробовать создать свой собственный EntryCache:

class EntryCache {
    var previousEntry: SimpleEntry?
}
struct SimpleEntry: TimelineEntry {
    let date: Date
    var previousDate: Date?
}
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)
    }
}

Примечание

Я не нашел никакой информации о том, когда TimelineProvider может быть воссоздан. В моих тестах виджет использовал один и тот же Provider для каждого обновления, но безопаснее предположить, что поставщик может быть повторно инициализирован в какой-то момент в будущем. Тогда теоретически для одного цикла обновления previousEntry будет nil.

person pawello2222    schedule 17.01.2021