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()
}
}
}