Is key-value observation (KVO) available in Swift?


In Swift, you can use KVO to observe changes to an object property by registering an observer for that property. When the property value changes, the observer is notified and can take appropriate actions. In this article, you will see an example of how to implement KVO in Swift.

To use KVO in Swift, you need to do the following

  • Mark the property that you want to observe with the @objc dynamic attribute. This attribute tells the Swift compiler to generate Objective-C-compatible code for the property.

  • Register an observer for the property using the addObserver(_:forKeyPath:options:context:) method of the observed object.

  • Implement the observeValue(forKeyPath:of:change:context:) method in the observer to handle changes to the observed property.

addObserver(_:forKeyPath:options:context:) method

The addObserver(_:forKeyPath:options:context:) method is used to add an observer for a specific key path of an object. It takes the following parameters −

  • observer − The object that is observing the key path.

  • keyPath − A string that specifies the key path to observe.

  • options − An array of options that determine how changes to the key path are reported to the observer.

  • context − A context object that is passed to the observer when it is notified of changes.

The options parameter is an array of options that determine how changes to the observed key path are reported to the observer. The available options are −

  • .old − Include the old value of the property in the change dictionary when a change is reported.

  • .new − Include the new value of the property in the change dictionary when a change is reported.

  • .initial − Report the initial value of the property when the observer is added.

  • .prior − Include the previous value of the property in the change dictionary when a change is reported.

The context parameter is an arbitrary context object that is passed to the observer when it is notified of changes. This can be used to provide additional information to the observer.

removeObserver(_:forKeyPath:) method

The removeObserver(_:forKeyPath:) method is used to remove an observer for a specific key path of an object. It takes the following parameters −

  • observer − The object that is observing the key path.

  • keyPath − A string that specifies the key path to stop observing.

It is imperative to remove observers when they are no longer needed. This is to avoid memory leaks and to prevent the observer from receiving notifications after it has been deallocated.

@objc dynamic

@objc dynamic is a Swift language attribute that is used to make properties of a class observable with key-value observing (KVO) in Objective-C. KVO is a mechanism that allows objects to observe changes to a property of another object.

The @objc attribute is used to expose a Swift class or its members to Objective-C so that they can be used by Objective-C code. The dynamic attribute is used to tell the compiler to use dynamic dispatch instead of static dispatch for a property or method. This means that the property or method can be overridden at runtime.

In order for a property to be observable with KVO, it must be marked with both @objc and dynamic. When a property is marked with @objc dynamic, the compiler generates additional code that enables KVO for that property. This includes creating a new dynamic subclass of the class that overrides the property's getter and setter methods to notify registered observers when the property value changes.

Example

import Foundation
class Person: NSObject {
   // Declare the property that you want to observe as @objc dynamic in the class where it is defined.
   @objc dynamic var name: String = ""
}
// Define a new observer class that inherits from NSObject.
class PersonObserver: NSObject {
   var person: Person
   init(person: Person) {
      self.person = person
      super.init()
      person.addObserver(self, forKeyPath: #keyPath(Person.name), options: [.old, .new], context: nil)
   }
   deinit {
      person.removeObserver(self, forKeyPath: #keyPath(Person.name))
   }
   override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
      guard keyPath == #keyPath(Person.name),
      let newValue = change?[.newKey] as? String,
      let oldValue = change?[.oldKey] as? String else {
         return
      }
      print("Property '\(keyPath!)' changed from '\(oldValue)' to '\(newValue)'")
   }
}
// Create an instance of Person and an instance of PersonObserver.
let alex = Person()
let observer = PersonObserver(person: alex)
// Change the value of the name on alex
alex.name = "Alex"
alex.name = "Alex Murphy"

Output

Property 'name' changed from '' to 'Alex'
Property 'name' changed from 'Alex' to 'Alex Murphy'

In this example, Person is the observed object and PersonObserver is the observer. The name property of Person is marked with the @objc dynamic attribute to enable KVO. The observer is registered for the name property of alex and implements the observeValue(forKeyPath:of:change:context:) method to handle changes to the property. When the property value changes, the observer's method implementation is called with the changed value.

Conclusion

In conclusion, @objc dynamic is a Swift language attribute that is used to make properties of a class observable with key-value observing (KVO) in Objective-C. It is important to mark a property with both @objc and dynamic in order for it to be observable with KVO. When a property is marked with @objc dynamic, the compiler generates additional code that enables KVO for that property. It is recommended to only use @objc dynamic when the property needs to be observable with KVO in Objective-C, as using it unnecessarily can impact performance.

Updated on: 04-May-2023

962 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements