I'm trying to build a macOS app that loads a list from a server and updates each list item's view every x
seconds.
I browsed some existing code and followed some tutorials as I am still learning but I can't seem to figure out what's going wrong.
I have a Store
that holds the data and updates it periodically like so:
class Store: NSObject, ObservableObject {
@Published var items: [Item]
var timer: Timer = Timer()
override init() {
var newItems: [Item] = []
// updateList calls the web service and receives a `[Item]`
updateList(update: { i in
newItems = i
})
items = newItems
super.init()
startTimer()
}
func startTimer() {
self.timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { _ in
var newList: [Item] = []
updateList(update: { vals in
newList = vals
})
self.objectWillChange.send()
items = newList
})
}
}
The view looks like this:
struct ContentView: View {
@ObservedObject var store: Store
var body: some View {
VStack(alignment: .leading, spacing: 12) {
List(store.items, id: \.self) { item in
ListRow(item: binding(for: item))
}
}
.frame(minWidth: windowSize().minWidth, minHeight: windowSize().minHeight)
.navigationTitle("Mission")
.toolbar {
ToolbarItem(placement: .status) {
Button(action: { /* Show connect dialog */ }) {
Image(systemName: "network")
}
}
ToolbarItem(placement: .primaryAction) {
Button(action: { /* Show file chooser/magnet entry dialog */ }) {
Image(systemName: "plus")
}
}
}
}
func binding(for item: Item) -> Binding<Item> {
guard let index = store.items.firstIndex(where: { $0.id == item.id }) else {
fatalError("Can't find in array")
}
return $store.items[index]
}
}
struct ListRow: View {
@Binding var item: Item
var body: some View {
Text(item.name)
.padding(.all, 20)
}
}
When I run the app, it's an empty list. I can add breakpoints and I can see the list of items is getting sent around properly so I have a feeling I'm doing something wrong with the list view itself, but I'm not sure. The only thing I know for sure is that my updateList
method is 100% returning a list of Item
s. Any ideas?
CodePudding user response:
Although you do not show enough of the code to replicate your issue, especially what updateList
does, try something like this (untested code):
class Store: NSObject, ObservableObject {
@Published var items: [Item] = [] // <-- here
var timer: Timer = Timer()
override init() {
super.init()
// updateList calls the web service and receives a `[Item]`
updateList(update: { vals in // <-- here
self.items = vals // <-- here
})
startTimer()
}
func startTimer() {
self.timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { _ in
self.updateList(update: { vals in // <-- here
self.objectWillChange.send() // <-- here
self.items = vals // <-- here
})
})
}
}
You must also have before ContentView
something like,
@StateObject var store = Store()
that you pass as ContentView(store: store)
.