Home > Mobile >  SwiftUI: Cannot use mutating member on immutable value: 'shop' is a 'let' consta
SwiftUI: Cannot use mutating member on immutable value: 'shop' is a 'let' consta

Time:11-29

I would like to add a Product to a Shop's container but i can't and i don't understand why because my Shop is var and not let. My goal is to put a Product into a Shop like this: input Shop(name: "Apple Store", container: []) output: Shop(name: "Apple Store", container: [Product(name: "Cheese")])

Here is My code:

import SwiftUI


struct Shop: Identifiable {
    let name: String
    var container: [Product]
    var id = UUID()
}

struct Product: Identifiable {
    let name: String
    var id = UUID()
}

struct ContentView: View {
    
    @State var shops: [Shop] = [
        Shop(name: "Apple Store", container: []),
        Shop(name: "StopShop", container: [Product(name: "milk")])
    ]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(shops) { shop in
                    NavigationLink(shop.name, destination: {
                        List {
                            ForEach(shop.container) { product in
                                Text(product.name)
                            }
                        }
                        .navigationBarTitle(shop.name)
                        .navigationBarItems(trailing: Button {
                            shop.container.append(Product(name: "Cheese"))
                        } label: {
                            Text("add").bold()
                        })
                    })
                }
            }
            .navigationBarTitle("Shops")
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

I have tried by append or insert it and i expected it to work but it didn't. :(

CodePudding user response:

The error tells the truth: Index variables in a loop are constants in Swift (since the initial release).

Due to value semantics you have to modify the Shop element in the shops array directly.

Add a function inside the view

func appendProduct(_ product: Product, to shop: Shop) {
    guard let index = shops.firstIndex(where: {$0.id = shop.id}) else { return }
    shops[index].container.append(product)
}

and call it

.navigationBarItems(trailing: Button {
    appendProduct(Product(name: "Cheese"), to: shop)
} label: {
    Text("add").bold()
})

CodePudding user response:

Your problem here is that you are not modifying the struct's

@State var shops: [Shop]

You are trying to change the element iterated on the foreach, which is, in fact, a let.

ForEach(shops) { shop in // shop here is a let

Knowing that, you should be able to modify your code to make the proper implementation.

So for example, if you want to modify the struct's param, you can:

ForEach(Array(shops.enumerated()), id: \.offset) { index, element in
  // ...
  shops[index].container.append(Product(name: "Cheese"))
}

(This is a draft example, you can do it however you think its best)

  • Related