Subha_26
Subha_26

Reputation: 450

Array of binding variables for multiple toggles in MVVM pattern in SwiftUI

I have a simple use case of having a VStack of a dynamic number of Text with Toggle buttons coming from an array.

import SwiftUI

public struct Test: View {
        
    @ObservedObject public var viewModel = TestViewModel()
    
    public init() {
        
    }
    
    public var body: some View {
        VStack {
            ForEach(viewModel.models) { model in
                ToggleView(title: <#T##Binding<String>#>, switchState: <#T##Binding<Bool>#>)
                //how to add the above
            }
            
        }.padding(50)
    }
}

struct ToggleView: View {
    
    @Binding var title: String
    @Binding var switchState: Bool
    
    var body: some View {
        VStack {
            Toggle(isOn: $switchState) {
                Text(title)
            }
        }
    }
}

public class TestModel: Identifiable {
    
    @Published var state: Bool {
        didSet {
            //do something
            //publish to the view that the state has changed
        }
    }
    @Published var title: String
    
    init(state: Bool, title: String) {
        self.state = state
        self.title = title
    }
}

public class TestViewModel: ObservableObject {
    
    @Published var models: [TestModel] = [TestModel(state: false, title: "Title 1"), TestModel(state: true, title: "Title 2")]
   
}

The following questions arise:

  1. In MVVM pattern, is it ok to have the binding variables in model class or should it be inside the view model?
  2. How to send the message of state change from model class to view/scene when the toggle state is changed?
  3. If using an array of binding variables in view model for each of the toggle states, how to know which particular element of array has changed? (see the following code snippet)
class ViewModel {

    @Published var dataModel: [TestModel]


    @Published var toggleStates = [Bool]() {
        didSet {
            //do something based on which element of the toggle states array has changed
        }
    }
}

Please help with the above questions.

Upvotes: 2

Views: 1148

Answers (1)

Martin
Martin

Reputation: 1133

One way you could achieve what you desire is to use the binding power of @ObservedObject. The trick is to use indexes to reach the array elements for you binding. If you loop on the array elements model directly you loose the underlying binding properties.

struct Test: View {

@ObservedObject public var viewModel = TestViewModel()

var body: some View {
    VStack {
        
        ForEach(viewModel.models.indices) { index in
            ToggleView(title: self.viewModel.models[index].title, switchState:  self.$viewModel.models[index].state)
        }
        
    }.padding(50)
  }
}

class TestViewModel: ObservableObject {
    @Published var models: [TestModel] = [
        TestModel(state: false, title: "Title 1"),
        TestModel(state: true, title: "Title 2")]
}


struct ToggleView: View {

    var title: String
    @Binding var switchState: Bool

    var body: some View {
        VStack {
            Toggle(isOn: $switchState) {
                Text(title)
        }
      }
   }
}

class TestModel: Identifiable {

    var state: Bool
    var title: String

    init(state: Bool, title: String) {
        self.title = title
        self.state = state
    }
}

Hope this does the trick for you.

Best

Upvotes: 2

Related Questions