0

I’m practicing MVVM and SwiftUI by making a simple practice app. The main view of the app is a list where presents a title (in each cell) that can change by user input (text field). By selecting the cell, the app presents you the detail view where it presents another text.

I managed to change the cell´s title but I can’t figure out how to change the text in the detail view and make it stay that way. When I change the text in the detail view and go back to the main view, after entering again, the text doesn’t stay the same.

How can I make the text in the detail view maintain the text of whatever the user writes?

Byto H
  • 73
  • 8
  • In your Sandwish.swift file you need to use an `@Binding` instead of an `@State` so that you are modifying the object passed to it not it's own state version. – Upholder Of Truth Jul 02 '20 at 21:51

2 Answers2

2

Your Sandwish is a struct which means when you pass it around it's copied (See structure vs class in swift language). This also means that when you pass a sandwish:

CPCell(sandwish: sandwish)
...
struct CPCell: View {
    @State var sandwish: Sandwish
    ...
}

a sandwish is copied - any changes you make on this copy will not apply to the original sandwish.

When you do $sandwish.name in CPCell you're already binding to a copy. And in the NavigationLink you're copying it again.

struct CPCell: View {
    ...
    var body: some View {
        NavigationLink(destination: SandwishDetail(sandwish: sandwish)) {
            TextField("Record", text: $sandwish.name)
        }
    }
}

So in the SandwishDetail you're already using a copy of a copy of your original sandwish.

struct SandwishDetail: View {
    @State var sandwish: Sandwish // <- this is not the same struct as in `ContentView`
    ...
}

One way is to make Sandwish a class. Another, maybe better, solution is to use @Binding.

Note that the change from @State var sandwish to @Binding is not enough. CPCell expects the sandwish parameter to be Binding<Sandwish>, so you can't just pass a struct of type Sandwish.

One of the solutions is to use an index to access a binding from the SandwishStore:

ForEach (0..<store.sandwishes.count, id:\.self) { index in
    CPCell(sandwish: self.$store.sandwishes[index])
}
...
struct CPCell: View {
    @Binding var sandwish: Sandwish
    ...
}

Also you should do the same for all other places where the compiler expects Binding<Sandwish> and you originally passed Sandwish.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • Thanks very much for your answer, your explanation is really detailed. I just have one question. Your answer fixes the title problem of the cells, so I tried the same logic to the SandwishDetai view. But when I change in my SandwishDetail view the state for binding, it causes an error in the preview: Cannot convert value of type 'Sandwish' to expected argument type 'Binding'. How can I fix it? – Byto H Jul 02 '20 at 23:04
  • @BytoH I updated my answer to include more details. In short: you need to pass a *binding* and not just a `sandwish` which is just a *struct*. You can get this binding from the `SandwishStore` as presented above. – pawello2222 Jul 02 '20 at 23:15
  • 1
    Thank you very much for your help, I really appreciate the time you took to answer my questions. – Byto H Jul 02 '20 at 23:21
0

Change @State to @Binding in Sandwish.swift and in your CPCell struct in ContentView.swift