blob: 96fea769111d0669c7714f6f6fb47e8de28d95a0 [file] [log] [blame]
//
// NSObject+RACLifting.m
// iOSDemo
//
// Created by Josh Abernathy on 10/13/12.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "NSObject+RACLifting.h"
#import "EXTScope.h"
#import "NSInvocation+RACTypeParsing.h"
#import "NSObject+RACDeallocating.h"
#import "RACSignal+Operations.h"
#import "RACTuple.h"
#import "NSObject+RACDescription.h"
@implementation NSObject (RACLifting)
- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals {
NSCParameterAssert(selector != NULL);
NSCParameterAssert(signals != nil);
NSCParameterAssert(signals.count > 0);
NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector));
NSUInteger numberOfArguments __attribute__((unused)) = methodSignature.numberOfArguments - 2;
NSCAssert(numberOfArguments == signals.count, @"Wrong number of signals for %@ (expected %lu, got %lu)", NSStringFromSelector(selector), (unsigned long)numberOfArguments, (unsigned long)signals.count);
@unsafeify(self);
return [[[[[RACSignal
combineLatest:signals]
takeUntil:self.rac_willDeallocSignal]
map:^(RACTuple *arguments) {
@strongify(self);
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
invocation.rac_argumentsTuple = arguments;
[invocation invokeWithTarget:self];
return invocation.rac_returnValue;
}]
replayLast]
setNameWithFormat:@"%@ -rac_liftSelector: %s withSignalsFromArray: %@", [self rac_description], sel_getName(selector), signals];
}
- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... {
NSCParameterAssert(firstSignal != nil);
NSMutableArray *signals = [NSMutableArray array];
va_list args;
va_start(args, firstSignal);
for (id currentSignal = firstSignal; currentSignal != nil; currentSignal = va_arg(args, id)) {
NSCAssert([currentSignal isKindOfClass:RACSignal.class], @"Argument %@ is not a RACSignal", currentSignal);
[signals addObject:currentSignal];
}
va_end(args);
return [[self
rac_liftSelector:selector withSignalsFromArray:signals]
setNameWithFormat:@"%@ -rac_liftSelector: %s withSignals: %@", [self rac_description], sel_getName(selector), signals];
}
@end
@implementation NSObject (RACLiftingDeprecated)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
static NSArray *RACMapArgumentsToSignals(NSArray *args) {
NSMutableArray *mappedArgs = [NSMutableArray array];
for (id arg in args) {
if ([arg isEqual:RACTupleNil.tupleNil]) {
[mappedArgs addObject:[RACSignal return:nil]];
} else if ([arg isKindOfClass:RACSignal.class]) {
[mappedArgs addObject:arg];
} else {
[mappedArgs addObject:[RACSignal return:arg]];
}
}
return mappedArgs;
}
- (RACSignal *)rac_liftSelector:(SEL)selector withObjects:(id)arg, ... {
NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
NSMutableArray *arguments = [NSMutableArray array];
va_list args;
va_start(args, arg);
for (NSUInteger i = 2; i < methodSignature.numberOfArguments; i++) {
id currentObject = (i == 2 ? arg : va_arg(args, id));
[arguments addObject:currentObject ?: RACTupleNil.tupleNil];
}
va_end(args);
return [self rac_liftSelector:selector withObjectsFromArray:arguments];
}
- (RACSignal *)rac_liftSelector:(SEL)selector withObjectsFromArray:(NSArray *)args {
return [self rac_liftSelector:selector withSignalsFromArray:RACMapArgumentsToSignals(args)];
}
- (RACSignal *)rac_liftBlock:(id)block withArguments:(id)arg, ... {
NSMutableArray *arguments = [NSMutableArray array];
va_list args;
va_start(args, arg);
for (id currentObject = arg; currentObject != nil; currentObject = va_arg(args, id)) {
[arguments addObject:currentObject];
}
va_end(args);
return [self rac_liftBlock:block withArgumentsFromArray:arguments];
}
- (RACSignal *)rac_liftBlock:(id)block withArgumentsFromArray:(NSArray *)args {
return [[[[RACSignal
combineLatest:RACMapArgumentsToSignals(args)]
reduceEach:block]
takeUntil:self.rac_willDeallocSignal]
replayLast];
}
#pragma clang diagnostic pop
@end