Search code examples
iosswiftobservedobjectappstorage

Changes to Observed Object not reflected (intermittently) when sheet is dismissed


I am using a class conforming to ObservableObject to store locations. I have used @AppStorage to persist an array of locations selected by a user.

class SavedLocations: ObservableObject {
    @AppStorage("Locations") var all = [Location]()

    func deleteLocation(at offsets: IndexSet) {
        all.remove(atOffsets: offsets)
    }
}

The class above is first instantiated using @StateObject in a menu that displays chosen locations (if any) and a sheet which can be used to search for new locations to add.

struct LocationView: View {
    @Environment(\.dismiss) var dismiss
    @ObservedObject var networking: Networking
    @StateObject var savedLocations = SavedLocations()
    
    @State private var showingSheet = false
    
    var body: some View {
        NavigationView{
            VStack {

                // the list of all location, not always refreshed when updated using LocationSearch sheet presentation 
                List {
                    ForEach(savedLocations.all, id: \.self) { saved in
                        Button(saved.name) {
                            // use the selected location 
                            dismiss()
                        }
                    }
                    .onDelete(perform: savedLocations.deleteLocation)
                }
            }
            .sheet(isPresented: $showingSheet) {
                LocationSearch(networking: networking)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button{
                        showingSheet.toggle()
                    } label: {
                        Label("New Location", systemImage: "plus")
                    }
                }
            }
            .navigationTitle("Locations")
        }
    }
}

If a user searches for a location, they are presented with a list of locations as shown below:

struct LocationSearch: View {
    @Environment(\.dismiss) var dismiss
    @StateObject var locationService = LocationService()
    @ObservedObject var networking: Networking
    @ObservedObject var savedLocations = SavedLocations()
    @State var showAlert = false
    
    var body: some View{
        Form {
            ...

            Section {
                List {
                    ForEach(locationService.searchResults, id: \.self) { completionResult in
                        Button("\(completionResult.city), \(completionResult.country)") {
                            networking.getCoordinate(addressString: completionResult.city) { coordinates, error in
                                if error == nil {
                                    networking.lastLocation = CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
                                    DispatchQueue.main.async() {
                                        let newLocation = Location(name: completionResult.city)
                                        
                                        // UPDATES not always reflected when sheet is dismissed
                                        savedLocations.all.append(newLocation)
                                        savedLocations.all = savedLocations.all.unique()
                                        
                                        dismiss()
                                    }
                                } else {
                                    print("Error setting custom location: \(String(describing: error))")
                                    showAlert.toggle()
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

The problem is that upon dismissing the sheet used to search for and add locations, the new location does not always immediately appear in the list of all locations. Usually it does appear immediately after the search sheet is dismissed, but occasionally it is necessary to dismiss both sheets and trigger a new instance of the sheet containing the list of all locations.

I based my approach off of this post.


Solution

  • You are creating a new instance of SavedLocations when you do this:

    @ObservedObject var savedLocations = SavedLocations()
    

    You likely mean to do:

    @ObservedObject var savedLocations: SavedLocations
    

    and pass the existing SavedLocations into LocationSearch