Improving the codebase by refactoring towards a specific programming language

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];
}
...

Step 2: Extracting Commonalities

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

Licensed under CC BY-NC-SA 4.0
Last updated on Oct 23, 2022 23:54 +0100