Search code examples
iosxcodeswiftuicore-datapicker

Refreshing picker after returning from child view


I have a view to add a new Inventory object. to do so it requires that you select a Vessel object from a picker that it's assigned to. If no Vessels have been created yet, it prompts you to bring up a sheet to add a Vessel first, then dismisses the sheet once the Vessel is added.

If I run this and a Vessel is already existing, everything runs as expected. However, in the case where there are not yet any Vessels to select from, it brings up VesselNewView() on a sheet, and correctly adds a new object to the Vessel class. When it returns to the NewInventorySheet it now shows the picker with the added Vessel checked. Except it isn't. If I print("vessel= \(vessel)") at this point I get vessel = <Vessel: 0x6000017d07c0> (entity: <null>; id: (null); data: <fault>) and trying to save crashes the app.

However if I click on the picker and re-select the item that looks like its already selected, then it works fine. How can I force the NewInventoryView to refresh the picker choices for vessel once it returns from adding one on NewVesselView?

Here's my code:

import SwiftUI

struct InventoryNewView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @State private var showSheetVessel = false
    
    @FetchRequest(
        sortDescriptors: [SortDescriptor(\.name)])
    var vessels:FetchedResults<Vessel>
    
    @Environment(\.dismiss) var dismiss
    @State var name = ""
    @State var vessel = Vessel()
    
    var body: some View {
        NavigationView {
            ScrollView {
                VStack(alignment: .leading){
                    Grid(alignment: .leading) {
                        GridRow {
                            Text("Name:")
                            TextField("Inventory item", text: $name)
                        }
                        .padding(.bottom, 5)
                        GridRow {
                            Text("Assigned to:")
                            if vessels.isEmpty == false {
                                Picker("", selection: $vessel) {
                                    ForEach(vessels) { (vessel: Vessel) in
                                        Text(vessel.name)
                                            .tag(vessel)
                                    }
                                }
                            }
                            else {
                                Text("Add Vessel")
                                    .fontWeight(.semibold)
                                    .foregroundColor(Color.red)
                            }
                            Button(action: {
                                self.showSheetVessel.toggle()
                            }) {
                                Image(systemName: "plus.circle")
                                    .font(.footnote)
                            }.sheet(isPresented: $showSheetVessel) {
                                VesselNewView()
                            }
                        }
                        .padding(.bottom, 5)
                    }
                }
                .navigationTitle("Add New Inventory")
                .navigationBarItems(
                    trailing:
                        Button("Add") {
                            addInventory()
                            dismiss()
                        }
                        .disabled(name.isEmpty)
                )
            }
        }
        
        func addInventory() {
            // Add the item into Core Data
            let inventory = Inventory(context: viewContext)
            inventory.id = UUID()
            inventory.name = name
            inventory.vessel = vessel
            vessel.addToInventory(inventory)
            
            
            do {
                try viewContext.save()
            }
            catch {
            }
            
        }
    }
}

Solution

  • To update the selected vessel, add

     .onChange(of: vessels.last) { 
        if let vesl = vessels.last { 
           vessel = vesl 
        } 
      } 
     
    

    to the NavigationView{...}.

    Note that NavigationView is deprecated, you should use NavigationStack.