Home > Blockchain >  Periodically update list in swiftUI
Periodically update list in swiftUI

Time:02-27

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 Items. 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).

  • Related