Skip to content

Instantly share code, notes, and snippets.

@angelolloqui
Last active May 15, 2018 10:33
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save angelolloqui/8807692 to your computer and use it in GitHub Desktop.
Save angelolloqui/8807692 to your computer and use it in GitHub Desktop.
Advanced NSObject additions to handle commands
@interface AGCommand : NSObject
@property(nonatomic, weak) UIResponder *firingObject;
@property(nonatomic, readonly) SEL action;
+ (instancetype)command;
@end
@implementation AGCommand
+ (instancetype)command {
return [[self alloc] init];
}
- (SEL)action {
return @selector(performGenericCommand:);
}
@end
@interface NSObject (AGCommands)
@property (nonatomic, assign) NSObject *nextCommandResponder;
- (BOOL)canPerformCommand:(AGCommand *)command;
- (NSObject *)targetForCommand:(AGCommand *)command;
- (BOOL)sendCommand:(AGCommand *)command;
@end
#import "NSObject+AGCommands.h"
#import <objc/runtime.h>
@implementation NSObject (AGCommands)
#pragma mark - Public API
- (BOOL)canPerformCommand:(AGCommand *)command {
return [self respondsToSelector:command.action];
}
- (NSObject *)targetForCommand:(AGCommand *)command {
NSObject *implementor = self;
while (implementor) {
if ([implementor canPerformCommand:command] == YES) {
return implementor;
}
implementor = [implementor nextCommandResponder];
}
return nil;
}
- (BOOL)sendCommand:(AGCommand *)command {
if (!command) return NO;
if (command.firingObject == nil) {
command.firingObject = self;
}
NSObject *target = [self targetForCommand:command];
if (target) {
[self performCommand:command onResponder:target];
return YES;
}
return NO;
}
#pragma mark - Properties
static void *const kNextCommandResponderKey = (void *)&kNextCommandResponderKey;
- (NSObject *)nextCommandResponder {
NSObject *responder = objc_getAssociatedObject(self, kNextCommandResponderKey);
if (responder) return responder;
if ([self respondsToSelector:@selector(nextResponder)]) {
return [self performSelector:@selector(nextResponder)];
}
return nil;
}
- (void)setNextCommandResponder:(NSObject *)object {
objc_setAssociatedObject(self, kNextCommandResponderKey, object, OBJC_ASSOCIATION_ASSIGN);
}
#pragma mark - Helpers
- (void)performCommand:(AGCommand *)command onResponder:(NSObject *)responder {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[responder class] instanceMethodSignatureForSelector:command.action]];
[invocation setSelector:command.action];
[invocation setTarget:responder];
[invocation setArgument:&command atIndex:2];
[invocation invoke];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment