Trying to Update Previous View on Form Submission SwiftUI

Viewed 76

I am working on a project where users will be able to enter their stock trades (lots) and later view some statistics. My problem is that when going from the list view to a detail edit view and pressing save, the list view doesn't get notified of this change (neither does the home view)

I could always try to pass the previous view models down to the edit view to invalidate them but this seems like a hack to me, so I am wondering if I am missing some important piece of SwiftUI.

Here is the code for the List View

struct StockView: View {
    
    @ObservedObject var vm: StockViewModel
    
    init(_ symbol: String) {
        vm = StockViewModel(symbol)
    }
    
    var body: some View {
        List {
            ForEach(vm.lots, id: \.id) { lot in
                NavigationLink(destination: LotEditView(lot.id)) {
                    ...
                }
            }            
        }.navigationTitle("My Lots")
        .listStyle(PlainListStyle())
    }
}

and the code for the Edit View

struct LotEditView: View {
    @Environment(\.presentationMode) var mode
    @ObservedObject var vm: LotEditViewModel
    
    init(_ id: UUID) {
        vm = LotEditViewModel(id)
    }
    
    var body: some View {
        VStack {
            Form {
                ...
            }
        }.navigationTitle("Edit Lot")
        .toolbar {
            Button("Done") {
                vm.save()
                mode.wrappedValue.dismiss()
            }
        }
    }
}

The List ViewModel is initialized with a stock name and then uses that to get the lots from a mock database, then the id of each of these lots is passed to the Edit ViewModel. The vm.save() simply updates the database which in this case is just an array which I've confirmed is being updated.

1 Answers

So what you want to do is to have one instance of that Mock database and inject it into viewModels. I think the easiest way here would be to make this mock database as a @StateObject. Create it in the inital view(might be homeView or even Appdelegate) and pass it on as environmentObject to other views.

struct StockView: View {

    @ObservedObject var vm: StockViewModel
    @StateObject var dbService = MyMockDatabase()

    init(_ symbol: String) {
        vm = StockViewModel(dbService: dbService, symbol: symbol)
    }

    var body: some View {
        List {
            ForEach(vm.lots, id: \.id) { lot in
                NavigationLink(destination: LotEditView(lot.id).environmentObject(dbService)) {
                    ...
                }
            }            
        }.navigationTitle("My Lots")
        .listStyle(PlainListStyle())
    }
}

And in your next screen use it as:

struct LotEditView: View {

    @Environment(\.presentationMode) var mode
    @EnvironmentObject var dbService: MyMockDatabase
    @ObservedObject var vm: LotEditViewModel

    init(_ id: UUID) {
        vm = LotEditViewModel(dbService: dbService, id: id)
    }

    var body: some View {
        VStack {
            Form {
                ...
            }
        }.navigationTitle("Edit Lot")
        .toolbar {
            Button("Done") {
                vm.save()
                mode.wrappedValue.dismiss()
            }
        }
    }
}

MockArray would look something like:

class MyMockDatabase: ObservableObject {
    @Published var array = [...]
}
Related