In iOS and macOS development, using NSNotification for model-view updates in MVC is common but can lead to repetitive code in ViewControllers.
1
2
3
4
5
6
7
8
9
| - (void)viewDidLoad
{
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(modelDidChange:)
name:MPWAppNotificationModelDidChange
object:nil];
...
}
|
Reducing Redundancy
To avoid this repetition, we can refactor the code.
Step 1: Helper Methods
Create methods to handle subscribing and unsubscribing from notifications.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| - (void)subscribeToModelDidChangeNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(modelDidChange:)
name:MPWAppNotificationModelDidChange
object:nil];
}
- (void)unsubscribeFromModelDidChangeNotification
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPWAppNotificationModelDidChange
object:nil];
}
- (void)subscribeToModelWasDeletedNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(modelDidChange:)
name:MPWAppNotificationModelWasDeleted
object:nil];
}
- (void)unsubscribeFromModelWasDeletedNotification
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPWAppNotificationModelWasDeleted
object:nil];
}
...
|
Notice that the helper methods share a common structure, differing only in selector and notification name. We can extract these as parameters.
1
2
3
4
| [[NSNotificationCenter defaultCenter] addObserver:self
selector:
name:
object:nil];
|
Create more generalized helper methods:
1
2
3
4
5
6
7
| -(void)subscribeToNotification:(NSString *)notificationName usingSelector:(SEL)selectorName
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:**selectorName**
name:**notificationName**
object:nil];
}
|
And for unsubscribing:
1
2
3
4
5
6
| -(void)unsubscribeFromNotification:(NSString *)notificationName
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:**notificationName**
object:nil];
}
|
Now, the helper methods are simplified:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| - (void)subscribeToModelDidChangeNotification
{
[self subscribeToNotification: MPWAppNotificationModelDidChange usingSelector:@selector(modelDidChange:)];
}
- (void)unsubscribeFromModelDidChangeNotification
{
[self unsubscribeFromNotification: MPWAppNotificationModelDidChange];
}
- (void)subscribeToModelWasDeletedNotification
{
[self subscribeToNotification: MPWAppNotificationModelWasDeleted usingSelector:@selector(modelDidChange:)];
}
- (void)unsubscribeFromModelWasDeletedNotification
{
[self unsubscribeFromNotification: MPWAppNotificationModelWasDeleted];
}
|
Step 3: Leveraging Macros
Further refactoring reveals more duplication. We can use C preprocessor macros to streamline the process.
1
2
3
4
5
6
7
8
9
10
11
12
| - (void)subscribeToNotification
{
[self subscribeToNotification: MPWAppNotification usingSelector:@selector(:)];
}
- (void)unsubscribeFromNotification
{
[self unsubscribeFromNotification: MPWAppNotification];
}
- (void):(NSNotification *)notification
{
}
|
Define macros to generate the subscribe/unsubscribe methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #define SUBSCRIBE_UNSUBSCRIBE_WITHMETHODNAME( name , notification, theMessage ) \
- (void)subscribeTo##name \
{\
[self subscribeToNotification:notification usingSelector:@selector(theMessage:)];\
}\
\
- (void)unsubscribeFrom##name \
{\
[self unsubscribeFromNotification:notification];\
}\
-(void)theMessage:(NSNotification *)notification\
{\
}\
#define SUBSCRIBE_UNSUBSCRIBE( commonName, theMessage ) SUBSCRIBE_UNSUBSCRIBE_WITHMETHODNAME( commonName##Notification, MPWAppNotification##commonName , theMessage )
|
The helper methods become more concise:
1
2
3
4
5
6
| SUBSCRIBE_UNSUBSCRIBE( ModelDidChange, modelDidChange )
SUBSCRIBE_UNSUBSCRIBE( ModelWasDeleted, modelWasDeleted )
SUBSCRIBE_UNSUBSCRIBE( ActivityCountDidChange, activityCountDidChange )
SUBSCRIBE_UNSUBSCRIBE( ObjectIdDidChange, objectIdDidChange )
SUBSCRIBE_UNSUBSCRIBE( UserDidLogin, userDidLogin )
|
Moving Beyond Macros: Notification Protocols
While macros reduce redundancy, they can impact readability. A more elegant solution is using Objective-C protocols to represent notifications.
1
2
3
| @protocol ModelDidChange -(void)modelDidChange:(NSNotifiction*)notification;
@end
|
Specify that a class listens to a specific notification:
1
| @interface NotifiedView:NSView @end
|
Notification sending becomes more explicit and somewhat compiler-checked:
1
| [@protocol(ModelDidChange) notify];
|
Conclusion
By iteratively refactoring and exploring metaprogramming techniques, we’ve discovered a protocol-based approach for handling notifications. This not only simplifies the code but also provides a glimpse into how language-level features could enhance this aspect of iOS/macOS development.
implicit invocation
Notification Protocols