Flow of the program depends on the value of the various variables we use in our code. Depending on the values of the variables in our program, we navigate the flow of execution as we needed / required.
For example if we are using any collection types, it is hard to perform logic when the collection is modified or changed. i.e when new items are added, deleted, or modified. We can still manage this kind of scenarios in many ways.
One of them is using Notification-Centre, To notify us whenever the change occurs in property value. But if want to check on many properties in our code, this (using Notification Centre) way lead us to heavy chunk of code, to call for all those properties we need to check on. The Other way which is better In this kind of scenarios is (also Apple using this in its libraries a lot) known as KVO(Key Value Observing), which is also directly related to another powerful mechanism called KVC(Key Value Coding).
Note: Any property we want to observe for changes must be KeyValueCoding (KVC)complaint.
Both of these KVO & KVC, provide an efficient way to write our code. Now lets get started with KVC and then KVO.
KVC is a form of coding that allows you to access an object’s properties indirectly, using strings to access them instead of the property’s accessors or instead of accessing the variables directly. To enable such mechanism, your classes must comply to the NSKeyValueCoding informal protocol. (OR)
Key-value coding is a mechanism for accessing an object’s properties indirectly, using strings to identify properties, rather than through invocation of an accessor method or accessing them directly through instance variables.
Ex: class Profile: NSObject {
var firstName: String
var lastName: String
var customProfile: Profile
}
If we want assign the values for the above declared variables, in the class init() or somewhere in the class file, normally we do like the following:
self.firstName = “Robert”
self.lastName = “Stark”
With KVC; we do like the following:
self.setValue: “Robert” for key: “firstName” //self.setValue: Any for key: key/KeyPath
self.setValue: “Stark” for key: “lastName”
To retrieve the values of KVC properties we use like the following:
let robertLastName = self.value( forKey: “lastName” )
The above KVC working way is like working with Dictionaries in swift. Right?
Here, In KVC; Instead of directly assigning the values to properties, or using (#If available) setter methods of objects, we are doing in a way simply assigning values to keys/keyPaths. So we use keys and values, this technique is called Key Value Coding(KVC).
Note: There is protocol known as NSKeyValueCoding informal protocol, which is compulsory to work with KVC. Our classes must be confirmed to this protocol, in order to use KVC & KVO. NSObject confirms to this protocol. So every class that is defined in the Foundation framework and that inherits from NSObject conforms to the NSKeyValueCoding protocol.
Key : simply “Key” specifies a single property, the one we want to set a value or get one from. So its name should be the same as the property’s name.
Ex: self.setValue: “Stark” for key: “lastName”
KeyPath : A KeyPath is formed with the dot-syntax by following the substrings, so it is not a single word/string. Key-path represents all the properties of an object, which comes in the way until to reach the desired value/property.
Ex:
var myProfile: Profile
self.setValue: “Baratheon” for key: “myProfile.customProfile.lastName”
KVC :
→ In swift, there are many keywords/ attributes to help the compiling, runtime specifications, access controlling etc. Ex: @escaping, @available, etc. Likewise, It also defines a range of declaration modifiers, to modify the declarations of properties/class-members. For example, by marking a class declaration with the‘ final’ keyword, we inform the compiler that the class cannot be subclassed. This allows the compiler to make a number of optimisations to increase performance. ‘dynamic’ is also a declaration modifier we use in swift.
Dynamic dispatch, is one of the cool features in Objective-C. It simply means that the Objective-C runtime decides at runtime which implementation of a particular method or function it needs to invoke. For example, if a subclass overrides a method of its superclass, dynamic dispatch figures out which implementation of the method needs to be invoked, that of the subclass or that of the parent class. This is a very powerful concept.
Swift uses the Swift runtime whenever possible. The result is that it can make a number of optimisations. While Objective-C solely relies on dynamic dispatch, Swift only opts for dynamic dispatch if it has no other choice. If the compiler can figure out at compile time which implementation of a method it needs to choose, it wins a few nanoseconds by opting out of dynamic dispatch.
Swift runtime chooses other options, such as static and virtual dispatch, over dynamic dispatch whenever possible. It does this to increase performance.Static and virtual dispatch are much faster than dynamic dispatch. Even though we are talking nanoseconds, the net result can be dramatic. Many features we have come accustomed to are only possible because of the dynamic Objective-C runtime, including Core Data and Key-Value Observing.
By applying the ‘dynamic’ declaration modifier to a member of a class, you tell the compiler that dynamic dispatch should be used to access that member.
By prefixing a declaration with the ‘dynamic’ keyword, the declaration is implicitly marked with the objc attribute. The objc attribute makes the declaration available in Objective-C, which is a requirement for it to be dispatched by the Objective-C runtime.
‘dynamic’ declaration modifier can only be used for members of a class. Structures and enumerations don't support inheritance, which means the runtime doesn't have to figure out which implementation it needs to use.
So to use KVC & KVO in swift, for the properties we want to observe in KVO we need to declare them with @objc dynamic keyword.
First we use of ‘child1’ object. In the viewDidLoad() method, we initialise the child1 object, and then assign the values to its properties.
If you print the‘ child1’ objects properties like name and age you will get the assigned values in result.
Now, we will use the KVC methods to do the same thing.
In the above snippet, In the first couple of rows we set the desired values to both properties using the setValue:forKey: method. observe the key strings are the same to the properties’s names.
Next, we perform the exact opposite task. We extract the values out of the properties using the valueForKey: method, and we assign them to two local variables. And then we printed the values in the console. The result is the same as the previous one.
Note: If we give keys different to the property names, The app will crash. When writing KVC-compliant code it’s really important to take care so the key strings actually match to the property names, otherwise the app will simply fall apart. This is not a case when dealing directly with properties, as there’s no chance to do any mistake on their names; the compiler would throw an error in such a case which would drive us to fix it.
With all the above, we have managed to see how to write KVC-styled code, and how to set and get values using keys. In the next part we will complete the project.
working with key-paths:
Now go to the Children class, and add the following property.
@objc dynamic var child: Children?
in the viewDidLoad method. Now, add the next lines that initialise the related objects and assign their initial values:
In the above snippet, in the first couple of rows we just initialised the‘ _child2 ’_object and its ‘child’ property. Next we set the values to the properties name and age of child2. For the child property of child2, we used key-path to set the values. Observe closely. we can retrieve the values to check if the assignment was successful: Uncomment the print statements for that.
Now we will see what if the child of the child has a child too…
If you want to check the values/result use the print statements for the above snippet.
So far, we learned how to write KVC complaint code using keys and key-paths. Next we will learn Observing the Property’s value changes:
KVO :
Here we will see what actions should be taken in order to be able to track down changes on properties. First of all, let me introduce you as a list the steps needed to implement KVO:
1. The class of which you want to observe its properties must be KVO compliant. That means:
2. The class that will be used to observe the property of another class should be set as observer.
3. A special method named observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) should be implemented to the observing class.
Let’s see everything one by one. The most important thing when we want to observe for changes of a property, is to make our class observe for these changes. This is done more or less with as with the casual notifications (NSNotifications), but using another method. This method is the addObserver(<#T##observer: NSObject##NSObject#>, forKeyPath: <#T##String#>, options: <#T##NSKeyValueObservingOptions#>, context: <#T##UnsafeMutableRawPointer?#>)
Here we observe the value changes for name and age properties of a child1 object. So in viewWillAppear() method, add the observers for the child1 object.
The parameters the above method accepts are:
Now that we have made our class able to observe for any changes in the above two properties, we must implement the observeValueForKeyPath:ofObject:change:context: method. Its implementation is mandatory, and it has one great disadvantage. That is the fact that is called for every KVO change, and if you observe many properties then you must write a lot of if statements so as to take the proper actions for each property. However, this can be easily overlooked as the benefits of the KVO are greater than this limitation.
The above method will be called every time change in the value of properties which are added to the observer. Here by key-path parameter, we printed the new and old values of the name and age properties.
To test this functionality, wire-up an action for a UIButton to your viewController, and in that action method add the following lines of code:
Super! After we have set a new value to the name and age properties of the child1 object, we received the notification, and the messages we asked for to be displayed were shown on the debugger. As you see, both the previous and the new value are included in the dictionary.
From the change dictionary you can extract any value you want (if needed), but the most important of all is that it’s super-easy to be notified about changes in properties.
add the following lines of code in the button action method and run the program.
If you observe the result, we receive two notifications regarding changes to the age property. But that seems confusing, because even though we know the object that each notification belongs to, programmatically we can do nothing to determine the object that sent the notification. So, how do we face that, and how can we programmatically be a 100% sure about the object that the changed property belongs to?
The answer to the above question is one: We will make use of the context argument of the addObserver(<#T##observer: NSObject##NSObject#>, forKeyPath: <#T##String#>, options: <#T##NSKeyValueObservingOptions#>, context: <#T##UnsafeMutableRawPointer?#>) method. I have already mentioned before that the purpose of the context is to uniquely identify a change on a property, so it’s the best tool we have at our disposal.
Here I defined two contexts for each child1 and child2:
Note that the context value for each observed property must be a global variable, because it has to be accessible from both the addObserver… and the observeValueForKeyPath… methods.
Now modify the code in child1 and child2 add observer methods, by passing the context parameter. And then we need to modify the observe method as well like the following: This way we can identify the changed property with help of passed context parameter. So we managed to programmatically specify each changed property.
Lastly and before we reach at the end of this chapter, it’s also quite important at some point to remove the observers you add. There is not a recipe on where you should do that. For instance, in many cases it would be useful to do that in the observeValueForKeyPath:ofObject:change:context:, after you have handled a received notification. In other cases, you should do so upon the dismissal of a view controller. Generally, it’s up to your application’s structure the decision you will make about that. In this example, we will do it in the viewWillDissapear: method. Here it is: For child2, I removed observer in the above snippet.
By default, the system sends a notification every time a property gets changed when you observe using KVO. This is suitable in most cases, however there are times that we don’t want to get a notification once a change has happened, but after a bunch of changes have taken place in multiple properties or at a later time. Thankfully, iOS SDK provides us with some quite convenient methods that gives us control over the notifications, so we can manually send them whenever it’s actually needed. Before we get into more details, let me just say that using the method you’ll see right next is not mandatory. On the contrary, you may implement it if and when it is really necessary.
Getting into the point now, in order to control the notifications that are sent upon property changes, you must implement the _automaticallyNotifiesObserverForKey:_class method. The parameter it accepts is a string representation of the key of the property for which you need to control the notification, and it returns a boolean value. In case that you don’t want a notification to be sent after the observed property’s value has been changed, then the method must return false. In any other case, you should let iOS decide about the notifications.
In practice, let’s suppose that we don’t want a notification to be posted when the name property of the Children class get changed. With that in mind, here’s the implementation of that method in the Children class
In the else clause, we call the same method using the super class in order to let iOS handle all the keys that we haven’t explicitly added here, and the value that we get back is the one that we return at the end.
If you run the app at this point, you’ll find out that no message regarding the name changing is appeared on the debugger. Of course, this is what we desire, so we’ve managed to achieve our goal. But, have we really done it?
Well, as you understand by returning false in the above method for the specific key, we’ve only managed to stop the relevant notifications from being sent. And of course, this doesn’t mean manual notifications, it means no notifications at all! In order to send a notification when we decide so, we must use two other methods. These are the willChangeValueForKey: and the didChangeValueForKey:. When using them, the willChangeValueForKey: must be called first, then the new value must be assigned to the property, while the didChangeValueForKey: should be called at the end. To test the manual actions, add these lines in the action method of viewcontroller.
If you run the app now, the message regarding the name change is appeared on the debugger, and that means that we’ve sent the notification manually with success!
Actually, the notification is sent after the didChangeValueForKey: method is invoked, therefore place that method wherever you feel appropriate for receiving notification.
That’s what exactly we have been expecting from the app! As you can see, we’ve managed to control the notification sending point, and all that with a little effort! Note that between the willChangeValueForKey: and the didChangeValueForKey: methods, you can have more than one property values assigned.
The willChangeValueForKey: and the didChangeValueForKey: methods are not mandatory to be used as shown above. They can be implemented in the Children class (or in your own class) as well.
Please click here for the project describing above tutorial.
References:
If you like my tutorials please follow me on medium. You can contact me through my-twitter or linkedIn accounts.
Thanks for reading. Will get better soon with more tutorials.
See you!!!