Skip to content

Instantly share code, notes, and snippets.

@nyteshade
Created July 10, 2013 05:09
Show Gist options
  • Save nyteshade/5963631 to your computer and use it in GitHub Desktop.
Save nyteshade/5963631 to your computer and use it in GitHub Desktop.
Eventables are small pub/sub objects that can be reused. The can listen for any number of custom events and take blocks of code to act upon. Their usage is very simple and hopefully straight forward. I've also tried to provide the ability to pass as much data as necessary for each event action. This code is written to be used with ARC.
//
// Eventable.h
// Copyright (c) 2013 Gabriel Harrison
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
@class NEEventInfo;
@class NEEventListener;
typedef void (^NEEventAction)(NEEventInfo *);
@interface NEEventInfo : NSObject
@property NSDictionary *eventData;
@property NSMutableDictionary *dataset;
@property NSString *eventName;
@property id context;
- (id) initWithListener:(NEEventListener *)listener;
- (id) initWithListener:(NEEventListener *)listener andData:(NSMutableDictionary *)data;
- (id) initWithListener:(NEEventListener *)listener andContext:(id)eventContext;
- (id) initWithListener:(NEEventListener *)listener andContext:(id)eventContext andData:(NSMutableDictionary *)data;
@end
@interface NEEventListener : NSObject
@property NSString *eventName;
@property NSDictionary *eventData;
@property (atomic,strong) NEEventAction action;
- (id) init:(NSString *)name withAction:(NEEventAction) eventAction;
- (id) init:(NSString *)name withAction:(NEEventAction) eventAction andData:(NSDictionary *)data;
@end
@interface NEEventable : NSObject {
// The format for listeners is to use the eventName as a key. If the
// event name doesn't yet exist, an array is assigned to the name. The
// array contains instances of EventListener. These are used whenever
// an event is fired to generate the EventInfo.
NSMutableDictionary *listeners;
}
- (void) addEvent:(NSString *)eventName;
- (void) addListener:(NEEventListener *)listener;
- (void) addListener:(NSString *)eventName withAction:(NEEventAction)action;
- (void) addListener:(NSString *)eventName withEventData:(NSDictionary *)eventData withAction:(NEEventAction)action;
- (NSSet *) eventNames;
- (void) fireEvent:(NSString *)eventName;
- (void) fireEvent:(NSString *)eventName withContext:(id)context;
- (void) fireEvent:(NSString *)eventName withData:(NSMutableDictionary *)data;
- (void) fireEvent:(NSString *)eventName withContext:(id)context withData:(NSMutableDictionary *)data;
- (void) removeListeners;
- (void) removeListeners:(NSString *)eventName;
- (void) removeListener:(NSString *)eventName withAction:(NEEventAction)action;
@end
//
// Eventable.m
// Copyright (c) 2013 Gabriel Harrison
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "Eventable.h"
@implementation NEEventInfo
@synthesize eventName, eventData, dataset, context;
- (id) initWithListener:(NEEventListener *)listener {
self = [super init];
if (self) {
self.eventName = [listener.eventName copy];
self.eventData = [listener.eventData copy];
self.dataset = nil;
self.context = nil;
}
return self;
}
- (id) initWithListener:(NEEventListener *)listener andData:(NSMutableDictionary *)data {
self = [super init];
if (self) {
self.eventName = [listener.eventName copy];
self.eventData = [listener.eventData copy];
self.dataset = data;
self.context = nil;
}
return self;
}
- (id) initWithListener:(NEEventListener *)listener andContext:(id)eventContext {
self = [super init];
if (self) {
self.eventName = [listener.eventName copy];
self.eventData = [listener.eventData copy];
self.dataset = nil;
self.context = context;
}
return self;
}
- (id) initWithListener:(NEEventListener *)listener andContext:(id)eventContext andData:(NSMutableDictionary *)data {
self = [super init];
if (self) {
self.eventName = [listener.eventName copy];
self.eventData = [listener.eventData copy];
self.dataset = data;
self.context = eventContext;
}
return self;
}
@end
@implementation NEEventListener
@synthesize eventName, eventData, action;
- (id) init:(NSString *)name withAction:(NEEventAction) eventAction {
self = [super init];
if (self) {
eventName = name;
eventData = nil;
action = eventAction;
}
return self;
}
- (id) init:(NSString *)name withAction:(NEEventAction) eventAction andData:(NSDictionary *)data {
self = [super init];
if (self) {
eventName = name;
eventData = data;
action = eventAction;
}
return self;
}
@end
@implementation NEEventable
- (id)init {
self = [super init];
if (self) {
listeners = [@{} mutableCopy];
}
return self;
}
- (void) addEvent:(NSString *)eventName {
if ([listeners objectForKey:eventName] == nil) {
[listeners setObject:[@[] mutableCopy] forKey:eventName];
}
}
- (void) addListener:(NEEventListener *)listener {
[self addEvent:listener.eventName];
NSMutableArray *list = [listeners objectForKey:listener.eventName];
[list addObject:listener];
}
- (void) addListener:(NSString *)eventName withAction:(NEEventAction)action {
NEEventListener *listener = [[NEEventListener alloc] init:eventName withAction:action];
[self addListener:listener];
}
- (void) addListener:(NSString *)eventName withEventData:(NSDictionary *)eventData withAction:(NEEventAction)action {
NEEventListener *listener = [[NEEventListener alloc] init:eventName withAction:action andData:eventData];
[self addListener:listener];
};
- (NSSet*) eventNames {
return [listeners keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
return YES;
}];
};
- (void) fireEvent:(NSString *)eventName {
NSMutableArray *list = [listeners objectForKey:eventName];
for (NEEventListener *listener in list) {
@try {
NEEventInfo *info = [[NEEventInfo alloc] initWithListener:listener];
listener.action(info);
}
@catch (NSException *exception) {
NSLog(@"%@", exception.reason);
}
}
}
- (void) fireEvent:(NSString *)eventName withContext:(id)context {
NSMutableArray *list = [listeners objectForKey:eventName];
for (NEEventListener *listener in list) {
@try {
NEEventInfo *info = [[NEEventInfo alloc] initWithListener:listener];
info.context = context;
listener.action(info);
}
@catch (NSException *exception) {
NSLog(@"%@", exception.reason);
}
}
}
- (void) fireEvent:(NSString *)eventName withData:(NSMutableDictionary *)data {
NSMutableArray *list = [listeners objectForKey:eventName];
for (NEEventListener *listener in list) {
@try {
NEEventInfo *info = [[NEEventInfo alloc] initWithListener:listener];
info.dataset = data;
listener.action(info);
}
@catch (NSException *exception) {
NSLog(@"%@", exception.reason);
}
}
}
- (void) fireEvent:(NSString *)eventName withContext:(id)context withData:(NSMutableDictionary *)data {
NSMutableArray *list = [listeners objectForKey:eventName];
for (NEEventListener *listener in list) {
@try {
NEEventInfo *info = [[NEEventInfo alloc] initWithListener:listener];
info.context = context;
info.dataset = data;
listener.action(info);
}
@catch (NSException *exception) {
NSLog(@"%@", exception.reason);
}
}
}
- (void) removeListeners {
[listeners removeAllObjects];
}
- (void) removeListeners:(NSString *)eventName {
[listeners removeObjectForKey:eventName];
}
- (void) removeListener:(NSString *)eventName withAction:(NEEventAction)action {
NSMutableArray *list = [listeners objectForKey:eventName];
[list removeObject:action];
}
@end
@nyteshade
Copy link
Author

Example of code usage.

  NEEventable *eventable = [[NEEventable alloc] init];
  [eventable addListener:@"custom-event" withAction:^(NEEventInfo *event) {
    NSLog(@"Custom event listener 1");
  }];
  [eventable addListener:@"custom-event" withAction:^(NEEventInfo *event) {
    NSLog(@"Custom event listener 2");
  }];

  // ... someplace else in the code with access to the eventable object
  [eventable fireEvent:@"custom-event"];

This example logs two messages, one after the other, in the order they were added to the eventable. There is no easy way to modify this order other than to remove and add to the end of the list with the current gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment