1

The main class of my model is Concentration with a property called flipCount

class Concentration {
    @objc var flipCount = 0

In My ViewController I have an @IBOutlet to a UILabel called flipCountLabel

class ViewController: UIViewController {
    lazy var game = Concentration(numberOfPairsOfCards: self.cardButtons.count / 2)
    @IBOutlet weak var flipCountLabel: UILabel!
...

I want to update the text in flipCountLabel when flipCount changes. I am trying to do this by placing a call to the observe method of UIViewController in my ViewController's viewDidLoad method. There are a few observe methods. I don't know which one is right and cannot find an example for how to what to fill in the variables with.

I have tried to use self.observe like

    self.observe(KeyPath<Concentration,Int>(game, "flipCount"), changeHandler: {
        self.flipCountLabel.text = "Flip Count: \(flipCount)"
    })

I am trying to update the ViewControllers flipCountLabel.text every time game.flipCount is updated. I think this is kvo?

2
  • @Don I can't access flipCountLabel within the Concentration class. The IBOutlet is in the ViewController Commented Oct 22, 2019 at 22:54
  • Oh, sorry. Didn't read closely. Commented Oct 22, 2019 at 22:55

1 Answer 1

2

The observe method is the way to go here, be it with some changes to your code.

Change your Concentration class to the following:

class Concentration: NSObject {
    @objc dynamic var flipCount = 0
}

In order for KVO to work in Swift, properties need to be marked with both @objc and dynamic. The class needs to inherit from NSObject to prevent runtime errors.

Your ViewController class should look something like this:

class ViewController: UIViewController {
    @objc lazy var game = Concentration()
    @IBOutlet weak var flipCountLabel: UILabel!

    private var observation: NSKeyValueObservation?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.observation = self.observe(\.game.flipCount) { _, _ in
            self.flipCountLabel.text = "\(self.game.flipCount)"
        }
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        self.observation = nil
    }
}

The game property needs to be marked @objc (again to prevent runtime errors). Furthermore, the documentation of the observe method states

when the returned NSKeyValueObservation is deinited or invalidated, it will stop observing

This is why you need to keep a strong reference to the value returned by the observe method.

Also, consider using the KeyPath notation I used here. If you make a typo, it will fail when compiling, instead of at runtime.

I hope this helps you out!

Sign up to request clarification or add additional context in comments.

2 Comments

When I place @Objc in front of lazy var game = Concentration, I receive Property cannot be marked @objc because its type cannot be represented in Objective-C
That's why you need to make Concentration a subclass of NSObject

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.