Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 829
Pedro Avatar asked Apr 26 '26 13:04

Pedro


1 Answers

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"?

like image 145
Lawliet Avatar answered Apr 28 '26 06:04

Lawliet