Observers and Delegates are famous design patterns. And they are most easy to be confused about. This article explains basics of Observers vs Delegates.
While writing iOS apps, a good dev always follow design pattern. The reason many devs won’t do is – mobile development is considered more as a hobby than a business – the very reason it has attracted indie community. If an app studio is a shopping mall, indie dev is a shopkeeper on the street. To enforce his independent identity he would love to brag about his non-conformity to established norms.
However, writing robust apps is not a choice but a pre-requisite. Every failed indie dev realizes this easy or hard way. Practices such as Massive View Controllers (MVC?) and copy-paste code would render themselves obsolete as languages like Swift evolve with time. But while they do, there are certain easy to follow rules that every dev can follow to make their living better – especially amid constant flux of changes and upgrades in device software space.
Design patterns are such subset of rules that no one will repent following. The most common types used my many devs are obviously observers and delegates. It is quite easy to intermingle the two.
Both Observer & Delegate design patterns come into picture when interaction between two objects is involved. They both are useful when one object needs to perform certain task when the other object wants it to perform it.
Observers are – let me guess – your twitter followers. Any updates you post, they all get it in their timelines. The reverse isn’t true however – you don’t get to know about them. The flow of information is unidirectional.
Delegates are – stop being pluralistic. A delegate is like a friend in need. He does his assigned task and resigns gracefully. There can be only one delegate for certain type of task.
Observer vs Delegate:
Both observers & delegates work on some common premises of one thing – the object who is notifying (notifier) and the object being notified. Having fixed this terminology, it is easy to understand the difference.
In observer pattern, there can be single notifier and multitude of objects being notified. No matter which classes those objects implement, no matter how many instances of them being alive, they all can be notified of a change that their notifier object is eager to report.
In Apple’s world, a widely-implemented example of an observer is NSNotificationCenter’s postNotification functionality. Using postNotification, one can notify as many objects (irrespective of their type) at once. An object can receive a notification as long as it has registered itself as an observer to certain notification type. This notification type is the single bond between the notifier and the one being notified. The objects do not know each other at all.
Another well-implemented Observer example is SKPaymentTransactionObserver. This is not a class but a protocol. Whichever object in your app implements this protocol (by implementing its mandatory updatedTransactions method) – that object automatically becomes an observer for any notifications that storekit has for the transactions. Storekit has no knowledge of your in-app purchase class. Your app should tell it at the time of launching through [[SKPaymentQueue defaultQueue] addTransactionObserver:<Your Observer Instance>], and at runtime storekit will notify your observer about all transaction status changes – via updatedTransactions.
Delegates do the same tasks as observers do – perform an action in response to a change. But instead of performing things themselves, the object responsible for change must invoke them. The notifier object already knows (at compile time) who is it’s delegate – at runtime it invokes certain delegate method (implemented through the delegate protocol) and performs the task. The method definition lies with delegate class definition, but the invocation happens from the notifier.
Do I need to remind you of UITableView and its relatives? UITableViewDelegate protocol’s didSelectRowAtIndexPath is always implemented inside your table view controller. But you do not invoke it directly. When you select a row on table view, the table view, and not your view controller, knows about it first hand. UITableView’s _selectRowAtIndexPath is directly invoked through selection. UITableView then asks about it’s delegate’s implementation of didSelectRowAtIndexPath. If this implementation exist, it is invoked. If not, nothing happens and table view goes about its usual business.
But who is the delegate? Your own view controller. How did table view know about it? In other words, who established this relationship? The following statement in your code (written in viewDidLoad or somewhere similar – or connected via Storyboard):
_mytTableView.delegate = self;
XCode compiler ensures that whoever is self (your view controller) – that class should implement UITableViewDelegate for the above statement to hold true. The notifier knows in advance who their delegate is, from the time of compilation.
Observer vs Delegate: What to use & when:
When you want anonymity about the object being notified, use observer design pattern. An observer must know who it is observing, but the reverse is not true. You can have single notifier and multiple observers in this pattern.
Notifications are easy to implement from programming perspective because all they require is posting notifications to anonymous observers, and observers declaring their own notification handlers themselves, like this:
[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationPosted) name:kNotificationName object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kNotificationName object:nil];
One thing to take care of while doing this is to remove observers as and when their need to observe changes is over. NSNotificationCenter has removeObserver set of methods just for this purpose. Observer notifications to non-existing observers cause significant performance decay.
When you want tight coupling between the notifier and the object being notified, use delegate design pattern. Delegates also require that there is one-to-one relationship between object being notified and the notifier object. From programming complexity standpoint, delegates are harder to implement, but issues related to it are easy to debug because they are easily traceable.
It’s easy to ignore Design patterns and code your way in haste to make your app public. It’s easy to define as many properties as you want and use them at leisure wherever required. However, as you go along and when your code requires modifications, or when you assign your development tasks to someone else (delegation!) – code reusability and extensibility becomes necessity. Efforts invested in following design patterns justify themselves when such benefits are reaped.