TestsTested | ✓ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Sep 2015 |
Maintained by Ian Keen.
A basic pub/sub eventing mechanism that handles the subscribers lifecycle for you!
iOS provides a few great ways for one object to notify others, lets have a look..
A fairly good way to get notified of changes to a property, multiple subscribers and you can even get an initial value when you subscribe which is super handy on MVVM setups. There are a couple of downsides.. firstly you can only get notified of a change in the properties value, nothing else and secondly (also a huge oversight IMHO) is that there is no built in way to keep track of subscribers, if you fail to unsubscribe you'll cause leaks but if you try to unsubscribe when your not actualy subscribed you'll cause an exception.. Its not all bad though.. Facebook have a fantastic KVOController which takes care of most of that for you!.
It's also worth noting that KVO hasn't even made it into Swift natively yet! (you still need to make your Swift classes basically Objective-C ones in disguise.. hmm..)
Arguably a bit of an improvement over KVO, NSNotificationCenter provides all the pros of KVO except that it takes away the ability to get an initial value. It improves on KVO, however, by providing a built in way to keep track of wether you've subscribed and NSNotifications can send extra bits of data along for you to consume, after doing a quick search I came across this repo that appears to modify KVOController to handle NSNotificationCenter subscribers lifecycle for you.
Another widely used means for notifying an object that something has happened.. you can include as many parameters that you need and the lifecycle can usually be handled nicely by declaring your delegate to be weak
. However this is a single subscriber mechanism, you can of course change this by keeping an array of delegates but then you lose the lifecycle management provided by using weak references.
Finally we were given blocks, another single subscriber pattern they provide the same pros/cons as the delegate pattern except the subscriber can handle the event directly where to subscription code is. This often seems like a great idea and makes sense for something like a UIAlertView however it may actaully make your code less readable if you have a bunch of event handling blocks mashed together in one place within your subscriber.
So it looks like we have a range of ways to handle events.. why would we need another?? Good question.. you might not. Using an IKEvent
may not make sense for your use case but lets have a look at how to set one up.
@interface EventPublisher: NSObject
@property (readonly) IKEvent *myEvent;
@end
@implementation EventPublisher
-(instancetype)init {
if (!(self = [super init])) { return nil; }
_myEvent = [IKEvent new];
return self;
}
-(void)methodThatTiggersEvent {
/* some misc code.. */
notify(self.myEvent, someParameter);
}
@end
Pretty easy no? and to subscribe all you need is
-(void)methodThatSubscribes:(EventPublisher *)publisher {
[publisher.myEvent add:self selector:@selector(myEventHappened:)];
}
-(void)myEventHappened:(id)parameter { ... }
Thats it.. the rest is taken care of, if the object containing methodThatSubscribes:
is deallocated EventPublisher
removes it from the list of subscribers and nothing bad will happen!
You can respond to an event everytime it happens using either a target/selector or a block, given an event
@property (readonly) IKEvent *newNumberValue; //parameters are 'NSNumber *oldValue' and 'NSNumber *newValue'
To subscribe with a target/selector you would use add:selector:
-(void)methodThatSubscribes:(IKEvent *)event {
[event add:self selector:@selector(numberChangedFrom:to:)];
}
-(void)numberChangedFrom:(NSNumber *)oldValue to:(NSNumber *)newValue { ... }
Or using a block using add:block:
-(void)methodThatSubscribes:(IKEvent *)event {
[event add:self block:^(NSNumber *oldValue, NSNumber *newValue) {
/* ... */
}];
}
You can also only listen for a single occurence of an event. Using once:selector:
-(void)methodThatSubscribes:(IKEvent *)event {
[event once:self selector:@selector(numberChangedFrom:to:)];
}
-(void)numberChangedFrom:(NSNumber *)oldValue to:(NSNumber *)newValue { ... }
Or with blocks using once:block:
-(void)methodThatSubscribes:(IKEvent *)event {
[event once:self block:^(NSNumber *oldValue, NSNumber *newValue) {
/* ... */
}];
}
After a subscriber has received an event once it is removed from the list of subscribers.
Notifying is as simple as using
notify(event, param1, param2, param3, ...)
Currently 0 up to 5 parameters are supported, but submit a PR if you need more! adding more is a piece of cake :)
Sometimes you may have an intermediary object that needs to send an event on to subscribers of its own, its annoying to have to subscribe to one event when all you want to do is re-fire it with the same parameters right?!
@interface IntermediaryObject: NSObject
@property (readonly) IKEvent *myEvent;
@end
@implementation IntermediaryObject
-(void)forwardEvent:(IKEvent *)event {
[event addForwarding:self.myEvent];
}
@end
Now anytime event
is fired myEvent
will be fired with the same parameters (if any)
When you are finished with an event and you don't want to be notified anymore simply call
-(void)unsubscribeFrom:(IKEvent *)evvent {
//stop receiving notifications about this event
[event remove:self];
/* or */
//stop forwarding notifications of this event
[event removeForwarding:self.myEvent];
/* or */
//completely removes all subscribers and forwarding from an event
[event removeAllTargetsAndForwarding];
}
or manually by adding the source files from the IKEvents
subfolder to your project
Pull Requests are welcome!
If you use this in a project I would love to hear about it!..
Shoutout to the amazing PromiseKit whose NSMethodSignatureForBlock
implementation I borrowed
I'm usually hanging out on iOS Developers. You should check them out!