0

I'm using MVVM architecture with SwiftUI.

I'm trying to refactor my code to use ForEach() in my view to display a number of subviews that all have the same basic format, a string (asking a question) and a button to press to display a sheet to enter the value.

struct EntryView: View {
    @ObservedObject var viewModel = EntryViewModel()
    var body: some View {
        VStack (spacing: 50) {
            ForEach(viewModel.questions.indices) { index in
                HStack (spacing: 50) {
                    Text(viewModel.questions[index].text)
                        .sheet(isPresented: $viewModel.questions[index].sheetIsPresented,
                                     content: {
                                        viewModel.questions[index].sheet
                                     })
                    Button(action: {
                        viewModel.questions[index].sheetIsPresented = true
                    }, label: {
                        Text("Enter")
                    })
                }
            }
        }
    }
}



class EntryViewModel: ObservableObject {
    @Published var questions: [Question] = []
    @Published var firstQuestion = Question()
    @Published var secondQuestion = Question()
    
    init() {
        questions.append(firstQuestion)
        questions.append(secondQuestion)
    }
}

class Question: ObservableObject {
    @Published var sheetIsPresented = false
    let text = "Foo"
    var sheet = AnyView(InitialView())
}

The problem is that although the button changes the correct property in the correct element at the correct index in the array, this doesn't then cause the view to update.

I'm aware that this is because when the sheetIsPresented property of the Question element of the questions Array is changed to true, it doesn't actually mutate the Array itself and so the publisher doesn't update the view.

How do I fix it please?

I'm still struggling to get my head around SwiftUI and Combine, so any pointers will be gratefully received.

1 Answer 1

1

You can't "chain" ObservableObjects. You have to Observe them directly.

import SwiftUI

struct EntryView: View {
    @ObservedObject var viewModel = EntryViewModel()
    var body: some View {
        VStack (spacing: 50) {
            ForEach(viewModel.questions.indices) { index in
                SingleEntryView(viewModel: viewModel.questions[index])
            }
        }
    }
}
struct SingleEntryView: View {
    @ObservedObject var viewModel: Question
    var body: some View {
        HStack (spacing: 50) {
            Text(viewModel.text)
                .sheet(isPresented: $viewModel.sheetIsPresented,
                             content: {
                                viewModel.sheet
                             })
            Button(action: {
                viewModel.sheetIsPresented = true
            }, label: {
                Text("Enter")
            })
        }
    }
}


class EntryViewModel: ObservableObject {
    @Published var questions: [Question] = []
    @Published var firstQuestion = Question()
    @Published var secondQuestion = Question()
    
    init() {
        questions.append(firstQuestion)
        questions.append(secondQuestion)
    }
}

class Question: ObservableObject {
    @Published var sheetIsPresented = false
    let text = "Foo"
    var sheet = AnyView(Text("Sheet"))
}

struct EntryView_Previews: PreviewProvider {
    static var previews: some View {
        EntryView()
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. Makes sense. Will try on Tuesday and mark it as correct then.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.