5

I have a struct that outlines the data, and then a class that has an array of the structs.

struct Example: Codable, Identifiable {
     var id: UUID

     var title: String
     var description: String?

     var tags: [String]
}

class Examples: ObservableObject {
     @Published var examples = [Example]()
}

Then, I have a ForEach that displays the data with a button:

ForEach(examples.examples) { example in
     VStack {
         Button("Complete") {
            example.completed = true
         }
         Text(example.title)
     }
}

When I try to run, it gives me the error "Cannot assign to property: 'example' is a 'let' constant." I've tried passing the example into a separate view with an @Binding var (from this post ) and also modifying the data with an index (from this post), but both give me an error saying that Xcode was unable to produce a diagnostic report and to file feedback. How can I modify the data?

Sorry if this is a bad question, I'm pretty new to SwiftUI

2 Answers 2

5

This is because a ForEach returns you a read-only copy of the original example to operate on and you can't modify it. And even if you could it wouldn't be the same struct (structs are value types).

You need to access the original item instead:

var body: some View {
    ForEach(examples.examples.indices) { index in
        VStack {
            Button("Complete") {
                examples.examples[index].completed = true
            }
            Text(examples.examples[index].title)
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I believe this approach does not work if you want to be able to reorder/delete/insert items in the examples array. Any ideas for that case? The items in the ForEach would need to be identified by the id property rather than by index.
@Anton you could enumerate over the data ForEach(Array(examples.enumerated), id: \.element) { index, element in ... }
3
struct Example: Identifiable {
    var id = UUID()
    var title: String
    var description: String
    var tags: [String]
    var completed = false
}

class Examples: ObservableObject {
    @Published var examples : [Example]
    
    init() {
        self.examples = [Example(title: "Title 1", description: "Desc 1", tags: ["tag 1", "tag 1.1", "tag 1.2"]),
                         Example(title: "Title 2", description: "Desc 2", tags: ["tag 2"]),
                         Example(title: "Title 3", description: "Desc 3", tags: ["tag 3", "tag 3.1"])
        ]
    }
}

struct ContentView: View {
    @ObservedObject var examples = Examples()
    var body: some View {
        ForEach($examples.examples) { $example in
            VStack {
                Button("Complete") {
                    example.completed.toggle()
                }
                Text(example.title + " " + example.completed.description)
                TextField("Description", text: $example.description)
                    
                ForEach($example.tags, id: \.self) { $t in
                    TextField("Tag ", text: $t)
                }
            }
            .multilineTextAlignment(.center)
        }
    }
}

Comments

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.