4

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

jasnzhng
  • 85
  • 1
  • 6

2 Answers2

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)
        }
    }
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • 1
    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 Aug 14 '21 at 17:13
  • 1
    @Anton you could enumerate over the data ForEach(Array(examples.enumerated), id: \.element) { index, element in ... } – José Roberto Abreu Sep 22 '21 at 03:17
1
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)
        }
    }
}
Paul B
  • 3,989
  • 33
  • 46