WidgetKit @StateObject не обновляет представление

У меня проблемы с пониманием того, как заставить мой пример модели данных SwiftUI работать с моим виджетом. Он отлично работает в моем тестовом приложении, я сразу замечаю изменения. Когда я пытаюсь использовать виджет, я вижу, что данные печатаются в консоли, но в моем View в WidgetKit не происходит никаких изменений. Я использую ObservableObject класс и @Published переменные. Я попытался использовать @StateObject, @ObservedObject и @EnvironmentObject и получил те же результаты. Всегда приводит к тому, что сегодня нет игры. Моя модель данных Combine связана, я не уверен, имеет ли это какое-либо отношение к тому, почему я не вижу изменений данных в моем WidgetKit View.

Пример работы приложения SwiftUI

struct ContentView: View {
    @ObservedObject var data = CombineData()
    @State private var showSortSheet: Bool = false
    @State private var nhlTeams: TeamOrder = .NewYorkIslanders
    
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                if data.schedule?.dates.first != nil {
                    Text("Game today")
                } else {
                    Text("No game today")
                }
            }
            .listStyle(PlainListStyle())
            .navigationBarTitle(Text(""), displayMode: .inline)
            .navigationBarItems(leading: HStack {
                Button(action: {
                    self.showSortSheet.toggle()
                }) {
                    HStack {
                        Image(systemName: "arrow.up.arrow.down")
                    }
                }
            })
            .actionSheet(isPresented: $showSortSheet) {
                ActionSheet(title: Text("Teams:"), buttons: [TeamOrder.NewYorkIslanders, TeamOrder.MontrealCanadiens].map { method in
                    ActionSheet.Button.default(Text("\(method.rawValue)")) {
                        self.nhlTeams = method
                        data.fetchSchedule(self.nhlTeams.rawValue)
                        print("\(self.nhlTeams.rawValue)")
                    }
                })
            }
            .onAppear {
                data.fetchSchedule(self.nhlTeams.rawValue)
            }
        }
    }
}

Пример виджета @StateObject (без изменений данных)

struct ExampleEntryView : View {
    var entry: Provider.Entry
    @Environment(\.widgetFamily) var widgetFamily
    @StateObject var model = CombineData()
    @ObservedObject var model = CombineData()
    /*@EnvironmentObject private var model: CombineData*/
    var body: some View {
        VStack(alignment: .leading) {
            if model.schedule?.dates.first != nil {
                Text("Game today")
            } else {
                Text("No game today")
            }
        }
        .onAppear {
            model.fetchSchedule(entry.configuration.teams.rawValue) {
                WidgetCenter.shared.reloadTimelines(ofKind: "NHL_Widget")
            }
        }
    }
}

Пример виджета 2 TimelineEntry (без изменений данных)

struct SimpleEntry: TimelineEntry {
    let date: Date
    let configuration: ConfigurationIntent
    let model: CombineData
}

struct Provider: IntentTimelineProvider {
    
    func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []
        let currentDate = Date()
        let model = CombineData()
        let entryDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
        let entry = SimpleEntry(date: entryDate, configuration: configuration, model: model)
        entries.append(entry)
        let timeline = Timeline(entries: entries, policy: .atEnd)
        model.fetchSchedule(entry.configuration.teams.rawValue) {
            completion(timeline)
        }
    }
}

struct ExampleEntryView : View {
    var entry: Provider.Entry
    @Environment(\.widgetFamily) var widgetFamily
    var body: some View {
        VStack(alignment: .leading) {
            if entry.model.schedule?.dates.first != nil {
                Text("Game today")
            } else {
                Text("No game today")
            }
        }
    }
}

person cole    schedule 26.03.2021    source источник


Ответы (1)


Виджеты статичны, представление, которое вы предоставляете на getTimeline, будет отображаться как есть и не будет обновляться, поэтому вызов fetchSchedule на onAppear даже не будет выполнен. После выполнения обработчика завершения в getTimeline код больше не выполняется.

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

В вашем последнем примере я бы посоветовал вам создать запись в обработчике завершения model.fetchSchedule и передать объект schedule вашему представлению.

person EmilioPelaez    schedule 26.03.2021
comment
Вы были абсолютно правы, спасибо, что указали мне правильное направление! Сейчас он работает imgur.com/a/A5QzKaJ - person cole; 27.03.2021