swiftmvvmkey-value-observing

Simple MVVM with KVO


I'm trying to create a simple mvvm model using kvo

My goal is when the UITextField text changes, automatically change the UILabel text. But for some reason the observeValue function is not called

import UIKit
class ViewController: UIViewController, UITextFieldDelegate {

    var viewModel : TestViewModel?
    @IBOutlet weak var LBLABEL: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel = TestViewModel()

        addObserver(self, forKeyPath: #keyPath(viewModel.infoText), options:  [.old, .new], context: nil)

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        viewModel?.infoText = textField.text
        return true
    }


    // MARK: - Key-Value Observing
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "info" {
            // Update Time Label
            LBLABEL.text = viewModel?.infoText
        }
    }

}


class TestViewModel : NSObject{
    var model : TestModel

    var infoText:String? {
        didSet{
            model.info = self.infoText

        }
    }

    override init() {
        model = TestModel()
    }
}


class TestModel {
    var info:String?
}

I already tried to change the observer's declaration or even the ViewModel gets and sets and never succeeded


Solution

  • UPDATE

    According to Apple docs, creating an observer for the key path will be much simpler in Swift 4.

    class MyObserver: NSObject {
        @objc var objectToObserve: MyObjectToObserve
        var observation: NSKeyValueObservation?
    
        init(object: MyObjectToObserve) {
            objectToObserve = object
            super.init()
    
            observation = observe(\.objectToObserve.myDate) { object, change in
                print("Observed a change to \(object.objectToObserve).myDate, updated to: \(object.objectToObserve.myDate)")
            }
        }
    }
    
    let observed = MyObjectToObserve()
    let observer = MyObserver(object: observed)
    
    observed.updateDate()
    

    You need to add dynamic to the properties of NSObject subclass you want to observe. In your case:

    @objc dynamic var infoText:String? {
        didSet{
            model.info = self.infoText            
        }
    }
    

    Btw, I don't know why you want to you textField:shouldChangeCharactersIn as it gets the textfield value before updated. Also, keyPath == "info" won't never be true. Shouldn't it be something else. E.g., keyPath == "viewModel.infoText"?