0

I am quite new in Swift and SwiftUI and I am getting a error that I cannot understand.

Here is my class that I load with JSON from a web service

class ResaListViewModel: ObservableObject {
    @Published var resasDeparts = [ResaViewModel]()
    
    init() {
        WSResa().getDeparts { resas in
            if let resas = resas as? [Resa] {
                self.resasDeparts = resas.map(ResaViewModel.init)
            }
        }
    }
}

class ResaViewModel: Identifiable, ObservableObject {
    @Published var resa: Resa
    
    init(resa: Resa) {
        self.resa = resa
    }
    
    var id: Int {
        return Int(self.resa.id)!
    }

 var nbveh: Int
    {
        return Int(self.resa.nbveh) ?? 0
    }
}

In a view I would like to Bind the nbveh property and to increment and decrement it with buttons.

the error is : "Cannot assign to property: 'nbveh' is a get-only property"

struct ResaDetail: View {
    @ObservedObject var resa:ResaViewModel
    
    var body: some View {
      vehiculeView(nbveh: $resa.nbveh)
}

struct ResaView: View {
    let resaListVM = ResaListViewModel()
    var body: some View {
        List(self.resaListVM.resasDeparts, id: \.id) { resa in
            NavigationLink(destination: ResaDetail(resa: resa)) {
                ResaList(resa: resa)
            }
        }
    } .navigationBarTitle(Text("Réservations"))
}

How can I make this property editable and bindable ?

Thanks for any kind of help

-- Godefroy

Asperi
  • 228,894
  • 20
  • 464
  • 690
Godefroy
  • 46
  • 4

2 Answers2

1

And the answer to send the message when it is changed and to reload the UI is :

get { return Int(resa.nbveh) ?? 0 }
    set { resa.nbveh = String(newValue)
        objectWillChange.send()
    }

Thanks for your help

Godefroy
  • 46
  • 4
0

The issue is that the nbveh property in your ResaViewModel is a computed property. A Binding in SwiftUI needs to be able set and get the value. Because of the way computed properties work, you can only get them, and not set them.

The solution would be to change the nbveh to this:

var nbveh: Int {
    get { return Int(resa.nbveh) ?? 0 }
    set { resa.nbveh = newValue }
}

This tells Swift to execute the code in get { } whenever you're reading the value, and it will execute set { } when setting nbveh. This way you'll be able to it as a Binding.

A different solution could be to get rid of your nbveh var in your ResaViewModel and simply use the nbveh property of resa in ResaViewModel. This would look like this:

vehiculeView(nbveh: $resa.resa.nbveh)
milo
  • 936
  • 7
  • 18
  • Hi Thanks for your help. I have tried both options. And with the get and set the problem is that if update the value it does not update the view. I should probably add an objectWillChange somewhere. I tried to add the property @Published to the nbveh attribute, but it does not apply on computed value. The second option work but feels like the resaViewModel become useless – Godefroy Aug 25 '20 at 21:31
  • I can see that you have nested `ObservableObject`s, which is not supported yet, that could be part of your problem why your view does not update. See here for the solution: https://stackoverflow.com/questions/58406287/how-to-tell-swiftui-views-to-bind-to-nested-observableobjects – milo Aug 26 '20 at 07:31
  • I, I found the solution by adding when I set the object attribute objectWillChange.send() – Godefroy Aug 27 '20 at 00:17