3

I'm having trouble understanding how to make my SwiftUI data model example work with my Widget. It's working in my test app just fine, I observe changes immediately. When I attempt the Widget, I can see the data being printed in console but no changes are happening in my View in WidgetKit. I'm using a ObservableObject class and @Published variables. I've attempted to use a @StateObject, @ObservedObject and an @EnvironmentObject and the same results. Always results in No game today. My data model is Combine related, not sure if that has anything to do with why I can't see data changes in my WidgetKit's View.

SwiftUI App example working

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)
            }
        }
    }
}

Example Widget @StateObject (No data changes)

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")
            }
        }
    }
}

Example Widget 2 TimelineEntry (No data changes)

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")
            }
        }
    }
}
cole
  • 1,039
  • 1
  • 8
  • 35

1 Answers1

4

Widgets are static, the view you provide on getTimeline is what will be displayed as is and won't be updated, so the call to fetchSchedule on onAppear won't even be executed. Once you execute the completion handler in getTimeline, no more code is executed.

To display data on your widgets you need to fetch your data in getTimeline, create a view that displays the data from the start, and return that entry in your completion handler.

In your last example I would suggest that you create your entry in the completion handler of model.fetchSchedule and pass the schedule object to your view.

EmilioPelaez
  • 18,758
  • 6
  • 46
  • 50
  • 1
    You were absolutely correct, thanks for pointing me in the right direction! It’s working now https://imgur.com/a/A5QzKaJ – cole Mar 27 '21 at 09:20