Model changes observing in Swift

Alexander Ruzmanov
4 min readMay 4, 2019

--

Sometimes you want to observe model (or some else module) changes performed by another view controller. For example, if your initial view controller have audioplayer (it plays soundtrack), and then user opens settings screen and changes sound volume. So, in this case, you should notify initial view controller, that sounds settings changed, and it will ask audio player to set appropriate volume.

But in this article I would like to give examples for a bit different application. It has two screens: first screen has one button and one label, second screen has one button and date picker.

If user push first screen button it will perform segue with identifier “ShowSecondVC”, which destination is the second screen (SecondViewController). First screen’s label (called dateDescriptionLabel) displays info about chosenDate (one and only variable in Model). FirstViewController gets link to the Model class object for SecondViewController using prepare(for segue:, sender:) method. If user changed date picker value on the second screen, then SecondViewController changes chosenDate value in Model. If user pushed the button on the second screen, it will dismiss.

Application scenes
FirstViewController
SecondViewController
Model

Well, it looks awesome! But when first VC comes back to the screen, dateDescriptionLabel doesn’t change its text. Of course, you could call updateViewFromModel() method in viewWillApper() lifecycle function or you can use unwind segue in this case. However, if you have deal with sounds (for instance) and you have to change volume before first screen will get back, then you can chose between: delegation, notifications and closure.

DELEGATION

Lets start with delegation. First of all, you should create protocol contains requirements for your model’s delegate. After that you can add optional weak variable (you can use unowned instead of weak, but in this case you should set delegate during model initialisation) in your model (called delegate), whose type is your protocol type.

Model with its delegate

Now you can call some methods from your delegate in appropriate time. For example, perform function chosenDateDidChange() after chosenDate has been set in didSet. But that’s not all. If you want to update your first scene view using delegate, you should implement ModelDelegate protocol in FirstViewController, and set self as model.delegate value.

FirstViewController using delegation

NOTIFICATIONS

The second way to do the same thing is using notifications. Notifications work as radio stations: when some object post notification many other can receive it if they have an observer. Every notification “radio station” has name. So lets create one for our chosenDate changes. I created it using extension for Notification.Name struct.

Posting notifications

When you have created name for your “radio station”, you could post notifications: just ask NotificationCenter.default to do it for you. NotificationCenter has three post methods (details you can find in documentation: https://developer.apple.com/documentation/foundation/notificationcenter).

So, we post notifications, but there are not “listeners” for our “radio station”. Lets add one! Go to FirstViewController and create NSObjectProtocol? type observer variable. In viewDidLoad() method set value for observer using addObserver in NotificationCenter.default. There are two methods for adding and two methods for removing observer in NotificationCenter (documentation: https://developer.apple.com/documentation/foundation/notificationcenter). Don’t forget to remove observer from NotificationCenter (it is not important in given example, but it needs to be removed if you view controller loads periodically when your app is running, because NotificationCenter save link to observer even if your view controller has gone).

FirstViewController using notifications

CLOSURES

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages (https://docs.swift.org/swift-book/LanguageGuide/Closures.html).

In given example you can use closure instead of delegation or notifications. Just add (()->())? type variable called chosenDateDidChangeAction to your Model. And perform it whenever you want!

Using closure in model

Lets set chosenDateDidChangeAction value in FirsViewController’s viewDidLoad() method.

Closure implementation in FirstViewController

CONCLUSION

You can use every of this three ways. Delegation is widely used in UI events handling (UITableView, UICollectionView, UIDragInteraction, UIDropInteraction and etc). In MVC architectural pattern when you want notify your view controller about changes in model, your will use notifications. Sometimes it is easiest way to use single closure instead of delegation (for instance, if you have cells with button inside, you can just create pushButtonAction variable and set its value in tableView(_:cellForRowAt:)).

Thank you for reading :) If I made a mistake, please, let me know!

--

--