I have a swiftui application with a model structure similar to below. Event and Media are classes managed by a data manager and they're passed in to a parent-view EventsListView that shows events with a media count and tapping on event will take you details EventDetailsView where it shows all media belonging to an event.
EventsListView takes on an Event object from data manager and creates a @State object of type EventDisplayModel based on it's properties that is used to display thumbnail and count of media in it.
EventDetailsView takes on same Event and displayed each Media. It keeps reference to Event as an @ObservedObject. The details view can enable or disable media that belongs to an event.
The problem I'm having is, when user enable or disable their media from EventDetailsView they're correctly captured in both details view as well as changed properly at data manager level. The parent view EventsListView however is not being updated of these changes when the user goes back. I can see the view body changes in events list screen but the count of media still remains original value, despite the actual Event carries the updated values. The @State property is also getting changed, but UI doesnt get updated. Why does this happen?
// UI model
struct EventDisplayModel: Identifiable {
init(from event: Event) {
self.id = event.id; self.thumbnailId = event.thumbnailId; self.title = event.name; self.count = event.selectedMediaCount
}
let id: UUID
var thumbnailId: String?
var title: String
var count: Int
}
// Data manager models
class Event: ObservableObject, Identifiable {
let id = UUID()
@Published var thumbnailId: String?
@Published var media: [Media] = []
@Published var name: String = ""
var selectedMediaCount: Int {
media.filter { $0.isSelected }.count
}
}
class Media: ObservableObject, Identifiable {
internal init(thumbnailId: String? = nil, isSelected: Bool = false) {
self.thumbnailId = thumbnailId
self.isSelected = isSelected
}
let id = UUID()
var thumbnailId: String?
@Published var isSelected: Bool = false
}
How the EventsListView's cell view is constructed
struct EvenListCell: View {
@State private var displayModel: EventDisplayModel
var body: some View {
Text("count: \(displayModel.count)") // doesn't get updated
}
init(model: Event) {
_displayModel = State(initialValue: .init(from: model))
}
}
How the EventDetailView each media item is rendered and the enabled state gets updated.
struct EventDetailsCell: View {
@ObservedObject var mediaItem: Media
var body: some View {
ZStack {
// UI Logic
}
.onTapGesture {
// This gets updated in Event details page as well as on original model owned by data manager. But the changes are not reflected by EventList
mediaItem.isSelected.toggle()
}
}
}
Puzzled by how the model changes are not reflected in the EventsList screen. Should I discard the EventDisplayModel all together and keep individual @State variables where needed with a reference to the Event model as @ObservedObject (These state properties will become really complex however with future planned feature updates)
Eventshould be a struct becauseIdentifiablebyiddoesn't make sense for a class that already is an object reference.Viewstructs should not have aninitbecause it will break the View struct's change detection if you do any transformation of the params ininitvs transforming on the down e.g. in via computed properties in parentView.