Another Dude
Another Dude

Reputation: 1454

SwiftUI: UI not updated with object transiting through several views

I have an issue with a OSX app developed with SwiftUI.

Here is my model.

class Chapter: Identifiable, Hashable, ObservableObject {
    var content: [ChapterContent] = []
    var id: Int 
    // Other Equatable/Hashable related code...
}

// A chapter content contains either a message or an event, 
// both can't be nil at the same time
struct ChapterContent: Identifiable, Hashable {    
    var event: Event?
    var message: Message?

    var id: Int {
        return self.message?.id ?? self.event?.id ?? .min
    }

    init(_ event: Event) {
        self.event = event
        self.message = nil
    }
    init(_ message: Message) {
        self.event = nil
        self.message = message
    }

    var representingView: AnyView {
        // A view that represents the event or the message
    }
    // Other Equatable/Hashable related code...
}

The app starts with a ContentView() containing an array of chapters, represented in a list. The view also contains a button to add a new chapter (with a hardcoded id so far just to test this).

struct ContentView: View {
    var chapters: [Chapter]
    
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(chapters, id: \.self) { chapter in
                        Text("Chapter C\(chapter.id)")
                    }
                }
                Spacer()
                NavigationLink(
                    destination: ChapterView(chapter: Chapter(id: 1)), 
                    label: {
                        Text("ADD CHAPTER")
                    })
            }
        }
    }
}

In this ChapterView, I display all the content of the chapter and also two buttons to add either a new event or a new message into this chapter. To simplify it I'll just show the ADD EVENT button.

struct ChapterView: View {
    @State var chapter: Chapter

    var body: some View {
        NavigationView {
            VStack(spacing: 0) {
                VStack(spacing: 0) {
                    ForEach(chapter.content, id: \.self) { content in
                        content.representingView 
                    }
                    .padding(0)
                }
                HStack(spacing: 0) {
                    NavigationLink(
                        destination: EventFormView(with: $chapter),
                        label: {
                            Text("ADD EVENT")
                        })
                }
            }
        }
    }
}

And in the EventFormView:

struct EventFormView: View {
    @Binding var chapter: Chapter
 
    init(with chapter: Binding<Chapter>) {
        self._chapter = chapter
    }
}

Somewhere in EventFormView I add the newly created event with:

chapter.content.append(newEventChapterContent)

After checking while debugging, I ensured that the value is actually added into the content array of the chapter. However, this doesn't trigger any UI update in the list (which is contained in ChapterView).

I guess it is related to the way I use the State/Binding protocols. I should even maybe use ObservedObject but I don't know how to do so and correctly transmit data from view to view.

Thank you for your help

Upvotes: 1

Views: 54

Answers (1)

Asperi
Asperi

Reputation: 257493

At first make content published so it can be observed by views

class Chapter: Identifiable, Hashable, ObservableObject {
    @Published var content: [ChapterContent] = []

now as it is observable, wrap it in observed in views

struct ChapterView: View {
    @ObservedObject var chapter: Chapter
struct EventFormView: View {
    @ObservedObject var chapter: Chapter

    // previous init not needed

and create it as usual, because observable object is a reference, ie

EventFormView(chapter: chapter)

Upvotes: 2

Related Questions