Friday, April 20, 2018

Notification Protocols

NSNotificationCenter is straightforward mechanism for implementing a clean MVC architecture, but it is also somewhat cumbersome and error-prone. The receiving object has to register itself with the notification center for the particular notification, specified by a string. It also has to implement a method for handling the notification, and the registration and the method have to match. The string used has to be coordinated with the senders of the notification, with no checking whatsoever. The compiler doesn't help with the manual bookkeeping and there is no indication in the interface that the class receives notifications.

Let's fix this with Notification Protocols.

Notification Protocols: Usage

First, let's define a protocol for our notification. We want a ModelDidChange notification. We also want common message to be sent to our objects, in this case the message -modelDidChange:.
@protocol ModelDidChange <MPWNotificationProtocol>

-(void)modelDidChange:(NSNotifiction*)notification;

@end

This protocol must contain a single message and conform to MPWNotificationProtocol, which indicates that this is a notification protocol. This way, we define that a particular notification always maps to one specific message, but this is common and should probably be considered a best-practice.

Second, a class wishing to adopt this notification must conform to the protocol:


@interface NotifiedView:NSView <ModelDidChange>

@end

NotifiedView must implement -(void)modelDidChange:(NSNotificaiton*)notification;, but fortunately the compiler will tell us if we forget to do this, because of the protocol conformance declaration. Finally, and this is the part that can't really be checked, the object must call [self installProtocolNotifications] somewhere in its initializer. It's probably best to do this in a common superclass.

Then, just use the PROTOCOL_NOTIFY macro to send the notification. The macro takes the name of the protocol as its argument. It uses the @protocol compiler directive to turn that into the protocol and then uses the protocol's name for the notification.


PROTOCOL_NOTIFY(ModelDidChange,changedUri);

That's it! Your NotifiedView will now get the -modelDidChange: message. The string is hidden behind the use of @protocol, meaning the compiler helps a bit in checking that we are sending the right notification (there are far fewer protocols than strings). The protocol also helps us keep the mapping from notification to message straight, mostly by automating it away. It also allows us to declare conformance to the notification statically, both for readers and for checking that we actually implement the method.

Notification Protocols: Implementation

The implementation is quite simple: the -installProtocolNotifications method iterates over all the protocols an object's class conforms to. For the ones that conform to MPWNotificationProtocol it registers itself with NSotificationCenter to be sent the message in the protocol.

The implementation is part of MPWFoundation.

No comments: