From f2fc1719143426a62817f6bc135877f064c17460 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 12 Aug 2022 15:40:16 -0500 Subject: [PATCH] updated for model binding Signed-off-by: Matt Bruce --- .../Resources/Components.storyboard | 27 ++----- .../RadioButtonViewController.swift | 43 ++++++------ .../ViewControllers/TestViewController.swift | 70 ++++++++++++------- 3 files changed, 67 insertions(+), 73 deletions(-) diff --git a/VDSSample/Supporting Files/Resources/Components.storyboard b/VDSSample/Supporting Files/Resources/Components.storyboard index a6f4b71..d21ccec 100644 --- a/VDSSample/Supporting Files/Resources/Components.storyboard +++ b/VDSSample/Supporting Files/Resources/Components.storyboard @@ -1,8 +1,9 @@ - + - + + @@ -487,7 +488,7 @@ - + @@ -594,25 +595,6 @@ - - - - - - - - - - - - - - @@ -645,7 +627,6 @@ - diff --git a/VDSSample/ViewControllers/RadioButtonViewController.swift b/VDSSample/ViewControllers/RadioButtonViewController.swift index 5c64700..1a264d3 100644 --- a/VDSSample/ViewControllers/RadioButtonViewController.swift +++ b/VDSSample/ViewControllers/RadioButtonViewController.swift @@ -26,12 +26,11 @@ class RadioButtonViewController: UIViewController, StoryboardInitable { @IBOutlet weak var labelTextField: UITextField! @IBOutlet weak var childTextField: UITextField! @IBOutlet weak var showErrorSwitch: UISwitch! - @IBOutlet weak var errorTextField: UITextField! // var radioButton: RadioButton! // var radioButton2: RadioButton! var radioButtonGroup = RadioButtonGroup() - @Published public var model = DefaultRadioButtonGroupModel() + public var model = DefaultRadioButtonGroupModel() public var subscribers = Set() override func viewDidLoad() { @@ -59,30 +58,33 @@ class RadioButtonViewController: UIViewController, StoryboardInitable { labelTextField.text = model1.labelText childTextField.text = model1.childText showErrorSwitch.isOn = model1.hasError - errorTextField.text = model1.errorText model.selectors = [model1, model2] radioButtonGroup.set(with: model) componentContainerView.addSubview(radioButtonGroup) + + //create the subject + let modelSubject = CurrentValueSubject(model) - $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { viewModel in - guard let selectedModel = viewModel.selectedModel else { return } - print("RadioButtonViewController selectedModel Id: \(selectedModel.id)") + //assign - this will auto overwrite any changes + modelSubject.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).assign(to: \.model, on: self).store(in: &subscribers) + + //bind + radioButtonGroup.createBinding(with: modelSubject, storeIn: &subscribers) + + //print out on subject changes + modelSubject.sink { [weak self] model in + + self?.showErrorSwitch.isOn = model.hasError + print("RadioButtonViewController hasError: \(model.hasError)") + + if let selectedModel = model.selectedModel { + print("RadioButtonViewController selectedModel Id: \(selectedModel.id)") + } }.store(in: &subscribers) - - radioButtonGroup.modelPublisher.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { viewModel in - guard let selectedModel = viewModel.selectedModel else { return } - print("radioButtonGroup.modelPublisher selectedModel Id: \(selectedModel.id)") - print("RadioButtonViewController selectedModel Id: \(self.model.selectedModel?.id)") - }.store(in: &subscribers) - -// radioButtonGroup.modelSubject.sink { [weak self] model in -// print("RadioButtonViewController model: \(model.selectedModel?.id)") -// print("RadioButtonViewController local.model: \(self?.model.selectedModel?.id)") -// }.store(in: &subscribers) - + radioButtonGroup.leadingAnchor.constraint(equalTo: componentContainerView.leadingAnchor, constant: 10).isActive = true radioButtonGroup.topAnchor.constraint(equalTo: componentContainerView.topAnchor, constant: 20).isActive = true radioButtonGroup.bottomAnchor.constraint(equalTo: componentContainerView.bottomAnchor, constant: -20).isActive = true @@ -114,11 +116,6 @@ class RadioButtonViewController: UIViewController, StoryboardInitable { radioButtonGroup.hasError = sender.isOn } - @IBAction func onErrorTextDidEnd(_ sender: UITextField) { - radioButton?.errorText = sender.text - sender.resignFirstResponder() - } - @IBAction func surfaceClick(_ sender: Any) { pickerType = .surface } diff --git a/VDSSample/ViewControllers/TestViewController.swift b/VDSSample/ViewControllers/TestViewController.swift index d97aaa1..4b1c8e9 100644 --- a/VDSSample/ViewControllers/TestViewController.swift +++ b/VDSSample/ViewControllers/TestViewController.swift @@ -8,8 +8,9 @@ import Foundation import UIKit import Combine +import VDS -class User: ObservableObject { +struct User { var firstName: String var lastName: String @@ -19,20 +20,29 @@ class User: ObservableObject { } } -class TextFieldCell: UIView { +public protocol Bindable { + associatedtype ModelType + var model: ModelType { get set } + var subject: CurrentValueSubject? { get set } + func createBinding(with subject: CurrentValueSubject, storeIn subscriptions: inout Set) +} + +extension Bindable { + public func send() { + subject?.send(model) + } +} + +class TextFieldBindingCell: UIView, Bindable { + //bindable + var model: User + var subject: CurrentValueSubject? + let firstNameTextField = TextField() let lastNameTextField = TextField() - var subject: CurrentValueSubject - private var subscriptions = Set() - var model: User - - private func send() { - subject.send(model) - } - - init(subject: CurrentValueSubject) { - self.model = subject.value - self.subject = subject + + init(model: User) { + self.model = model super.init(frame: .zero) translatesAutoresizingMaskIntoConstraints = false @@ -54,6 +64,16 @@ class TextFieldCell: UIView { lastNameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true lastNameTextField.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func createBinding(with subject: CurrentValueSubject, storeIn subscriptions: inout Set) { + self.model = subject.value + self.subject = subject + //add the logic let firstNameSubject = CurrentValueSubject(model.firstName) firstNameTextField.createBinding(with: firstNameSubject, storeIn: &subscriptions) @@ -71,18 +91,12 @@ class TextFieldCell: UIView { }.store(in: &subscriptions) } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } } - @objcMembers class TestViewController: UIViewController, Initable { + var user = User(firstName: "Joe", lastName: "User") - var nameTextField: TextFieldCell! - var button = UIButton() - private var cancellables = Set() + private var subscriptions = Set() required init() { super.init(nibName: nil, bundle: nil) @@ -97,8 +111,12 @@ class TextFieldCell: UIView { view.backgroundColor = .white let subject = CurrentValueSubject(user) - subject.assign(to: \.user, on: self).store(in: &cancellables) - nameTextField = TextFieldCell(subject: subject) + subject.assign(to: \.user, on: self).store(in: &subscriptions) + + let nameTextField = TextFieldBindingCell(model: user) + nameTextField.createBinding(with: subject, storeIn: &subscriptions) + + let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false @@ -120,21 +138,19 @@ class TextFieldCell: UIView { } }), for: .touchUpInside) } - - } extension UITextField { - func textPublisher() -> AnyPublisher { + public func textPublisher() -> AnyPublisher { NotificationCenter.default .publisher(for: UITextField.textDidChangeNotification, object: self) .compactMap({ ($0.object as? UITextField)?.text }) .eraseToAnyPublisher() } - func createBinding(with subject: CurrentValueSubject, + public func createBinding(with subject: CurrentValueSubject, storeIn subscriptions: inout Set) { subject.sink { [weak self] (value) in