MVVM + RxSwift 코드를 Rx 대신 KVO(Key-Value Observing) 로 변경해 보았다.
// ViewController class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. if let v: View = UIView.loadFromNib() { v.add(self.view) } } }
// View class View: UIView { @IBOutlet var label: UILabel! private let viewModel = ViewModel() private var ob: NSKeyValueObservation! override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func awakeFromNib() { super.awakeFromNib() // ViewModel의 output.label 값이 변경되는 것을 감시하여 변경되면 View의 label을 업데이트 ob = viewModel.output.observe(\.label) { [weak self] obj, _ in guard let self = self else { return } self.label.text = obj.label } viewModel.loadLabel() } func add(_ superView: UIView) { superView.addSubview(self) self.translatesAutoresizingMaskIntoConstraints = false var constraints = [NSLayoutConstraint]() constraints.append(superView.topAnchor.constraint(equalTo: self.topAnchor)) constraints.append(superView.leadingAnchor.constraint(equalTo: self.leadingAnchor)) constraints.append(superView.trailingAnchor.constraint(equalTo: self.trailingAnchor)) constraints.append(superView.bottomAnchor.constraint(equalTo: self.bottomAnchor)) NSLayoutConstraint.activate(constraints) } } extension UIView { class func loadFromNib<T>() -> T? { let identifier = String(describing: T.self) let view = Bundle.main.loadNibNamed(identifier, owner: self, options: nil)?.first return view as? T } }
// Model struct Model { var label: String = "model" }
// ViewModel class ViewModel { class Output: NSObject { //View에서 observe 하려면 NSObject 상속해야 함 @objc dynamic var label: String = "" } var output = Output() private var m = Model() init() { } func changed() { DispatchQueue.main.async { self.output.label = self.m.label } } func loadLabel() { output.label = "init" DispatchQueue.global().asyncAfter(deadline: .now()+2) { self.m.label = "value from server or app db" self.changed() } } }