├── ReactiveCocoaFramework ├── ReactiveCocoa │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── RACSwizzling.h │ ├── RACEagerSequence.h │ ├── RACEmptySequence.h │ ├── RACUnarySequence.h │ ├── RACImmediateScheduler.h │ ├── RACValueTransformer.h │ ├── RACSwizzling.m │ ├── NSObject+RACDescription.m │ ├── RACMulticastConnection+Private.h │ ├── UIGestureRecognizer+RACSignalSupport.h │ ├── NSObject+RACDeallocating.h │ ├── RACSubscriptionScheduler.h │ ├── RACUnit.h │ ├── NSArray+RACSequenceAdditions.m │ ├── NSString+RACSequenceAdditions.m │ ├── UITextField+RACSignalSupport.h │ ├── NSText+RACSignalSupport.h │ ├── NSObject+RACDescription.h │ ├── NSSet+RACSequenceAdditions.m │ ├── ReactiveCocoa-Prefix.pch │ ├── RACUnit.m │ ├── RACGroupedSignal.h │ ├── NSObject+RACObservablePropertySubject.h │ ├── NSOrderedSet+RACSequenceAdditions.m │ ├── RACSignalSequence.h │ ├── UIControl+RACSignalSupport.h │ ├── RACDelegateProxy.h │ ├── RACGroupedSignal.m │ ├── NSSet+RACSequenceAdditions.h │ ├── NSArray+RACSequenceAdditions.h │ ├── NSEnumerator+RACSequenceAdditions.h │ ├── NSObject+RACObservablePropertySubject.m │ ├── RACScopedDisposable.h │ ├── NSEnumerator+RACSequenceAdditions.m │ ├── RACObjCRuntime.h │ ├── NSOrderedSet+RACSequenceAdditions.h │ ├── RACArraySequence.h │ ├── RACBehaviorSubject.h │ ├── RACTupleSequence.h │ ├── RACSignal+Private.h │ ├── RACStringSequence.h │ ├── NSString+RACSequenceAdditions.h │ ├── NSControl+RACCommandSupport.h │ ├── RACSubject.h │ ├── UITextView+RACSignalSupport.h │ ├── UITextField+RACSignalSupport.m │ ├── NSObject+RACDeallocating.m │ ├── UIBarButtonItem+RACCommandSupport.h │ ├── NSControl+RACTextSignalSupport.h │ ├── RACScopedDisposable.m │ ├── NSString+RACKeyPathUtilities.h │ ├── RACReplaySubject.h │ ├── NSString+RACKeyPathUtilities.m │ ├── RACDisposable.h │ ├── RACDynamicSequence.h │ ├── RACQueueScheduler.h │ ├── RACPropertySubject.h │ ├── RACValueTransformer.m │ ├── NSDictionary+RACSequenceAdditions.m │ ├── NSObject+RACKVOWrapperPrivate.h │ ├── UIGestureRecognizer+RACSignalSupport.m │ ├── RACBlockTrampoline.h │ ├── RACBinding.h │ ├── ReactiveCocoa-Info.plist │ ├── RACBacktrace.h │ ├── RACEventTrampoline.h │ ├── RACImmediateScheduler.m │ ├── NSText+RACSignalSupport.m │ ├── UIControl+RACSignalSupport.m │ ├── RACScheduler+Private.h │ ├── NSControl+RACTextSignalSupport.m │ ├── NSDictionary+RACSequenceAdditions.h │ ├── RACObjCRuntime.m │ ├── RACBinding+Private.h │ ├── RACKVOTrampoline.h │ ├── RACPropertySubject+Private.h │ ├── RACSubscriptionScheduler.m │ ├── UITextView+RACSignalSupport.m │ ├── NSObject+RACSelectorSignal.h │ ├── NSObject+RACAppKitBindings.h │ ├── NSObject+RACKVOWrapper.h │ ├── RACBehaviorSubject.m │ ├── NSControl+RACCommandSupport.m │ ├── RACBacktrace+Private.h │ ├── RACSubscriptingAssignmentTrampoline.m │ ├── RACSubject.m │ ├── RACSubscriptingAssignmentTrampoline.h │ ├── RACEmptySequence.m │ ├── NSInvocation+RACTypeParsing.h │ ├── RACDisposable.m │ ├── NSObject+RACAppKitBindings.m │ ├── RACEvent.h │ ├── RACCompoundDisposable.h │ ├── RACMulticastConnection.h │ ├── RACStringSequence.m │ ├── RACSignalSequence.m │ ├── RACMulticastConnection.m │ ├── RACEagerSequence.m │ ├── UIBarButtonItem+RACCommandSupport.m │ ├── NSObject+RACSelectorSignal.m │ ├── RACTupleSequence.m │ ├── RACUnarySequence.m │ ├── RACSubscriber.h │ ├── RACQueueScheduler.m │ ├── RACSubscriber.m │ ├── ReactiveCocoa.h │ ├── RACBinding.m │ ├── RACDelegateProxy.m │ ├── RACObservablePropertySubject.h │ ├── RACKVOTrampoline.m │ ├── RACCompoundDisposable.m │ ├── RACEvent.m │ ├── RACPropertySubject.m │ ├── NSObject+RACKVOWrapper.m │ ├── RACReplaySubject.m │ ├── RACArraySequence.m │ └── RACEventTrampoline.m ├── ReactiveCocoaTests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── RACSubclassObject.m │ ├── RACSubclassObject.h │ ├── ReactiveCocoaTests-Prefix.pch │ ├── RACSequenceExamples.h │ ├── RACPropertySubjectExamples.h │ ├── RACPropertySignalExamples.h │ ├── NSObjectRACPropertySubscribingExamples.h │ ├── NSEnumeratorRACSequenceAdditionsSpec.m │ ├── ReactiveCocoaTests-Info.plist │ ├── RACSubscriberExamples.h │ ├── RACStreamExamples.h │ ├── NSTextRACSupportSpec.m │ ├── RACPropertySubjectSpec.m │ ├── NSObjectRACDeallocatingSpec.m │ ├── RACSubscriptingAssignmentTrampolineSpec.m │ ├── NSStringRACKeyPathUtilitiesSpec.m │ ├── RACBlockTrampolineSpec.m │ ├── RACTestObject.m │ ├── RACTestObject.h │ ├── NSObjectRACSelectorSignal.m │ ├── RACCompoundDisposableSpec.m │ ├── RACEventSpec.m │ ├── NSNotificationCenterRACSupportSpec.m │ ├── RACBacktraceSpec.m │ ├── RACSubscriberSpec.m │ ├── RACPropertySignalExamples.m │ └── RACMulticastConnectionSpec.m └── ReactiveCocoa.xcodeproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ └── xcshareddata │ └── xcschemes │ └── ReactiveCocoa-iOS.xcscheme ├── Documentation ├── README.md └── DifferencesFromRx.md ├── script ├── targets.awk ├── bootstrap ├── xcodebuild.awk ├── README.md ├── LICENSE.md └── cibuild ├── .gitignore ├── RACExtensions ├── NSFileHandle+RACSupport.h ├── NSNotificationCenter+RACSupport.h ├── NSData+RACSupport.h ├── NSString+RACSupport.h ├── NSNotificationCenter+RACSupport.m ├── NSData+RACSupport.m ├── NSString+RACSupport.m ├── NSFileHandle+RACSupport.m └── NSTask+RACSupport.h ├── .gitmodules ├── CONTRIBUTING.md └── LICENSE.md /ReactiveCocoaFramework/ReactiveCocoa/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Documentation/README.md: -------------------------------------------------------------------------------- 1 | This folder contains conceptual documentation and design guidelines that don't 2 | fit well on a single class or in any specific header file. 3 | -------------------------------------------------------------------------------- /script/targets.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | FS = "\n"; 3 | } 4 | 5 | /Targets:/ { 6 | while (getline && $0 != "") { 7 | if ($0 ~ /Tests/) continue; 8 | 9 | sub(/^ +/, ""); 10 | print "'" $0 "'"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | 17 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(dirname "$0") 4 | cd "$SCRIPT_DIR/.." 5 | 6 | set -o errexit 7 | 8 | echo "*** Updating submodules..." 9 | git submodule sync --quiet 10 | git submodule update --init 11 | git submodule foreach --recursive --quiet "git submodule sync --quiet && git submodule update --init" 12 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubclassObject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubclassObject.h" 10 | 11 | @implementation RACSubclassObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACSubclassObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubclassObject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTestObject.h" 10 | 11 | @interface RACSubclassObject : RACTestObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSwizzling.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSwizzling.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/3/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | extern void RACSwizzle(Class class, SEL originalSelector, SEL newSelector); 13 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveCocoaTests-Prefix.pch 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-29. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define EXP_SHORTHAND 12 | #import "Specta.h" 13 | #import "Expecta.h" 14 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEagerSequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 02/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACArraySequence.h" 10 | 11 | // Private class that implements an eager sequence. 12 | @interface RACEagerSequence : RACArraySequence 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEmptySequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Private class representing an empty sequence. 12 | @interface RACEmptySequence : RACSequence 13 | @end 14 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnarySequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Private class representing a sequence of exactly one value. 12 | @interface RACUnarySequence : RACSequence 13 | @end 14 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACImmediateScheduler.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // A scheduler which immediately executes its scheduled blocks. 12 | @interface RACImmediateScheduler : RACScheduler 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACValueTransformer.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/6/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface RACValueTransformer : NSValueTransformer 13 | 14 | + (instancetype)transformerWithBlock:(id (^)(id value))block; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSwizzling.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSwizzling.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/3/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSwizzling.h" 10 | #import "JRSwizzle.h" 11 | 12 | 13 | void RACSwizzle(Class class, SEL originalSelector, SEL newSelector) { 14 | [class jr_swizzleMethod:originalSelector withMethod:newSelector error:NULL]; 15 | } 16 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDescription.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACDescription.h" 10 | 11 | @implementation NSObject (RACDescription) 12 | 13 | - (NSString *)rac_description { 14 | return [[NSString alloc] initWithFormat:@"<%@: %p>", self.class, self]; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACMulticastConnection+Private.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSubject; 12 | 13 | @interface RACMulticastConnection () 14 | 15 | - (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+RACSignalSupport.h 3 | // Talks 4 | // 5 | // Created by Josh Vera on 5/5/13. 6 | // Copyright (c) 2013 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface UIGestureRecognizer (RACSignalSupport) 14 | 15 | // Returns a signal that sends the receiver when its gesture occurs. 16 | - (RACSignal *)rac_gestureSignal; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDeallocating.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Kazuo Koga on 2013/03/15. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface NSObject (RACDeallocating) 14 | 15 | // Returns a signal that will complete after the receiver has been deallocated. 16 | - (RACSignal *)rac_didDeallocSignal; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptionScheduler.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // A private scheduler used only for subscriptions. See the private 12 | // +[RACScheduler subscriptionScheduler] method for more information. 13 | @interface RACSubscriptionScheduler : RACScheduler 14 | @end 15 | -------------------------------------------------------------------------------- /RACExtensions/NSFileHandle+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileHandle+RACSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface NSFileHandle (RACSupport) 13 | 14 | // Read any available data in the background and send it. Completes when data 15 | // length is <= 0. 16 | - (RACSignal *)rac_readInBackground; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnit.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/27/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | // A unit represents an empty value. 13 | // 14 | // It should never be necessary to create a unit yourself. Just use +defaultUnit. 15 | @interface RACUnit : NSObject 16 | 17 | // A singleton instance. 18 | + (RACUnit *)defaultUnit; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+RACSequenceAdditions.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSArray+RACSequenceAdditions.h" 10 | #import "RACArraySequence.h" 11 | 12 | @implementation NSArray (RACSequenceAdditions) 13 | 14 | - (RACSequence *)rac_sequence { 15 | return [RACArraySequence sequenceWithArray:self offset:0]; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/specta"] 2 | path = external/specta 3 | url = git://github.com/github/specta.git 4 | [submodule "external/expecta"] 5 | path = external/expecta 6 | url = git://github.com/github/expecta.git 7 | [submodule "external/jrswizzle"] 8 | path= external/jrswizzle 9 | url = git://github.com/rentzsch/jrswizzle.git 10 | [submodule "ReactiveCocoaFramework/ReactiveCocoa/libextobjc"] 11 | path = ReactiveCocoaFramework/ReactiveCocoa/libextobjc 12 | url = git://github.com/jspahrsummers/libextobjc.git 13 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSequenceAdditions.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSString+RACSequenceAdditions.h" 10 | #import "RACStringSequence.h" 11 | 12 | @implementation NSString (RACSequenceAdditions) 13 | 14 | - (RACSequence *)rac_sequence { 15 | return [RACStringSequence sequenceWithString:self offset:0]; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+RACSignalSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface UITextField (RACSignalSupport) 14 | 15 | // Creates and returns a signal for the text of the field. It always starts with 16 | // the current text. 17 | - (RACSignal *)rac_textSignal; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSText+RACSignalSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-03-08. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface NSText (RACSignalSupport) 14 | 15 | // Returns a signal which sends the current `string` of the receiver, then the 16 | // new value any time it changes. 17 | - (RACSignal *)rac_textSignal; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACSequenceExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSequenceExamples.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-01. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for RACSequence instances. 10 | extern NSString * const RACSequenceExamples; 11 | 12 | // RACSequence * 13 | extern NSString * const RACSequenceExampleSequence; 14 | 15 | // NSArray * 16 | extern NSString * const RACSequenceExampleExpectedValues; 17 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDescription.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDescription.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (RACDescription) 12 | 13 | // Returns a simplified description of the receiver, which does not invoke 14 | // -description (and thus should be much faster in many cases). 15 | - (NSString *)rac_description; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /RACExtensions/NSNotificationCenter+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+RACSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface NSNotificationCenter (RACSupport) 13 | 14 | // Sends the NSNotification every time the notification is posted. 15 | - (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+RACSequenceAdditions.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSSet+RACSequenceAdditions.h" 10 | #import "NSArray+RACSequenceAdditions.h" 11 | 12 | @implementation NSSet (RACSequenceAdditions) 13 | 14 | - (RACSequence *)rac_sequence { 15 | // TODO: First class support for set sequences. 16 | return self.allObjects.rac_sequence; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ReactiveCocoa' target in the 'ReactiveCocoa' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import "RACBacktrace+Private.h" 8 | #endif 9 | 10 | #undef NSAssert 11 | #undef NSParameterAssert 12 | 13 | extern void NSAssert(int condition, ...) __attribute__((unavailable("Use NSCAssert instead."))); 14 | extern void NSParameterAssert(int condition, ...) __attribute__((unavailable("Use NSCParameterAssert instead."))); 15 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACUnit.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnit.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/27/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACUnit.h" 10 | 11 | 12 | @implementation RACUnit 13 | 14 | 15 | #pragma mark API 16 | 17 | + (RACUnit *)defaultUnit { 18 | static dispatch_once_t onceToken; 19 | static RACUnit *defaultUnit = nil; 20 | dispatch_once(&onceToken, ^{ 21 | defaultUnit = [[self alloc] init]; 22 | }); 23 | 24 | return defaultUnit; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACGroupedSignal.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/2/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | // A grouped signal is used by -[RACSignal groupBy:transform:]. 13 | @interface RACGroupedSignal : RACSubject 14 | 15 | // The key shared by the group. 16 | @property (nonatomic, readonly, copy) id key; 17 | 18 | + (instancetype)signalWithKey:(id)key; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySubjectExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySubjectExamples.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 30/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for RACPropertySubject and it's subclasses. 10 | extern NSString * const RACPropertySubjectExamples; 11 | 12 | // A block of type `RACPropertySubject (^)(void)`, which should return a new 13 | // RACPropertySubject. 14 | extern NSString * const RACPropertySubjectExampleGetPropertyBlock; 15 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACObservablePropertySubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACObservablePropertySubject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 01/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @class RACObservablePropertySubject; 11 | 12 | @interface NSObject (RACObservablePropertySubject) 13 | 14 | // Returns a property subject interface to the receiver's key path. 15 | - (RACObservablePropertySubject *)rac_propertyForKeyPath:(NSString *)keyPath; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSOrderedSet+RACSequenceAdditions.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSOrderedSet+RACSequenceAdditions.h" 10 | #import "NSArray+RACSequenceAdditions.h" 11 | 12 | @implementation NSOrderedSet (RACSequenceAdditions) 13 | 14 | - (RACSequence *)rac_sequence { 15 | // TODO: First class support for ordered set sequences. 16 | return self.array.rac_sequence; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSignalSequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-09. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | // Private class that adapts a RACSignal to the RACSequence interface. 14 | @interface RACSignalSequence : RACSequence 15 | 16 | // Returns a sequence for enumerating over the given signal. 17 | + (RACSequence *)sequenceWithSignal:(RACSignal *)signal; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+RACSignalSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface UIControl (RACSignalSupport) 14 | 15 | // Creates and returns a signal that sends the sender of the control event 16 | // whenever one of the control events is triggered. 17 | - (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDelegateProxy.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/19/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACEventTrampoline; 12 | 13 | @interface RACDelegateProxy : NSObject 14 | 15 | @property (nonatomic, weak) id actualDelegate; 16 | 17 | + (instancetype)proxyWithProtocol:(Protocol *)protocol andDelegator:(NSObject *)delegator; 18 | 19 | - (void)addTrampoline:(RACEventTrampoline *)trampoline; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACGroupedSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACGroupedSignal.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/2/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACGroupedSignal.h" 10 | 11 | @interface RACGroupedSignal () 12 | @property (nonatomic, copy) id key; 13 | @end 14 | 15 | @implementation RACGroupedSignal 16 | 17 | #pragma mark API 18 | 19 | + (instancetype)signalWithKey:(id)key { 20 | RACGroupedSignal *subject = [self subject]; 21 | subject.key = key; 22 | return subject; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSSet+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+RACSequenceAdditions.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence; 12 | 13 | @interface NSSet (RACSequenceAdditions) 14 | 15 | // Creates and returns a sequence corresponding to the receiver. 16 | // 17 | // Mutating the receiver will not affect the sequence after it's been created. 18 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSArray+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+RACSequenceAdditions.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence; 12 | 13 | @interface NSArray (RACSequenceAdditions) 14 | 15 | // Creates and returns a sequence corresponding to the receiver. 16 | // 17 | // Mutating the receiver will not affect the sequence after it's been created. 18 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSEnumerator+RACSequenceAdditions.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 07/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence; 12 | 13 | @interface NSEnumerator (RACSequenceAdditions) 14 | 15 | // Creates and returns a sequence corresponding to the receiver. 16 | // 17 | // The receiver is exhausted lazily as the sequence is enumerated. 18 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACObservablePropertySubject.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACObservablePropertySubject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 01/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACObservablePropertySubject.h" 10 | #import "RACObservablePropertySubject.h" 11 | 12 | @implementation NSObject (RACObservablePropertySubject) 13 | 14 | - (RACObservablePropertySubject *)rac_propertyForKeyPath:(NSString *)keyPath { 15 | return [RACObservablePropertySubject propertyWithTarget:self keyPath:keyPath]; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACScopedDisposable.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | // A disposable that calls its own -dispose when it is dealloc'd. 13 | @interface RACScopedDisposable : RACDisposable 14 | 15 | // Creates a new scoped disposable that will also dispose of the given 16 | // disposable when it is dealloc'd. 17 | + (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSEnumerator+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSEnumerator+RACSequenceAdditions.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 07/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSEnumerator+RACSequenceAdditions.h" 10 | #import "RACSequence.h" 11 | #import "EXTScope.h" 12 | 13 | @implementation NSEnumerator (RACSequenceAdditions) 14 | 15 | - (RACSequence *)rac_sequence { 16 | return [RACSequence sequenceWithHeadBlock:^{ 17 | return [self nextObject]; 18 | } tailBlock:^{ 19 | return self.rac_sequence; 20 | }]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACObjCRuntime.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/19/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RACObjCRuntime : NSObject 13 | 14 | + (void)findMethod:(SEL)method inProtocol:(Protocol *)protocol outMethod:(struct objc_method_description *)outMethod; 15 | + (const char *)getMethodTypesForMethod:(SEL)method inProtocol:(Protocol *)protocol; 16 | + (BOOL)method:(SEL)method existsInProtocol:(Protocol *)protocol; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSOrderedSet+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSOrderedSet+RACSequenceAdditions.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence; 12 | 13 | @interface NSOrderedSet (RACSequenceAdditions) 14 | 15 | // Creates and returns a sequence corresponding to the receiver. 16 | // 17 | // Mutating the receiver will not affect the sequence after it's been created. 18 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /RACExtensions/NSData+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+RACSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface NSData (RACSupport) 13 | 14 | // Read the data at the URL using -[NSData initWithContentsOfURL:options:error:]. 15 | // Sends the data or the error. 16 | // 17 | // scheduler - cannot be nil. 18 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACArraySequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Private class that adapts an array to the RACSequence interface. 12 | @interface RACArraySequence : RACSequence 13 | 14 | // Returns a sequence for enumerating over the given array, starting from the 15 | // given offset. The array will be copied to prevent mutation. 16 | + (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBehaviorSubject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/16/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | // A behavior subject sends the last value it received when it is subscribed to. 13 | @interface RACBehaviorSubject : RACSubject 14 | 15 | // Creates a new behavior subject with a default value. If it hasn't received 16 | // any values when it gets subscribed to, it sends the default value. 17 | + (instancetype)behaviorSubjectWithDefaultValue:(id)value; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTupleSequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Private class that adapts a RACTuple to the RACSequence interface. 12 | @interface RACTupleSequence : RACSequence 13 | 14 | // Returns a sequence for enumerating over the given backing array (from 15 | // a RACTuple), starting from the given offset. 16 | + (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSignal+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSignal+Private.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/15/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACDisposable; 12 | 13 | @interface RACSignal () 14 | 15 | @property (nonatomic, copy) RACDisposable * (^didSubscribe)(id subscriber); 16 | 17 | // All access to this must be synchronized. 18 | @property (nonatomic, strong) NSMutableArray *subscribers; 19 | 20 | - (void)performBlockOnEachSubscriber:(void (^)(id subscriber))block; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACStringSequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Private class that adapts a string to the RACSequence interface. 12 | @interface RACStringSequence : RACSequence 13 | 14 | // Returns a sequence for enumerating over the given string, starting from the 15 | // given character offset. The string will be copied to prevent mutation. 16 | + (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /script/xcodebuild.awk: -------------------------------------------------------------------------------- 1 | # Exit statuses: 2 | # 3 | # 0 - No errors found. 4 | # 1 - Build or test failure. Errors will be logged automatically. 5 | # 2 - Untestable target. Retry with the "build" action. 6 | 7 | BEGIN { 8 | status = 0; 9 | } 10 | 11 | { 12 | print; 13 | fflush(stdout); 14 | } 15 | 16 | /is not valid for Testing/ { 17 | exit 2; 18 | } 19 | 20 | /[0-9]+: (error|warning):/ { 21 | errors = errors $0 "\n"; 22 | } 23 | 24 | /(TEST|BUILD) FAILED/ { 25 | status = 1; 26 | } 27 | 28 | END { 29 | if (length(errors) > 0) { 30 | print "\n*** All errors:\n" errors; 31 | } 32 | 33 | fflush(stdout); 34 | exit status; 35 | } 36 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSString+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSequenceAdditions.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence; 12 | 13 | @interface NSString (RACSequenceAdditions) 14 | 15 | // Creates and returns a sequence containing strings corresponding to each 16 | // composed character sequence in the receiver. 17 | // 18 | // Mutating the receiver will not affect the sequence after it's been created. 19 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySignalExamples.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 9/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for a signal-driven property. 10 | extern NSString * const RACPropertySignalExamples; 11 | 12 | // The block should have the signature: 13 | // void (^)(RACTestObject *testObject, NSString *keyPath, RACSignal *signal) 14 | // and should tie the value of the key path on testObject to signal. The value 15 | // for this key should not be nil. 16 | extern NSString * const RACPropertySignalExamplesSetupBlock; 17 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACCommandSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/3/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACCommand; 12 | 13 | @interface NSControl (RACCommandSupport) 14 | 15 | // Sets the control's command. When the control is clicked, the command is 16 | // executed with the sender of the event. The control's enabledness is bound 17 | // to the command's `canExecute`. 18 | // 19 | // Note: this will reset the control's target and action. 20 | @property (nonatomic, strong) RACCommand *rac_command; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/9/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | // A subject can be thought of as a signal that you can manually control by 13 | // sending next, completed, and error. 14 | // 15 | // They're most helpful in bridging the non-RAC world to RAC, since they let you 16 | // manually control the sending of events. 17 | @interface RACSubject : RACSignal 18 | 19 | // Returns a new subject. 20 | + (instancetype)subject; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+RACSignalSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/18/12. 6 | // Copyright (c) 2012 Cody Krieger. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface UITextView (RACSignalSupport) 14 | 15 | // Creates and returns a signal that sends the sender of the delegate method 16 | // whenever it is triggered. 17 | - (RACSignal *)rac_signalForDelegateMethod:(SEL)method; 18 | 19 | // Creates and returns a signal for the text of the field. It always starts with 20 | // the current text. 21 | - (RACSignal *)rac_textSignal; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UITextField+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+RACSignalSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UITextField+RACSignalSupport.h" 10 | #import "RACSignal.h" 11 | #import "UIControl+RACSignalSupport.h" 12 | 13 | @implementation UITextField (RACSignalSupport) 14 | 15 | - (RACSignal *)rac_textSignal { 16 | return [[[[self rac_signalForControlEvents:UIControlEventEditingChanged] 17 | map:^(UITextField *x) { 18 | return x.text; 19 | }] 20 | startWith:self.text] 21 | setNameWithFormat:@"%@ -rac_textSignal", self]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACDeallocating.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDeallocating.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Kazuo Koga on 2013/03/15. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACDeallocating.h" 10 | #import "NSObject+RACPropertySubscribing.h" 11 | #import "RACDisposable.h" 12 | #import "RACSubject.h" 13 | 14 | @implementation NSObject (RACDeallocating) 15 | 16 | - (RACSignal *)rac_didDeallocSignal { 17 | RACSubject *subject = [RACSubject subject]; 18 | 19 | [self rac_addDeallocDisposable:[RACDisposable disposableWithBlock:^{ 20 | [subject sendCompleted]; 21 | }]]; 22 | 23 | return subject; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /RACExtensions/NSString+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface NSString (RACSupport) 13 | 14 | // Reads in the contents of the file using +[NSString stringWithContentsOfURL:usedEncoding:error:]. 15 | // Note that encoding won't be valid until the signal completes successfully. 16 | // 17 | // scheduler - cannot be nil. 18 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+RACCommandSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Kyle LeNeau on 3/27/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACCommand; 12 | 13 | @interface UIBarButtonItem (RACCommandSupport) 14 | 15 | // Sets the control's command. When the control is clicked, the command is 16 | // executed with the sender of the event. The control's enabledness is bound 17 | // to the command's `canExecute`. 18 | // 19 | // Note: this will reset the control's target and action. 20 | @property (nonatomic, strong) RACCommand *rac_command; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACTextSignalSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-03-08. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface NSControl (RACTextSignalSupport) 14 | 15 | // Observes a text-based control for changes. 16 | // 17 | // Using this method on a control without editable text is considered undefined 18 | // behavior. 19 | // 20 | // Returns a signal which sends the current string value of the receiver, then 21 | // the new value any time it changes. 22 | - (RACSignal *)rac_textSignal; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACPropertySubscribingExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectRACPropertySubscribingExamples.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Vera on 4/10/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for a signal-driven observation. 10 | extern NSString * const RACPropertySubscribingExamples; 11 | 12 | // The block should have the signature: 13 | // RACSignal * (^)(RACTestObject *testObject, NSString *keyPath, id observer) 14 | // and should observe the value of the key path on testObject with observer. The value 15 | // for this key should not be nil. 16 | extern NSString * const RACPropertySubscribingExamplesSetupBlock; 17 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSEnumeratorRACSequenceAdditionsSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSEnumeratorRACSequenceAdditionsSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 07/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSequenceExamples.h" 10 | 11 | #import "NSEnumerator+RACSequenceAdditions.h" 12 | 13 | SpecBegin(NSEnumeratorRACSequenceAdditions) 14 | 15 | describe(@"-rac_sequence", ^{ 16 | NSArray *values = @[ @0, @1, @2, @3, @4 ]; 17 | itShouldBehaveLike(RACSequenceExamples, ^{ 18 | return @{ 19 | RACSequenceExampleSequence: values.objectEnumerator.rac_sequence, 20 | RACSequenceExampleExpectedValues: values 21 | }; 22 | }); 23 | }); 24 | 25 | SpecEnd 26 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACScopedDisposable.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACScopedDisposable.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACScopedDisposable.h" 10 | 11 | @implementation RACScopedDisposable 12 | 13 | #pragma mark Lifecycle 14 | 15 | + (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable { 16 | return [self disposableWithBlock:^{ 17 | [disposable dispose]; 18 | }]; 19 | } 20 | 21 | - (void)dealloc { 22 | [self dispose]; 23 | } 24 | 25 | #pragma mark RACDisposable 26 | 27 | - (RACScopedDisposable *)asScopedDisposable { 28 | // totally already are 29 | return self; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | These scripts are primarily meant to support the use of 2 | [Janky](https://github.com/github/janky). To use them, read the contents of this 3 | repository into a `script` folder: 4 | 5 | ``` 6 | $ git remote add objc-build-scripts https://github.com/jspahrsummers/objc-build-scripts.git 7 | $ git fetch objc-build-scripts 8 | $ git read-tree --prefix=script/ -u objc-build-scripts/master 9 | ``` 10 | 11 | Then commit the changes to incorporate the scripts into your own repository's 12 | history. You can also freely tweak the scripts for your specific project's 13 | needs. 14 | 15 | To bring in upstream changes later: 16 | 17 | ``` 18 | $ git fetch -p objc-build-scripts 19 | $ git merge -Xsubtree=script objc-build-scripts/master 20 | ``` 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACKeyPathUtilities.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 05/05/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (RACKeyPathUtilities) 12 | 13 | // Returns an array of the components of the receiver, or nil if the receiver is 14 | // not a valid key path. 15 | - (NSArray *)rac_keyPathComponents; 16 | 17 | // Returns a key path with all the components of the receiver except for the 18 | // last one or nil if the receiver is not a valid key path, or has only one 19 | // component. 20 | - (NSString *)rac_keyPathByDeletingLastKeyPathComponent; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACReplaySubject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/14/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern const NSUInteger RACReplaySubjectUnlimitedCapacity; 12 | 13 | 14 | // A replay subject saves the values it is sent (up to its defined capacity) 15 | // and resends those to new subscribers. It will also replay an error or 16 | // completion. 17 | @interface RACReplaySubject : RACSubject 18 | 19 | // Creates a new replay subject with the given capacity. A capacity of 20 | // RACReplaySubjectUnlimitedCapacity means values are never trimmed. 21 | + (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSString+RACKeyPathUtilities.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACKeyPathUtilities.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 05/05/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSString+RACKeyPathUtilities.h" 10 | 11 | @implementation NSString (RACKeyPathUtilities) 12 | 13 | - (NSArray *)rac_keyPathComponents { 14 | if (self.length == 0) { 15 | return nil; 16 | } 17 | return [self componentsSeparatedByString:@"."]; 18 | } 19 | 20 | - (NSString *)rac_keyPathByDeletingLastKeyPathComponent { 21 | NSUInteger lastDotIndex = [self rangeOfString:@"." options:NSBackwardsSearch].location; 22 | if (lastDotIndex == NSNotFound) { 23 | return nil; 24 | } 25 | return [self substringToIndex:lastDotIndex]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDisposable.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/16/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACScopedDisposable; 12 | 13 | 14 | // A disposable encapsulates the work necessary to tear down and cleanup a 15 | // subscription. 16 | @interface RACDisposable : NSObject 17 | 18 | + (instancetype)disposableWithBlock:(void (^)(void))block; 19 | 20 | // Performs the disposal work. Can be called multiple times, though sebsequent 21 | // calls won't do anything. 22 | - (void)dispose; 23 | 24 | // Returns a new disposable which will dispose of this disposable when it gets 25 | // dealloc'd. 26 | - (RACScopedDisposable *)asScopedDisposable; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/ReactiveCocoaTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.github.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACDynamicSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDynamicSequence.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Private class that implements a sequence dynamically using blocks. 12 | @interface RACDynamicSequence : RACSequence 13 | 14 | // Returns a sequence which evaluates `dependencyBlock` only once, the first 15 | // time either `headBlock` or `tailBlock` is evaluated. The result of 16 | // `dependencyBlock` will be passed into `headBlock` and `tailBlock` when 17 | // invoked. 18 | + (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACQueueScheduler.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // A scheduler which asynchronously enqueues all its work to a private Grand 12 | // Central Dispatch queue. 13 | @interface RACQueueScheduler : RACScheduler 14 | 15 | // Initializes the receiver with the name of the scheduler and the queue which 16 | // the scheduler should target. 17 | // 18 | // name - The name of the scheduler. 19 | // targetQueue - The queue which the scheduler should target. Cannot be NULL. 20 | // 21 | // Returns the initialized object. 22 | - (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACPropertySubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySubject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 16/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubject.h" 10 | 11 | @class RACDisposable, RACBinding; 12 | 13 | // A RACPropertySubject saves the last value sent to it and resends it to new 14 | // subscribers. It will also resend error or completion. 15 | // 16 | // Values sent to a RACPropertySubject are also sent to it's bindings' 17 | // subscribers. Values sent to a RACProperty's bindings are also sent to the 18 | // RACPropertySubject. 19 | @interface RACPropertySubject : RACSubject 20 | 21 | // Returns a new RACPropertySubject with a starting value of `nil`. 22 | + (instancetype)property; 23 | 24 | // Returns a new binding of the RACPropertySubject. 25 | - (RACBinding *)binding; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriberExamples.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-27. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for implementors of . 10 | extern NSString * const RACSubscriberExamples; 11 | 12 | // id 13 | extern NSString * const RACSubscriberExampleSubscriber; 14 | 15 | // A block which returns an NSArray of the values received so far. 16 | extern NSString * const RACSubscriberExampleValuesReceivedBlock; 17 | 18 | // A block which returns any NSError received so far. 19 | extern NSString * const RACSubscriberExampleErrorReceivedBlock; 20 | 21 | // A block which returns a BOOL indicating whether the subscriber is successful 22 | // so far. 23 | extern NSString * const RACSubscriberExampleSuccessBlock; 24 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACStreamExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACStreamExamples.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-01. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for a RACStream subclass. 10 | extern NSString * const RACStreamExamples; 11 | 12 | // The RACStream subclass to test. 13 | extern NSString * const RACStreamExamplesClass; 14 | 15 | // An infinite RACStream to test, making sure that certain operations 16 | // terminate. 17 | // 18 | // The stream should contain infinite RACUnit values. 19 | extern NSString * const RACStreamExamplesInfiniteStream; 20 | 21 | // A block with the signature: 22 | // 23 | // void (^)(RACStream *stream, NSArray *expectedValues) 24 | // 25 | // … used to verify that a stream contains the expected values. 26 | extern NSString * const RACStreamExamplesVerifyValuesBlock; 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | We love that you're interested in contributing to this project! 2 | 3 | To make the process as painless as possible, we have just a couple of guidelines 4 | that should make life easier for everyone involved. 5 | 6 | ## Prefer Pull Requests 7 | 8 | If you know exactly how to implement the feature being suggested or fix the bug 9 | being reported, please open a pull request instead of an issue. Pull requests are easier than 10 | patches or inline code blocks for discussing and merging the changes. 11 | 12 | If you can't make the change yourself, please open an issue after making sure 13 | that one isn't already logged. 14 | 15 | ## Contributing Code 16 | 17 | Fork this repository, make it awesomer (preferably in a branch named for the 18 | topic), send a pull request! 19 | 20 | All code contributions should match our [coding 21 | conventions](https://github.com/github/objective-c-conventions). 22 | 23 | Thanks for contributing! :boom::camel: 24 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSTextRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTextRACSupportSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-03-08. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSText+RACSignalSupport.h" 10 | #import "RACSignal.h" 11 | 12 | SpecBegin(NSTextRACSupport) 13 | 14 | it(@"NSTextView should send changes on rac_textSignal", ^{ 15 | NSTextView *textView = [[NSTextView alloc] initWithFrame:NSZeroRect]; 16 | expect(textView).notTo.beNil(); 17 | 18 | NSMutableArray *strings = [NSMutableArray array]; 19 | [textView.rac_textSignal subscribeNext:^(NSString *str) { 20 | [strings addObject:str]; 21 | }]; 22 | 23 | expect(strings).to.equal(@[ @"" ]); 24 | 25 | [textView insertText:@"f"]; 26 | [textView insertText:@"o"]; 27 | [textView insertText:@"b"]; 28 | 29 | NSArray *expected = @[ @"", @"f", @"fo", @"fob" ]; 30 | expect(strings).to.equal(expected); 31 | }); 32 | 33 | SpecEnd 34 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySubjectSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySubjectSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 30/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACPropertySubject.h" 10 | #import "RACPropertySubjectExamples.h" 11 | #import "RACBinding.h" 12 | #import "RACDisposable.h" 13 | #import "NSObject+RACPropertySubscribing.h" 14 | 15 | SpecBegin(RACPropertySubject) 16 | 17 | describe(@"RACPropertySubject", ^{ 18 | itShouldBehaveLike(RACPropertySubjectExamples, ^{ 19 | return @{ 20 | RACPropertySubjectExampleGetPropertyBlock: [^{ return [RACPropertySubject property]; } copy] 21 | }; 22 | }); 23 | 24 | describe(@"created with +subject", ^{ 25 | itShouldBehaveLike(RACPropertySubjectExamples, ^{ 26 | return @{ 27 | RACPropertySubjectExampleGetPropertyBlock: [^{ return [RACPropertySubject subject]; } copy] 28 | }; 29 | }); 30 | }); 31 | }); 32 | 33 | SpecEnd 34 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACValueTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACValueTransformer.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/6/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACValueTransformer.h" 10 | 11 | @interface RACValueTransformer () 12 | @property (nonatomic, copy) id (^transformBlock)(id value); 13 | @end 14 | 15 | 16 | @implementation RACValueTransformer 17 | 18 | 19 | #pragma mark NSValueTransformer 20 | 21 | + (BOOL)allowsReverseTransformation { 22 | return NO; 23 | } 24 | 25 | - (id)transformedValue:(id)value { 26 | return self.transformBlock(value); 27 | } 28 | 29 | 30 | #pragma mark API 31 | 32 | @synthesize transformBlock; 33 | 34 | + (instancetype)transformerWithBlock:(id (^)(id value))block { 35 | NSCParameterAssert(block != NULL); 36 | 37 | RACValueTransformer *transformer = [[self alloc] init]; 38 | transformer.transformBlock = block; 39 | return transformer; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /RACExtensions/NSNotificationCenter+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+RACSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSNotificationCenter+RACSupport.h" 10 | #import 11 | 12 | @implementation NSNotificationCenter (RACSupport) 13 | 14 | - (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object { 15 | @unsafeify(object); 16 | return [[RACSignal createSignal:^(id subscriber) { 17 | @strongify(object); 18 | id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) { 19 | [subscriber sendNext:note]; 20 | }]; 21 | 22 | return [RACDisposable disposableWithBlock:^{ 23 | [self removeObserver:observer]; 24 | }]; 25 | }] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+RACSequenceAdditions.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+RACSequenceAdditions.h" 10 | #import "NSArray+RACSequenceAdditions.h" 11 | #import "RACSequence.h" 12 | #import "RACTuple.h" 13 | 14 | @implementation NSDictionary (RACSequenceAdditions) 15 | 16 | - (RACSequence *)rac_sequence { 17 | NSDictionary *immutableDict = [self copy]; 18 | 19 | // TODO: First class support for dictionary sequences. 20 | return [immutableDict.allKeys.rac_sequence map:^(id key) { 21 | id value = immutableDict[key]; 22 | return [RACTuple tupleWithObjects:key, value, nil]; 23 | }]; 24 | } 25 | 26 | - (RACSequence *)rac_keySequence { 27 | return self.allKeys.rac_sequence; 28 | } 29 | 30 | - (RACSequence *)rac_valueSequence { 31 | return self.allValues.rac_sequence; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapperPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACKVOWrapperPrivate.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 1/15/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (RACKVOWrapperPrivate) 12 | 13 | // Should only be manipulated while synchronized on the receiver. 14 | @property (nonatomic, strong) NSMutableArray *RACKVOTrampolines; 15 | 16 | // Remove the trampoline from the receiver. 17 | // 18 | // trampoline - The trampoline to add. Cannot be nil. 19 | // 20 | // This method is thread-safe. 21 | - (void)rac_addKVOTrampoline:(RACKVOTrampoline *)trampoline; 22 | 23 | // Removes the trampoline from the receiver. This does *not* stop the 24 | // trampoline's observation. 25 | // 26 | // trampoline - The trampoline to remove. Can be nil. 27 | // 28 | // This method is thread-safe. 29 | - (void)rac_removeKVOTrampoline:(RACKVOTrampoline *)trampoline; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /RACExtensions/NSData+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+RACSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSData+RACSupport.h" 10 | 11 | @implementation NSData (RACSupport) 12 | 13 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler { 14 | NSCParameterAssert(scheduler != nil); 15 | 16 | RACReplaySubject *subject = [RACReplaySubject subject]; 17 | [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ options: %lu scheduler: %@", URL, (unsigned long)options, scheduler]; 18 | 19 | [scheduler schedule:^{ 20 | NSError *error = nil; 21 | NSData *data = [[NSData alloc] initWithContentsOfURL:URL options:options error:&error]; 22 | if(data == nil) { 23 | [subject sendError:error]; 24 | } else { 25 | [subject sendNext:data]; 26 | [subject sendCompleted]; 27 | } 28 | }]; 29 | 30 | return subject; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /RACExtensions/NSString+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSString+RACSupport.h" 10 | 11 | @implementation NSString (RACSupport) 12 | 13 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler { 14 | NSCParameterAssert(scheduler != nil); 15 | 16 | RACReplaySubject *subject = [RACReplaySubject subject]; 17 | [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ usedEncoding:scheduler: %@", URL, scheduler]; 18 | 19 | [scheduler schedule:^{ 20 | NSError *error = nil; 21 | NSString *string = [NSString stringWithContentsOfURL:URL usedEncoding:encoding error:&error]; 22 | if(string == nil) { 23 | [subject sendError:error]; 24 | } else { 25 | [subject sendNext:string]; 26 | [subject sendCompleted]; 27 | } 28 | }]; 29 | 30 | return subject; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UIGestureRecognizer+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+RACSignalSupport.m 3 | // Talks 4 | // 5 | // Created by Josh Vera on 5/5/13. 6 | // Copyright (c) 2013 GitHub. All rights reserved. 7 | // 8 | 9 | #import "UIGestureRecognizer+RACSignalSupport.h" 10 | #import "RACEventTrampoline.h" 11 | #import 12 | 13 | @implementation UIGestureRecognizer (RACSignalSupport) 14 | 15 | - (RACSignal *)rac_gestureSignal { 16 | RACEventTrampoline *trampoline = [RACEventTrampoline trampolineForGestureRecognizer:self]; 17 | [trampoline.subject setNameWithFormat:@"%@ -rac_gestureSignal", self]; 18 | 19 | NSMutableSet *controlEventTrampolines = objc_getAssociatedObject(self, RACEventTrampolinesKey); 20 | if (controlEventTrampolines == nil) { 21 | controlEventTrampolines = [NSMutableSet set]; 22 | objc_setAssociatedObject(self, RACEventTrampolinesKey, controlEventTrampolines, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 23 | } 24 | 25 | [controlEventTrampolines addObject:trampoline]; 26 | 27 | return trampoline.subject; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBlockTrampoline.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBlockTrampoline.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 10/21/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACTuple; 12 | 13 | // Allows a limited type of dynamic block invocation. 14 | @interface RACBlockTrampoline : NSObject 15 | 16 | // Invokes the given block with the given arguments. All of the block's 17 | // argument types must be objects and it must be typed to return an object. 18 | // 19 | // At this time, it only supports blocks that take up to 15 arguments. Any more 20 | // is just cray. 21 | // 22 | // block - The block to invoke. Must accept as many arguments as are given in 23 | // the arguments array. Cannot be nil. 24 | // arguments - The arguments with which to invoke the block. `RACTupleNil`s will 25 | // be passed as nils. 26 | // 27 | // Returns the return value of invoking the block. 28 | + (id)invokeBlock:(id)block withArguments:(RACTuple *)arguments; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBinding.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBinding.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 01/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignal.h" 10 | #import "RACSubscriber.h" 11 | 12 | @class RACDisposable; 13 | 14 | // A binding of a RACPropertySubject. 15 | // 16 | // Values sent to the binding are sent to the binding's RACPropertySubject's 17 | // subscribers and subscribers of other RACBindings of the same property 18 | // subject, but are not sent to the receiver's subscribers. A binding's 19 | // subscribers will also receive values sent to the binding's property subject. 20 | @interface RACBinding : RACSignal 21 | 22 | // Binds the receiver to `binding` by subscribing each one to the other's 23 | // changes. 24 | // 25 | // When called, `binding`s current value will be sent to the receiver and the 26 | // receiver's current value will be discarded. 27 | // 28 | // Returns a disposable that can be used to stop the binding. 29 | - (RACDisposable *)bindTo:(RACBinding *)binding; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.github.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2012 GitHub, Inc. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /script/LICENSE.md: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2013 Justin Spahr-Summers** 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBacktrace.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-08-20. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #ifdef DEBUG 10 | 11 | // Preserves backtraces across asynchronous calls. 12 | @interface RACBacktrace : NSObject 13 | 14 | // The backtrace from any previous thread. 15 | @property (nonatomic, strong, readonly) RACBacktrace *previousThreadBacktrace; 16 | 17 | // The call stack of this backtrace's thread. 18 | @property (nonatomic, copy, readonly) NSArray *callStackSymbols; 19 | 20 | // Captures the current thread's backtrace, appending it to any backtrace from 21 | // a previous thread. 22 | + (instancetype)captureBacktrace; 23 | 24 | // Same as +captureBacktrace, but omits the specified number of frames at the 25 | // top of the stack (in addition to this method itself). 26 | + (instancetype)captureBacktraceIgnoringFrames:(NSUInteger)ignoreCount; 27 | 28 | // Prints the backtrace of the current thread, appended to that of any previous 29 | // threads. 30 | + (void)printBacktrace; 31 | 32 | @end 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEventTrampoline.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEventTrampoline.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/18/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern const char *RACEventTrampolinesKey; 13 | 14 | @class RACDelegateProxy; 15 | 16 | @interface RACEventTrampoline : NSObject { 17 | SEL delegateMethod; 18 | RACDelegateProxy *proxy; 19 | } 20 | 21 | + (instancetype)trampolineForControl:(UIControl *)control controlEvents:(UIControlEvents)controlEvents; 22 | + (instancetype)trampolineForTextView:(UITextView *)textView delegateMethod:(SEL)method; 23 | 24 | // Returns an event trampoline for the given gesture. 25 | + (instancetype)trampolineForGestureRecognizer:(UIGestureRecognizer *)gesture; 26 | 27 | - (void)didGetControlEvent:(id)sender; 28 | - (void)didGetDelegateEvent:(SEL)delegateMethod sender:(id)sender; 29 | 30 | @property (nonatomic, strong) RACSubject *subject; 31 | @property (nonatomic, strong) RACDelegateProxy *proxy; 32 | @property (nonatomic) SEL delegateMethod; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2012 - 2013, GitHub, Inc.** 2 | **All rights reserved.** 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACImmediateScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACImmediateScheduler.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACImmediateScheduler.h" 10 | #import "EXTScope.h" 11 | #import "RACScheduler+Private.h" 12 | 13 | @implementation RACImmediateScheduler 14 | 15 | #pragma mark Lifecycle 16 | 17 | - (id)init { 18 | return [super initWithName:@"com.ReactiveCocoa.RACScheduler.immediateScheduler"]; 19 | } 20 | 21 | #pragma mark RACScheduler 22 | 23 | - (RACDisposable *)schedule:(void (^)(void))block { 24 | NSCParameterAssert(block != NULL); 25 | 26 | block(); 27 | return nil; 28 | } 29 | 30 | - (RACDisposable *)after:(dispatch_time_t)when schedule:(void (^)(void))block { 31 | NSCParameterAssert(block != NULL); 32 | 33 | // Use a temporary semaphore to block the current thread until a specific 34 | // dispatch_time_t. 35 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 36 | dispatch_semaphore_wait(semaphore, when); 37 | dispatch_release(semaphore); 38 | 39 | block(); 40 | return nil; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSText+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSText+RACSignalSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-03-08. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSText+RACSignalSupport.h" 10 | #import "EXTScope.h" 11 | #import "RACDisposable.h" 12 | #import "RACSignal.h" 13 | #import "RACSubscriber.h" 14 | 15 | @implementation NSText (RACSignalSupport) 16 | 17 | - (RACSignal *)rac_textSignal { 18 | @unsafeify(self); 19 | return [[[[RACSignal 20 | createSignal:^(id subscriber) { 21 | @strongify(self); 22 | id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) { 23 | [subscriber sendNext:note.object]; 24 | }]; 25 | 26 | return [RACDisposable disposableWithBlock:^{ 27 | [NSNotificationCenter.defaultCenter removeObserver:observer]; 28 | }]; 29 | }] 30 | map:^(NSText *text) { 31 | return [text.string copy]; 32 | }] 33 | startWith:[self.string copy]] 34 | setNameWithFormat:@"%@ -rac_textSignal", self]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /script/cibuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")/../ReactiveCocoaFramework" 4 | SCRIPT_DIR=../script 5 | 6 | ## 7 | ## Configuration Variables 8 | ## 9 | 10 | # The build configuration to use. 11 | if [ -z "$XCCONFIGURATION" ] 12 | then 13 | XCCONFIGURATION="Release" 14 | fi 15 | 16 | # A bootstrap script to run before building. 17 | # 18 | # If this file does not exist, it is not considered an error. 19 | BOOTSTRAP="$SCRIPT_DIR/bootstrap" 20 | 21 | # Extra build settings to pass to xcodebuild. 22 | XCODEBUILD_SETTINGS= 23 | 24 | ## 25 | ## Build Process 26 | ## 27 | 28 | if [ -f "$BOOTSTRAP" ] 29 | then 30 | echo "*** Bootstrapping..." 31 | bash "$BOOTSTRAP" || exit $? 32 | fi 33 | 34 | echo "*** Cleaning all targets..." 35 | xcodebuild -alltargets clean OBJROOT="$PWD/build" SYMROOT="$PWD/build" $XCODEBUILD_SETTINGS 36 | 37 | echo "*** Building..." 38 | xcodebuild -configuration "$XCCONFIGURATION" -scheme ReactiveCocoa test OBJROOT="$PWD/build" SYMROOT="$PWD/build" $XCODEBUILD_SETTINGS || exit $? 39 | xcodebuild -configuration "$XCCONFIGURATION" -scheme ReactiveCocoa-iOS build OBJROOT="$PWD/build" SYMROOT="$PWD/build" $XCODEBUILD_SETTINGS || exit $? 40 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UIControl+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+RACSignalSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIControl+RACSignalSupport.h" 10 | #import "RACEventTrampoline.h" 11 | #import 12 | 13 | @implementation UIControl (RACSignalSupport) 14 | 15 | - (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents { 16 | RACEventTrampoline *trampoline = [RACEventTrampoline trampolineForControl:self controlEvents:controlEvents]; 17 | [trampoline.subject setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self, (unsigned long)controlEvents]; 18 | 19 | NSMutableSet *controlEventTrampolines = objc_getAssociatedObject(self, RACEventTrampolinesKey); 20 | if (controlEventTrampolines == nil) { 21 | controlEventTrampolines = [NSMutableSet set]; 22 | objc_setAssociatedObject(self, RACEventTrampolinesKey, controlEventTrampolines, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 23 | } 24 | 25 | [controlEventTrampolines addObject:trampoline]; 26 | 27 | return trampoline.subject; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /RACExtensions/NSFileHandle+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileHandle+RACSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSFileHandle+RACSupport.h" 10 | #import "NSNotificationCenter+RACSupport.h" 11 | 12 | @implementation NSFileHandle (RACSupport) 13 | 14 | - (RACSignal *)rac_readInBackground { 15 | RACReplaySubject *subject = [RACReplaySubject subject]; 16 | [subject setNameWithFormat:@"%@ -rac_readInBackground", self]; 17 | 18 | RACSignal *dataNotification = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:NSFileHandleReadCompletionNotification object:self] map:^(NSNotification *note) { 19 | return [note.userInfo objectForKey:NSFileHandleNotificationDataItem]; 20 | }]; 21 | 22 | __block RACDisposable *subscription = [dataNotification subscribeNext:^(NSData *data) { 23 | if(data.length > 0) { 24 | [subject sendNext:data]; 25 | [self readInBackgroundAndNotify]; 26 | } else { 27 | [subject sendCompleted]; 28 | [subscription dispose]; 29 | } 30 | }]; 31 | 32 | [self readInBackgroundAndNotify]; 33 | 34 | return subject; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACDeallocatingSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDeallocating.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Kazuo Koga on 2013/03/15. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACDeallocating.h" 10 | #import "RACTestObject.h" 11 | #import "RACSignal+Operations.h" 12 | 13 | SpecBegin(NSObjectRACDeallocatingSpec) 14 | 15 | describe(@"-rac_didDeallocSignal", ^{ 16 | it(@"should complete on dealloc", ^{ 17 | __block BOOL completed = NO; 18 | @autoreleasepool { 19 | [[[[RACTestObject alloc] init] rac_didDeallocSignal] subscribeCompleted:^{ 20 | completed = YES; 21 | }]; 22 | } 23 | 24 | expect(completed).to.beTruthy(); 25 | }); 26 | 27 | it(@"should not send anything", ^{ 28 | __block BOOL valueReceived = NO; 29 | __block BOOL completed = NO; 30 | @autoreleasepool { 31 | [[[[RACTestObject alloc] init] rac_didDeallocSignal] subscribeNext:^(id x) { 32 | valueReceived = YES; 33 | } completed:^{ 34 | completed = YES; 35 | }]; 36 | } 37 | 38 | expect(valueReceived).to.beFalsy(); 39 | expect(completed).to.beTruthy(); 40 | }); 41 | }); 42 | 43 | SpecEnd 44 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACScheduler+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACScheduler+Private.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/29/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // The queue-specific current scheduler key. 12 | extern const void *RACSchedulerCurrentSchedulerKey; 13 | 14 | // A private interface for internal RAC use only. 15 | @interface RACScheduler () 16 | 17 | // A dedicated scheduler that fills two requirements: 18 | // 19 | // 1. By the time subscription happens, we need a valid +currentScheduler. 20 | // 2. Subscription should happen as soon as possible. 21 | // 22 | // To fulfill those two, if we already have a valid +currentScheduler, it 23 | // immediately executes scheduled blocks. If we don't, it will execute scheduled 24 | // blocks with a private background scheduler. 25 | + (instancetype)subscriptionScheduler; 26 | 27 | // Initializes the receiver with the given name. 28 | // 29 | // name - The name of the scheduler. If nil, a default name will be used. 30 | // 31 | // Returns the initialized object. 32 | - (id)initWithName:(NSString *)name; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriptingAssignmentTrampolineSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptingAssignmentTrampolineSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 9/24/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubscriptingAssignmentTrampoline.h" 10 | #import "RACPropertySignalExamples.h" 11 | #import "RACTestObject.h" 12 | #import "RACSubject.h" 13 | 14 | SpecBegin(RACSubscriptingAssignmentTrampoline) 15 | 16 | id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, RACSignal *signal) { 17 | [RACSubscriptingAssignmentTrampoline trampoline][ [[RACSubscriptingAssignmentObjectKeyPathPair alloc] initWithObject:testObject keyPath:keyPath] ] = signal; 18 | }; 19 | 20 | itShouldBehaveLike(RACPropertySignalExamples, ^{ 21 | return @{ RACPropertySignalExamplesSetupBlock: setupBlock }; 22 | }); 23 | 24 | it(@"should expand the RAC macro properly", ^{ 25 | RACSubject *subject = [RACSubject subject]; 26 | RACTestObject *testObject = [[RACTestObject alloc] init]; 27 | RAC(testObject, objectValue) = subject; 28 | 29 | [subject sendNext:@1]; 30 | expect(testObject.objectValue).to.equal(@1); 31 | }); 32 | 33 | SpecEnd 34 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACTextSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACTextSignalSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-03-08. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSControl+RACTextSignalSupport.h" 10 | #import "EXTScope.h" 11 | #import "RACDisposable.h" 12 | #import "RACSignal.h" 13 | #import "RACSubscriber.h" 14 | 15 | @implementation NSControl (RACTextSignalSupport) 16 | 17 | - (RACSignal *)rac_textSignal { 18 | @weakify(self); 19 | return [[[[RACSignal 20 | createSignal:^(id subscriber) { 21 | @strongify(self); 22 | id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSControlTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) { 23 | [subscriber sendNext:note.object]; 24 | }]; 25 | 26 | return [RACDisposable disposableWithBlock:^{ 27 | [NSNotificationCenter.defaultCenter removeObserver:observer]; 28 | }]; 29 | }] 30 | map:^(NSControl *control) { 31 | return [control.stringValue copy]; 32 | }] 33 | startWith:[self.stringValue copy]] 34 | setNameWithFormat:@"%@ -rac_textSignal", self]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSDictionary+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+RACSequenceAdditions.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence; 12 | 13 | @interface NSDictionary (RACSequenceAdditions) 14 | 15 | // Creates and returns a sequence of RACTuple key/value pairs. The key will be 16 | // the first element in the tuple, and the value will be the second. 17 | // 18 | // Mutating the receiver will not affect the sequence after it's been created. 19 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 20 | 21 | // Creates and returns a sequence corresponding to the keys in the receiver. 22 | // 23 | // Mutating the receiver will not affect the sequence after it's been created. 24 | @property (nonatomic, copy, readonly) RACSequence *rac_keySequence; 25 | 26 | // Creates and returns a sequence corresponding to the values in the receiver. 27 | // 28 | // Mutating the receiver will not affect the sequence after it's been created. 29 | @property (nonatomic, copy, readonly) RACSequence *rac_valueSequence; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACObjCRuntime.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACObjCRuntime.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/19/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACObjCRuntime.h" 10 | 11 | @implementation RACObjCRuntime 12 | 13 | + (void)findMethod:(SEL)method inProtocol:(Protocol *)protocol outMethod:(struct objc_method_description *)outMethod { 14 | // First, we look for a @required method. If none is found, we look for an 15 | // @optional method. 16 | *outMethod = protocol_getMethodDescription(protocol, method, YES, YES); 17 | if (outMethod->name == NULL) { 18 | *outMethod = protocol_getMethodDescription(protocol, method, NO, YES); 19 | } 20 | } 21 | 22 | + (const char *)getMethodTypesForMethod:(SEL)method inProtocol:(Protocol *)protocol { 23 | struct objc_method_description desc; 24 | [self findMethod:method inProtocol:protocol outMethod:&desc]; 25 | return desc.types; 26 | } 27 | 28 | + (BOOL)method:(SEL)method existsInProtocol:(Protocol *)protocol { 29 | struct objc_method_description desc; 30 | [self findMethod:method inProtocol:protocol outMethod:&desc]; 31 | return desc.name != NULL; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBinding+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBinding+Private.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 01/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACBinding.h" 10 | 11 | @interface RACBinding () 12 | 13 | // Designated initializer. 14 | // 15 | // signal - A signal of `RACTuple`s where the first element is the value of 16 | // the binding's property as it changes, and the second element is 17 | // the `RACBinding` that triggered the change, or `nil` if the 18 | // change was triggered by other means. The signal must also send a 19 | // `RACTuple` with the current value and it's originator on 20 | // subscription. 21 | // subscriber - A subscriber that will be sent a `RACTuple` every time the 22 | // binding's property is changed. The first element will be the new 23 | // value, the second element will be the `RACBinding` that 24 | // triggered the change or nil if the change was triggered by the 25 | // property itself. 26 | - (instancetype)initWithSignal:(RACSignal *)signal subscriber:(id)subscriber; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACKVOTrampoline.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 1/15/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NSObject+RACKVOWrapper.h" 11 | 12 | // The KVO trampoline object. Represents a KVO observation. 13 | @interface RACKVOTrampoline : NSObject 14 | 15 | // Initializes the receiver with the given parameters. 16 | // 17 | // target - The object whose key path should be observed. Cannot be nil. 18 | // observer - The object that gets notified when the value at the key path 19 | // changes. Can be nil. 20 | // keyPath - The key path on `target` to observe. Cannot be nil. 21 | // options - Any key value observing options to use in the observation. 22 | // block - The block to call when the value at the observed key path changes. 23 | // Cannot be nil. 24 | // 25 | // Returns the initialized object. 26 | - (id)initWithTarget:(NSObject *)target observer:(NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block; 27 | 28 | // Stop the KVO observation. 29 | - (void)stopObserving; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACPropertySubject+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySubject+Private.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 31/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACPropertySubject.h" 10 | 11 | @class RACSignal; 12 | @protocol RACSubscriber; 13 | 14 | @interface RACPropertySubject () 15 | 16 | // Designated initializer. 17 | // 18 | // signal - A signal of `RACTuple`s where the first element is the value of 19 | // the property as it changes, and the second element is the 20 | // binding that triggered the change, or `nil` if the change was 21 | // triggered by other means. The signal must also send a `RACTuple` 22 | // with the current value and it's originator on subscription. 23 | // subscriber - A subscriber that will be sent a `RACTuple` every time the 24 | // property is changed. The first element will be the new value, 25 | // the second element will be the binding that triggered the change 26 | // or nil if the change was triggered by the property itself. 27 | - (instancetype)initWithSignal:(RACSignal *)signal subscriber:(id)subscriber; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSStringRACKeyPathUtilitiesSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSStringRACKeyPathUtilitiesSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 05/05/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSString+RACKeyPathUtilities.h" 10 | 11 | SpecBegin(NSStringRACKeyPathUtilities) 12 | 13 | describe(@"-keyPathComponents", ^{ 14 | it(@"should return components in the key path", ^{ 15 | expect(@"self.test.key.path".rac_keyPathComponents).to.equal((@[@"self", @"test", @"key", @"path"])); 16 | }); 17 | 18 | it(@"should return nil if given an empty string", ^{ 19 | expect(@"".rac_keyPathComponents).to.beNil(); 20 | }); 21 | }); 22 | 23 | describe(@"-keyPathByDeletingLastKeyPathComponent", ^{ 24 | it(@"should return the parent key path", ^{ 25 | expect(@"grandparent.parent.child".rac_keyPathByDeletingLastKeyPathComponent).to.equal(@"grandparent.parent"); 26 | }); 27 | 28 | it(@"should return nil if given an empty string", ^{ 29 | expect(@"".rac_keyPathByDeletingLastKeyPathComponent).to.beNil(); 30 | }); 31 | 32 | it(@"should return nil if given a key path with only one component", ^{ 33 | expect(@"self".rac_keyPathByDeletingLastKeyPathComponent).to.beNil(); 34 | }); 35 | }); 36 | 37 | SpecEnd 38 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptionScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptionScheduler.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubscriptionScheduler.h" 10 | #import "RACScheduler+Private.h" 11 | 12 | @interface RACSubscriptionScheduler () 13 | 14 | // A private background scheduler on which to subscribe if the +currentScheduler 15 | // is unknown. 16 | @property (nonatomic, strong, readonly) RACScheduler *backgroundScheduler; 17 | 18 | @end 19 | 20 | @implementation RACSubscriptionScheduler 21 | 22 | #pragma mark Lifecycle 23 | 24 | - (id)init { 25 | self = [super initWithName:@"com.ReactiveCocoa.RACScheduler.subscriptionScheduler"]; 26 | if (self == nil) return nil; 27 | 28 | _backgroundScheduler = [RACScheduler scheduler]; 29 | 30 | return self; 31 | } 32 | 33 | #pragma mark RACScheduler 34 | 35 | - (RACDisposable *)schedule:(void (^)(void))block { 36 | NSCParameterAssert(block != NULL); 37 | 38 | if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block]; 39 | 40 | block(); 41 | return nil; 42 | } 43 | 44 | - (RACDisposable *)after:(dispatch_time_t)when schedule:(void (^)(void))block { 45 | RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler; 46 | return [scheduler after:when schedule:block]; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UITextView+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+RACSignalSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/18/12. 6 | // Copyright (c) 2012 Cody Krieger. All rights reserved. 7 | // 8 | 9 | #import "UITextView+RACSignalSupport.h" 10 | #import "RACEventTrampoline.h" 11 | #import 12 | 13 | @implementation UITextView (RACSignalSupport) 14 | 15 | - (RACSignal *)rac_signalForDelegateMethod:(SEL)method { 16 | RACEventTrampoline *trampoline = [RACEventTrampoline trampolineForTextView:self delegateMethod:method]; 17 | [trampoline.subject setNameWithFormat:@"%@ -rac_signalForDelegateMethod: (%@)", self, NSStringFromSelector(method)]; 18 | 19 | NSMutableSet *controlEventTrampolines = objc_getAssociatedObject(self, RACEventTrampolinesKey); 20 | if (controlEventTrampolines == nil) { 21 | controlEventTrampolines = [NSMutableSet set]; 22 | objc_setAssociatedObject(self, RACEventTrampolinesKey, controlEventTrampolines, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 23 | } 24 | 25 | [controlEventTrampolines addObject:trampoline]; 26 | 27 | return trampoline.subject; 28 | } 29 | 30 | - (RACSignal *)rac_textSignal { 31 | return [[[[self rac_signalForDelegateMethod:@selector(textViewDidChange:)] 32 | map:^(UITextView *x) { 33 | return x.text; 34 | }] 35 | startWith:self.text] 36 | setNameWithFormat:@"%@ -rac_textSignal", self]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACBlockTrampolineSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACBlockTrampolineSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 10/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACBlockTrampoline.h" 10 | #import "RACTuple.h" 11 | 12 | SpecBegin(RACBlockTrampoline) 13 | 14 | it(@"should invoke the block with the given arguments", ^{ 15 | __block NSString *stringArg; 16 | __block NSNumber *numberArg; 17 | id (^block)(NSString *, NSNumber *) = ^ id (NSString *string, NSNumber *number) { 18 | stringArg = string; 19 | numberArg = number; 20 | return nil; 21 | }; 22 | 23 | [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi", @1)]; 24 | expect(stringArg).to.equal(@"hi"); 25 | expect(numberArg).to.equal(@1); 26 | }); 27 | 28 | it(@"should return the result of the block invocation", ^{ 29 | NSString * (^block)(NSString *) = ^(NSString *string) { 30 | return string.uppercaseString; 31 | }; 32 | 33 | NSString *result = [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi")]; 34 | expect(result).to.equal(@"HI"); 35 | }); 36 | 37 | it(@"should pass RACTupleNils as nil", ^{ 38 | __block id arg; 39 | id (^block)(id) = ^ id (id obj) { 40 | arg = obj; 41 | return nil; 42 | }; 43 | 44 | [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(nil)]; 45 | expect(arg).to.beNil(); 46 | }); 47 | 48 | SpecEnd 49 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACSelectorSignal.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | @interface NSObject (RACSelectorSignal) 14 | 15 | // Adds an implementation of `selector` to the receiver which will send the 16 | // argument each time it is invoked. The receiver itself shouldn't have an 17 | // existing implementation of `selector`. It will not swizzle or replace any 18 | // existing implementation. Superclass implementations are allowed but they 19 | // won't be called. 20 | // 21 | // This is most useful for implementing a method which is called to communicate 22 | // events to the receiver. For example, in an NSView: 23 | // [someSignal takeUntil:[self rac_signalForSelector:@selector(mouseDown:)]]; 24 | // 25 | // selector - The selector for which an implementation should be added. It 26 | // shouldn't already be implemented on the receiver. It must be of 27 | // the type: 28 | // - (void)selector:(id)argument 29 | // 30 | // Returns a signal which will send the argument on each invocation. 31 | - (RACSignal *)rac_signalForSelector:(SEL)selector; 32 | 33 | // The same as -rac_signalForSelector: but with class methods. 34 | + (RACSignal *)rac_signalForSelector:(SEL)selector; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACAppKitBindings.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSObject (RACAppKitBindings) 13 | 14 | // Calls -[NSObject bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, nil]] 15 | - (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath; 16 | 17 | // Calls -[NSObject bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, nilValue, NSNullPlaceholderBindingOption, nil]]; 18 | - (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath nilValue:(id)nilValue; 19 | 20 | // Same as `-[NSObject bind:toObject:withKeyPath:] but also transforms values 21 | // using the given transform block. 22 | - (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath transform:(id (^)(id value))transformBlock; 23 | 24 | // Same as `-[NSObject bind:toObject:withKeyPath:] but the value is transformed 25 | // by negating it. 26 | - (void)rac_bind:(NSString *)binding toObject:(id)object withNegatedKeyPath:(NSString *)keyPath; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACKVOWrapper.h 3 | // GitHub 4 | // 5 | // Created by Josh Abernathy on 10/11/11. 6 | // Copyright (c) 2011 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // The block called when the KVO notification fires. 12 | // 13 | // target - The object being observed. 14 | // observer - The object doing the observing. 15 | // change - The KVO change dictionary, as given to 16 | // -observeValueForKeyPath:ofObject:change:context:. 17 | typedef void (^RACKVOBlock)(id target, id observer, NSDictionary *change); 18 | 19 | @class RACKVOTrampoline; 20 | 21 | @interface NSObject (RACKVOWrapper) 22 | 23 | // Adds the given block as the callback for when the keyPath changes. The 24 | // observer does not need to be explicitly removed. It will be removed when the 25 | // observer or observed object is dealloc'd. 26 | // 27 | // observer - the object to which callbacks will be delivered. This is passed back 28 | // into the given block. 29 | // 30 | // keyPath - the key path to observe 31 | // 32 | // options - the key-value observing options 33 | // 34 | // block - the block called when the value at the key path changes. 35 | // 36 | // Returns the KVO trampoline that can be used to stop the observation. 37 | - (RACKVOTrampoline *)rac_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBehaviorSubject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACBehaviorSubject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/16/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACBehaviorSubject.h" 10 | #import "RACDisposable.h" 11 | #import "RACScheduler+Private.h" 12 | 13 | @interface RACBehaviorSubject () 14 | 15 | // This property should only be used while synchronized on self. 16 | @property (nonatomic, strong) id currentValue; 17 | 18 | @end 19 | 20 | @implementation RACBehaviorSubject 21 | 22 | #pragma mark Lifecycle 23 | 24 | + (instancetype)behaviorSubjectWithDefaultValue:(id)value { 25 | RACBehaviorSubject *subject = [self subject]; 26 | subject.currentValue = value; 27 | return subject; 28 | } 29 | 30 | #pragma mark RACSignal 31 | 32 | - (RACDisposable *)subscribe:(id)subscriber { 33 | RACDisposable *subscriptionDisposable = [super subscribe:subscriber]; 34 | 35 | RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ 36 | @synchronized (self) { 37 | [subscriber sendNext:self.currentValue]; 38 | } 39 | }]; 40 | 41 | return [RACDisposable disposableWithBlock:^{ 42 | [subscriptionDisposable dispose]; 43 | [schedulingDisposable dispose]; 44 | }]; 45 | } 46 | 47 | #pragma mark RACSubscriber 48 | 49 | - (void)sendNext:(id)value { 50 | @synchronized (self) { 51 | self.currentValue = value; 52 | [super sendNext:value]; 53 | } 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSControl+RACCommandSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACCommandSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/3/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSControl+RACCommandSupport.h" 10 | #import "RACCommand.h" 11 | 12 | #import 13 | 14 | static void * NSControlRACCommandKey = &NSControlRACCommandKey; 15 | 16 | @implementation NSControl (RACCommandSupport) 17 | 18 | - (RACCommand *)rac_command { 19 | return objc_getAssociatedObject(self, NSControlRACCommandKey); 20 | } 21 | 22 | - (void)setRac_command:(RACCommand *)command { 23 | objc_setAssociatedObject(self, NSControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 24 | 25 | [self unbind:NSEnabledBinding]; 26 | self.enabled = command != nil ? command.canExecute : YES; 27 | 28 | if (command == nil) return; 29 | 30 | [self bind:NSEnabledBinding toObject:self.rac_command withKeyPath:@"canExecute" options:nil]; 31 | 32 | [self rac_hijackActionAndTargetIfNeeded]; 33 | } 34 | 35 | - (void)rac_hijackActionAndTargetIfNeeded { 36 | SEL hijackSelector = @selector(rac_commandPerformAction:); 37 | if (self.target == self && self.action == hijackSelector) return; 38 | 39 | if (self.target != nil) NSLog(@"WARNING: NSControl.rac_command hijacks the control's existing target and action."); 40 | 41 | self.target = self; 42 | self.action = hijackSelector; 43 | } 44 | 45 | - (void)rac_commandPerformAction:(id)sender { 46 | [self.rac_command execute:sender]; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestObject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 9/18/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTestObject.h" 10 | 11 | @implementation RACTestObject 12 | 13 | - (void)setNilValueForKey:(NSString *)key { 14 | [self setValue:@0 forKey:key]; 15 | } 16 | 17 | - (void)setObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { 18 | self.hasInvokedSetObjectValueAndIntegerValue = YES; 19 | self.objectValue = objectValue; 20 | self.integerValue = integerValue; 21 | } 22 | 23 | - (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue { 24 | self.hasInvokedSetObjectValueAndSecondObjectValue = YES; 25 | self.objectValue = objectValue; 26 | self.secondObjectValue = secondObjectValue; 27 | } 28 | 29 | - (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { 30 | return [NSString stringWithFormat:@"%@: %ld", objectValue, (long)integerValue]; 31 | } 32 | 33 | - (NSString *)combineObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue { 34 | return [NSString stringWithFormat:@"%@: %@", objectValue, secondObjectValue]; 35 | } 36 | 37 | - (void)lifeIsGood:(id)sender { 38 | 39 | } 40 | 41 | + (void)lifeIsGood:(id)sender { 42 | 43 | } 44 | 45 | - (NSRange)returnRangeValueWithObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { 46 | return NSMakeRange((NSUInteger)[objectValue integerValue], (NSUInteger)integerValue); 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBacktrace+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBacktrace+Private.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-12-24. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // When this header is imported in RAC sources, any uses of GCD dispatches (in 12 | // Debug builds) will automatically use the backtrace-logging overrides instead. 13 | #ifdef DEBUG 14 | 15 | void rac_dispatch_async(dispatch_queue_t queue, dispatch_block_t block); 16 | void rac_dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); 17 | void rac_dispatch_after(dispatch_time_t time, dispatch_queue_t queue, dispatch_block_t block); 18 | void rac_dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t function); 19 | void rac_dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t function); 20 | void rac_dispatch_after_f(dispatch_time_t time, dispatch_queue_t queue, void *context, dispatch_function_t function); 21 | 22 | #define dispatch_async(...) \ 23 | rac_dispatch_async(__VA_ARGS__) 24 | 25 | #define dispatch_barrier_async(...) \ 26 | rac_dispatch_barrier_async(__VA_ARGS__) 27 | 28 | #define dispatch_after(...) \ 29 | rac_dispatch_after(__VA_ARGS__) 30 | 31 | #define dispatch_async_f(...) \ 32 | rac_dispatch_async_f(__VA_ARGS__) 33 | 34 | #define dispatch_barrier_async_f(...) \ 35 | rac_dispatch_barrier_async_f(__VA_ARGS__) 36 | 37 | #define dispatch_after_f(...) \ 38 | rac_dispatch_after_f(__VA_ARGS__) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptingAssignmentTrampoline.m 3 | // iOSDemo 4 | // 5 | // Created by Josh Abernathy on 9/24/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubscriptingAssignmentTrampoline.h" 10 | 11 | @interface RACSubscriptingAssignmentObjectKeyPathPair () 12 | @property (nonatomic, readonly, strong) NSObject *object; 13 | @property (nonatomic, readonly, copy) NSString *keyPath; 14 | @end 15 | 16 | @implementation RACSubscriptingAssignmentObjectKeyPathPair 17 | 18 | #pragma mark NSCopying 19 | 20 | - (id)copyWithZone:(NSZone *)zone { 21 | return self; 22 | } 23 | 24 | #pragma mark API 25 | 26 | - (id)initWithObject:(NSObject *)object keyPath:(NSString *)keyPath { 27 | self = [super init]; 28 | if (self == nil) return nil; 29 | 30 | _object = object; 31 | _keyPath = [keyPath copy]; 32 | 33 | return self; 34 | } 35 | 36 | @end 37 | 38 | @implementation RACSubscriptingAssignmentTrampoline 39 | 40 | #pragma mark API 41 | 42 | + (instancetype)trampoline { 43 | static dispatch_once_t onceToken; 44 | static RACSubscriptingAssignmentTrampoline *trampoline = nil; 45 | dispatch_once(&onceToken, ^{ 46 | trampoline = [[self alloc] init]; 47 | }); 48 | 49 | return trampoline; 50 | } 51 | 52 | - (void)setObject:(RACSignal *)signal forKeyedSubscript:(RACSubscriptingAssignmentObjectKeyPathPair *)pair { 53 | NSCParameterAssert([pair isKindOfClass:RACSubscriptingAssignmentObjectKeyPathPair.class]); 54 | 55 | [pair.object rac_deriveProperty:pair.keyPath from:signal]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/9/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubject.h" 10 | #import "EXTScope.h" 11 | #import "RACSignal+Private.h" 12 | #import "RACCompoundDisposable.h" 13 | 14 | @interface RACSubject () 15 | 16 | @property (nonatomic, strong, readonly) RACCompoundDisposable *disposable; 17 | 18 | @end 19 | 20 | @implementation RACSubject 21 | 22 | #pragma mark Lifecycle 23 | 24 | + (instancetype)subject { 25 | return [[self alloc] init]; 26 | } 27 | 28 | - (id)init { 29 | self = [super init]; 30 | if (self == nil) return nil; 31 | 32 | _disposable = [RACCompoundDisposable compoundDisposable]; 33 | 34 | return self; 35 | } 36 | 37 | - (void)dealloc { 38 | [self.disposable dispose]; 39 | } 40 | 41 | #pragma mark RACSubscriber 42 | 43 | - (void)sendNext:(id)value { 44 | [self performBlockOnEachSubscriber:^(id subscriber) { 45 | [subscriber sendNext:value]; 46 | }]; 47 | } 48 | 49 | - (void)sendError:(NSError *)error { 50 | [self.disposable dispose]; 51 | 52 | [self performBlockOnEachSubscriber:^(id subscriber) { 53 | [subscriber sendError:error]; 54 | }]; 55 | } 56 | 57 | - (void)sendCompleted { 58 | [self.disposable dispose]; 59 | 60 | [self performBlockOnEachSubscriber:^(id subscriber) { 61 | [subscriber sendCompleted]; 62 | }]; 63 | } 64 | 65 | - (void)didSubscribeWithDisposable:(RACDisposable *)d { 66 | if (d != nil) [self.disposable addDisposable:d]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubscriptingAssignmentTrampoline.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptingAssignmentTrampoline.h 3 | // iOSDemo 4 | // 5 | // Created by Josh Abernathy on 9/24/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | // Lets you assign a keypath / property to a signal. The value of the keypath or 14 | // property is then kept up-to-date with the latest value from the signal. 15 | // 16 | // If given just one argument, it's assumed to be a keypath or property on self. 17 | // If given two, the first argument is the object to which the keypath is 18 | // relative and the second is the keypath. 19 | // 20 | // Examples: 21 | // 22 | // RAC(self.blah) = someSignal; 23 | // RAC(otherObject, blah) = someSignal; 24 | #define RAC(...) metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(_RAC_OBJ(self, __VA_ARGS__))(_RAC_OBJ(__VA_ARGS__)) 25 | 26 | // Do not use this directly. Use the RAC macro above. 27 | #define _RAC_OBJ(OBJ, KEYPATH) [RACSubscriptingAssignmentTrampoline trampoline][ [[RACSubscriptingAssignmentObjectKeyPathPair alloc] initWithObject:OBJ keyPath:@keypath(OBJ, KEYPATH)] ] 28 | 29 | @interface RACSubscriptingAssignmentObjectKeyPathPair : NSObject 30 | 31 | - (id)initWithObject:(NSObject *)object keyPath:(NSString *)keyPath; 32 | 33 | @end 34 | 35 | @interface RACSubscriptingAssignmentTrampoline : NSObject 36 | 37 | + (instancetype)trampoline; 38 | - (void)setObject:(RACSignal *)signal forKeyedSubscript:(id)key; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEmptySequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEmptySequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACEmptySequence.h" 10 | 11 | @implementation RACEmptySequence 12 | 13 | #pragma mark Lifecycle 14 | 15 | + (instancetype)empty { 16 | static id singleton; 17 | static dispatch_once_t pred; 18 | 19 | dispatch_once(&pred, ^{ 20 | singleton = [[self alloc] init]; 21 | }); 22 | 23 | return singleton; 24 | } 25 | 26 | #pragma mark RACSequence 27 | 28 | - (id)head { 29 | return nil; 30 | } 31 | 32 | - (RACSequence *)tail { 33 | return nil; 34 | } 35 | 36 | - (RACSequence *)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence { 37 | return passthroughSequence ?: self; 38 | } 39 | 40 | #pragma mark NSCoding 41 | 42 | - (Class)classForCoder { 43 | // Empty sequences should be encoded as themselves, not array sequences. 44 | return self.class; 45 | } 46 | 47 | - (id)initWithCoder:(NSCoder *)coder { 48 | // Return the singleton. 49 | return self.class.empty; 50 | } 51 | 52 | - (void)encodeWithCoder:(NSCoder *)coder { 53 | } 54 | 55 | #pragma mark NSObject 56 | 57 | - (NSString *)description { 58 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@ }", self.class, self, self.name]; 59 | } 60 | 61 | - (NSUInteger)hash { 62 | // This hash isn't ideal, but it's better than -[RACSequence hash], which 63 | // would just be zero because we have no head. 64 | return (NSUInteger)(__bridge void *)self; 65 | } 66 | 67 | - (BOOL)isEqual:(RACSequence *)seq { 68 | return (self == seq); 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSInvocation+RACTypeParsing.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSInvocation+RACTypeParsing.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSInvocation (RACTypeParsing) 12 | 13 | // Sets the argument for the invocation at the given index by unboxing the given 14 | // object based on the type signature of the argument. 15 | // 16 | // This does not support C array, union, or struct types other than CGRect, 17 | // CGSize, CGPoint, and NSRange. 18 | // 19 | // object - The object to unbox and set as the argument. 20 | // index - The index of the argument to set. 21 | - (void)rac_setArgument:(id)object atIndex:(NSUInteger)index; 22 | 23 | // Gets the argument for the invocation at the given index based on the 24 | // invocation's method signature. The value is then wrapped in the appropriate 25 | // object type. 26 | // 27 | // This does not support C array, union, or struct types other than CGRect, 28 | // CGSize, CGPoint, and NSRange. 29 | // 30 | // index - The index of the argument to get. 31 | // 32 | // Returns the argument of the invocation, wrapped in an object. 33 | - (id)rac_argumentAtIndex:(NSUInteger)index; 34 | 35 | // Gets the return value from the invocation based on the invocation's method 36 | // signature. The value is then wrapped in the appropriate object type. 37 | // 38 | // This does not support C array, union, or struct types other than CGRect, 39 | // CGSize, CGPoint, and NSRange. 40 | // 41 | // Returns the return value of the invocation, wrapped in an object. Voids are 42 | // returned as `RACUnit.defaultUnit`. 43 | - (id)rac_returnValue; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACDisposable.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACDisposable.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/16/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACDisposable.h" 10 | #import "RACScopedDisposable.h" 11 | #import 12 | 13 | @interface RACDisposable () { 14 | // A copied block of type void (^)(void) containing the logic for disposal, 15 | // or NULL if the receiver is already disposed. 16 | // 17 | // This should only be used atomically. 18 | void * volatile _disposeBlock; 19 | } 20 | 21 | @end 22 | 23 | @implementation RACDisposable 24 | 25 | #pragma mark Lifecycle 26 | 27 | + (instancetype)disposableWithBlock:(void (^)(void))block { 28 | RACDisposable *disposable = [[self alloc] init]; 29 | 30 | id copiedBlock = [block copy]; 31 | disposable->_disposeBlock = (void *)CFBridgingRetain(copiedBlock); 32 | 33 | // Force the store to _disposeBlock to complete. 34 | OSMemoryBarrier(); 35 | 36 | return disposable; 37 | } 38 | 39 | - (void)dealloc { 40 | if (_disposeBlock != NULL) { 41 | CFRelease(_disposeBlock); 42 | _disposeBlock = NULL; 43 | } 44 | } 45 | 46 | #pragma mark Disposal 47 | 48 | - (void)dispose { 49 | void (^disposeBlock)(void) = NULL; 50 | 51 | while (YES) { 52 | void *blockPtr = _disposeBlock; 53 | if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) { 54 | disposeBlock = CFBridgingRelease(blockPtr); 55 | break; 56 | } 57 | } 58 | 59 | if (disposeBlock != nil) disposeBlock(); 60 | } 61 | 62 | #pragma mark Scoped Disposables 63 | 64 | - (RACScopedDisposable *)asScopedDisposable { 65 | return [RACScopedDisposable scopedDisposableWithDisposable:self]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACTestObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestObject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 9/18/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RACTestObject : NSObject 12 | 13 | @property (nonatomic, strong) id objectValue; 14 | @property (nonatomic, strong) id secondObjectValue; 15 | @property (nonatomic, assign) NSInteger integerValue; 16 | @property (nonatomic, assign) char *charPointerValue; 17 | @property (nonatomic, assign) const char *constCharPointerValue; 18 | @property (nonatomic, assign) CGRect rectValue; 19 | @property (nonatomic, assign) CGSize sizeValue; 20 | @property (nonatomic, assign) CGPoint pointValue; 21 | @property (nonatomic, assign) NSRange rangeValue; 22 | 23 | // Has -setObjectValue:andIntegerValue: been called? 24 | @property (nonatomic, assign) BOOL hasInvokedSetObjectValueAndIntegerValue; 25 | 26 | // Has -setObjectValue:andSecondObjectValue: been called? 27 | @property (nonatomic, assign) BOOL hasInvokedSetObjectValueAndSecondObjectValue; 28 | 29 | - (void)setObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; 30 | - (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue; 31 | 32 | // Returns a string of the form "objectValue: integerValue". 33 | - (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; 34 | - (NSString *)combineObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue; 35 | 36 | - (void)lifeIsGood:(id)sender; 37 | 38 | + (void)lifeIsGood:(id)sender; 39 | 40 | - (NSRange)returnRangeValueWithObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACAppKitBindings.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACAppKitBindings.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/17/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACAppKitBindings.h" 10 | #import "RACValueTransformer.h" 11 | 12 | 13 | @implementation NSObject (RACAppKitBindings) 14 | 15 | - (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath { 16 | [self rac_bind:binding toObject:object withKeyPath:keyPath nilValue:nil]; 17 | } 18 | 19 | - (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath nilValue:(id)nilValue { 20 | [self bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, nilValue, NSNullPlaceholderBindingOption, nil]]; 21 | } 22 | 23 | - (void)rac_bind:(NSString *)binding toObject:(id)object withKeyPath:(NSString *)keyPath transform:(id (^)(id value))transformBlock { 24 | RACValueTransformer *transformer = [RACValueTransformer transformerWithBlock:transformBlock]; 25 | [self bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, transformer, NSValueTransformerBindingOption, nil]]; 26 | } 27 | 28 | - (void)rac_bind:(NSString *)binding toObject:(id)object withNegatedKeyPath:(NSString *)keyPath { 29 | [self bind:binding toObject:object withKeyPath:keyPath options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption, NSNegateBooleanTransformerName, NSValueTransformerNameBindingOption, nil]]; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEvent.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-01-07. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Describes the type of a RACEvent. 12 | // 13 | // RACEventTypeCompleted - A `completed` event. 14 | // RACEventTypeError - An `error` event. 15 | // RACEventTypeNext - A `next` event. 16 | typedef enum : NSUInteger { 17 | RACEventTypeCompleted, 18 | RACEventTypeError, 19 | RACEventTypeNext 20 | } RACEventType; 21 | 22 | // Represents an event sent by a RACSignal. 23 | // 24 | // This corresponds to the `Notification` class in Rx. 25 | @interface RACEvent : NSObject 26 | 27 | // Returns a singleton RACEvent representing the `completed` event. 28 | + (instancetype)completedEvent; 29 | 30 | // Returns a new event of type RACEventTypeError, containing the given error. 31 | + (instancetype)eventWithError:(NSError *)error; 32 | 33 | // Returns a new event of type RACEventTypeNext, containing the given value. 34 | + (instancetype)eventWithValue:(id)value; 35 | 36 | // The type of event represented by the receiver. 37 | @property (nonatomic, assign, readonly) RACEventType eventType; 38 | 39 | // Returns whether the receiver is of type RACEventTypeCompleted or 40 | // RACEventTypeError. 41 | @property (nonatomic, getter = isFinished, assign, readonly) BOOL finished; 42 | 43 | // The error associated with an event of type RACEventTypeError. This will be 44 | // nil for all other event types. 45 | @property (nonatomic, strong, readonly) NSError *error; 46 | 47 | // The value associated with an event of type RACEventTypeNext. This will be 48 | // nil for all other event types. 49 | @property (nonatomic, strong, readonly) id value; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACCompoundDisposable.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // A disposable of disposables. When it is disposed, it disposes of all its 12 | // contained disposables. 13 | // 14 | // If -addDisposable: is called after the compound disposable has been disposed 15 | // of, the given disposable is immediately disposed. This allows a compound 16 | // disposable to act as a stand-in for a disposable that will be delivered 17 | // asynchronously. 18 | @interface RACCompoundDisposable : RACDisposable 19 | 20 | // Creates and returns a new compound disposable. 21 | + (instancetype)compoundDisposable; 22 | 23 | // Creates and returns a new compound disposable containing the given 24 | // disposables. 25 | + (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables; 26 | 27 | // Adds the given disposable. If the receiving disposable has already been 28 | // disposed of, the given disposable is disposed immediately. 29 | // 30 | // This method is thread-safe. 31 | // 32 | // disposable - The disposable to add. Cannot be nil. 33 | - (void)addDisposable:(RACDisposable *)disposable; 34 | 35 | // Removes the specified disposable from the compound disposable (regardless of 36 | // its disposed status), or does nothing if it's not in the compound disposable. 37 | // 38 | // This is mainly useful for limiting the memory usage of the compound 39 | // disposable for long-running operations. 40 | // 41 | // This method is thread-safe. 42 | // 43 | // disposable - The disposable to remove. This argument may be nil (to make the 44 | // use of weak references easier). 45 | - (void)removeDisposable:(RACDisposable *)disposable; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACMulticastConnection.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @class RACSignal; 10 | @class RACDisposable; 11 | 12 | // A multicast connection encapsulates the idea of sharing one subscription to a 13 | // signal to many subscribers. This is most often needed if the subscription to 14 | // the underlying signal involves side-effects or shouldn't be called more than 15 | // once. 16 | // 17 | // The multicasted signal is only subscribed to when 18 | // -[RACMulticastConnection connect] is called. Until that happens, no values 19 | // will be sent on `signal`. See -[RACMulticastConnection autoconnect] for how 20 | // -[RACMulticastConnection connect] can be called automatically. 21 | // 22 | // Note that you shouldn't create RACMulticastConnection manually. Instead use 23 | // -[RACSignal publish] or -[RACSignal multicast:]. 24 | @interface RACMulticastConnection : NSObject 25 | 26 | // The multicasted signal. 27 | @property (nonatomic, strong, readonly) RACSignal *signal; 28 | 29 | // Connect to the underlying signal by subscribing to it. Calling this multiple 30 | // times does nothing but return the existing connection's disposable. 31 | // 32 | // Returns the disposable for the subscription to the multicasted signal. 33 | - (RACDisposable *)connect; 34 | 35 | // Connects to the underlying signal when the returned signal is first 36 | // subscribed to, and disposes of the subscription to the multicasted signal 37 | // when the returned signal has no subscribers. 38 | // 39 | // If new subscribers show up after being disposed, they'll subscribe and then 40 | // be immediately disposed of. The returned signal will never re-connect to the 41 | // multicasted signal. 42 | // 43 | // Returns the autoconnecting signal. 44 | - (RACSignal *)autoconnect; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACStringSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACStringSequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACStringSequence.h" 10 | #import "EXTScope.h" 11 | 12 | @interface RACStringSequence () 13 | 14 | // The string being sequenced. 15 | @property (nonatomic, copy, readonly) NSString *string; 16 | 17 | // The index in the string from which the sequence starts. 18 | @property (nonatomic, assign, readonly) NSUInteger offset; 19 | 20 | @end 21 | 22 | @implementation RACStringSequence 23 | 24 | #pragma mark Lifecycle 25 | 26 | + (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset { 27 | NSCParameterAssert(offset <= string.length); 28 | 29 | if (offset == string.length) return self.empty; 30 | 31 | RACStringSequence *seq = [[self alloc] init]; 32 | seq->_string = [string copy]; 33 | seq->_offset = offset; 34 | return seq; 35 | } 36 | 37 | #pragma mark RACSequence 38 | 39 | - (id)head { 40 | return [self.string substringWithRange:NSMakeRange(self.offset, 1)]; 41 | } 42 | 43 | - (RACSequence *)tail { 44 | RACSequence *sequence = [self.class sequenceWithString:self.string offset:self.offset + 1]; 45 | sequence.name = self.name; 46 | return sequence; 47 | } 48 | 49 | - (NSArray *)array { 50 | NSUInteger substringLength = self.string.length - self.offset; 51 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:substringLength]; 52 | 53 | [self.string enumerateSubstringsInRange:NSMakeRange(self.offset, substringLength) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { 54 | [array addObject:substring]; 55 | }]; 56 | 57 | return [array copy]; 58 | } 59 | 60 | #pragma mark NSObject 61 | 62 | - (NSString *)description { 63 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@, string = %@ }", self.class, self, self.name, [self.string substringFromIndex:self.offset]]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSignalSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSignalSequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-09. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignalSequence.h" 10 | #import "RACDisposable.h" 11 | #import "RACReplaySubject.h" 12 | #import "RACSignal+Operations.h" 13 | 14 | @interface RACSignalSequence () 15 | 16 | // Replays the signal given on initialization. 17 | @property (nonatomic, strong, readonly) RACReplaySubject *subject; 18 | 19 | @end 20 | 21 | @implementation RACSignalSequence 22 | 23 | #pragma mark Lifecycle 24 | 25 | + (RACSequence *)sequenceWithSignal:(RACSignal *)signal { 26 | RACSignalSequence *seq = [[self alloc] init]; 27 | 28 | RACReplaySubject *subject = [RACReplaySubject subject]; 29 | [signal subscribeNext:^(id value) { 30 | [subject sendNext:value]; 31 | } error:^(NSError *error) { 32 | [subject sendError:error]; 33 | } completed:^{ 34 | [subject sendCompleted]; 35 | }]; 36 | 37 | seq->_subject = subject; 38 | return seq; 39 | } 40 | 41 | #pragma mark RACSequence 42 | 43 | - (id)head { 44 | id value = [self.subject firstOrDefault:self]; 45 | 46 | if (value == self) { 47 | return nil; 48 | } else { 49 | return value ?: NSNull.null; 50 | } 51 | } 52 | 53 | - (RACSequence *)tail { 54 | RACSequence *sequence = [self.class sequenceWithSignal:[self.subject skip:1]]; 55 | sequence.name = self.name; 56 | return sequence; 57 | } 58 | 59 | - (NSArray *)array { 60 | return self.subject.toArray; 61 | } 62 | 63 | #pragma mark NSObject 64 | 65 | - (NSString *)description { 66 | // Synchronously accumulate the values that have been sent so far. 67 | NSMutableArray *values = [NSMutableArray array]; 68 | RACDisposable *disposable = [self.subject subscribeNext:^(id value) { 69 | @synchronized (values) { 70 | [values addObject:value ?: NSNull.null]; 71 | } 72 | }]; 73 | 74 | [disposable dispose]; 75 | 76 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@, values = %@ … }", self.class, self, self.name, values]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACMulticastConnection.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACMulticastConnection.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 4/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACMulticastConnection.h" 10 | #import "RACMulticastConnection+Private.h" 11 | #import "RACDisposable.h" 12 | #import "RACSubject.h" 13 | #import "RACSignal+Private.h" 14 | 15 | @interface RACMulticastConnection () { 16 | RACSubject *_signal; 17 | } 18 | 19 | @property (nonatomic, readonly, strong) RACSignal *sourceSignal; 20 | @property (strong) RACDisposable *disposable; 21 | 22 | // Should only be used while synchronized on self. 23 | @property (nonatomic, assign) BOOL hasConnected; 24 | @end 25 | 26 | @implementation RACMulticastConnection 27 | 28 | #pragma mark Lifecycle 29 | 30 | - (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject { 31 | NSCParameterAssert(source != nil); 32 | NSCParameterAssert(subject != nil); 33 | 34 | self = [super init]; 35 | if (self == nil) return nil; 36 | 37 | _sourceSignal = source; 38 | _signal = subject; 39 | 40 | return self; 41 | } 42 | 43 | #pragma mark Connecting 44 | 45 | - (RACDisposable *)connect { 46 | BOOL shouldConnect = NO; 47 | @synchronized(self) { 48 | if (!self.hasConnected) { 49 | shouldConnect = YES; 50 | self.hasConnected = YES; 51 | } 52 | } 53 | 54 | if (shouldConnect) { 55 | self.disposable = [self.sourceSignal subscribe:_signal]; 56 | } 57 | 58 | return self.disposable; 59 | } 60 | 61 | - (RACSignal *)autoconnect { 62 | return [[RACSignal createSignal:^(id subscriber) { 63 | RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber]; 64 | [self connect]; 65 | 66 | return [RACDisposable disposableWithBlock:^{ 67 | [subscriptionDisposable dispose]; 68 | 69 | @synchronized(self.signal.subscribers) { 70 | if (self.signal.subscribers.count < 1) { 71 | [self.disposable dispose]; 72 | } 73 | } 74 | }]; 75 | }] setNameWithFormat:@"[%@] -autoconnect", self.signal.name]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEagerSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEagerSequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 02/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACEagerSequence.h" 10 | #import "NSObject+RACDescription.h" 11 | #import "RACArraySequence.h" 12 | 13 | @implementation RACEagerSequence 14 | 15 | #pragma mark RACStream 16 | 17 | + (instancetype)return:(id)value { 18 | return [[self sequenceWithArray:@[ value ] offset:0] setNameWithFormat:@"+return: %@", [value rac_description]]; 19 | } 20 | 21 | - (instancetype)bind:(RACStreamBindBlock (^)(void))block { 22 | NSCParameterAssert(block != nil); 23 | RACStreamBindBlock bindBlock = block(); 24 | NSArray *currentArray = self.array; 25 | NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:currentArray.count]; 26 | 27 | for (id value in currentArray) { 28 | BOOL stop = NO; 29 | RACSequence *boundValue = (id)bindBlock(value, &stop); 30 | if (boundValue == nil) break; 31 | 32 | for (id x in boundValue) { 33 | [resultArray addObject:x]; 34 | } 35 | 36 | if (stop) break; 37 | } 38 | 39 | return [[self.class sequenceWithArray:resultArray offset:0] setNameWithFormat:@"[%@] -bind:", self.name]; 40 | } 41 | 42 | - (instancetype)concat:(RACSequence *)sequence { 43 | NSCParameterAssert(sequence != nil); 44 | NSCParameterAssert([sequence isKindOfClass:RACSequence.class]); 45 | 46 | NSArray *array = [self.array arrayByAddingObjectsFromArray:sequence.array]; 47 | return [[self.class sequenceWithArray:array offset:0] setNameWithFormat:@"[%@] -concat: %@", self.name, sequence]; 48 | } 49 | 50 | #pragma mark Extended methods 51 | 52 | - (RACSequence *)eagerSequence { 53 | return self; 54 | } 55 | 56 | - (RACSequence *)lazySequence { 57 | return [RACArraySequence sequenceWithArray:self.array offset:0]; 58 | } 59 | 60 | - (id)foldRightWithStart:(id)start combine:(id (^)(id, RACSequence *rest))combine { 61 | return [super foldRightWithStart:start combine:^(id first, RACSequence *rest) { 62 | return combine(first, rest.eagerSequence); 63 | }]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSObjectRACSelectorSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectRACSelectorSignal.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTestObject.h" 10 | #import "RACSubclassObject.h" 11 | #import "NSObject+RACSelectorSignal.h" 12 | #import "RACSignal.h" 13 | 14 | SpecBegin(NSObjectRACSelectorSignal) 15 | 16 | describe(@"with an instance method", ^{ 17 | it(@"should send the argument for each invocation", ^{ 18 | RACSubclassObject *object = [[RACSubclassObject alloc] init]; 19 | __block id value; 20 | [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(id x) { 21 | value = x; 22 | }]; 23 | 24 | [object lifeIsGood:@42]; 25 | 26 | expect(value).to.equal(@42); 27 | }); 28 | 29 | it(@"shouldn't swizzle an existing method", ^{ 30 | RACTestObject *object = [[RACTestObject alloc] init]; 31 | #ifndef NS_BLOCK_ASSERTIONS 32 | expect(^{ 33 | [object rac_signalForSelector:@selector(lifeIsGood:)]; 34 | }).to.raiseAny(); 35 | #else 36 | __block id value; 37 | [[object rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(id x) { 38 | value = x; 39 | }]; 40 | 41 | [object lifeIsGood:@42]; 42 | 43 | expect(value).to.beNil(); 44 | #endif 45 | }); 46 | }); 47 | 48 | describe(@"with a class method", ^{ 49 | it(@"should send the argument for each invocation", ^{ 50 | __block id value; 51 | [[RACSubclassObject rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(id x) { 52 | value = x; 53 | }]; 54 | 55 | [RACSubclassObject lifeIsGood:@42]; 56 | 57 | expect(value).to.equal(@42); 58 | }); 59 | 60 | it(@"shouldn't swizzle an existing method", ^{ 61 | #ifndef NS_BLOCK_ASSERTIONS 62 | expect(^{ 63 | [RACTestObject rac_signalForSelector:@selector(lifeIsGood:)]; 64 | }).to.raiseAny(); 65 | #else 66 | __block id value; 67 | [[RACTestObject rac_signalForSelector:@selector(lifeIsGood:)] subscribeNext:^(id x) { 68 | value = x; 69 | }]; 70 | 71 | [RACTestObject lifeIsGood:@42]; 72 | 73 | expect(value).to.beNil(); 74 | #endif 75 | }); 76 | }); 77 | 78 | SpecEnd 79 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/UIBarButtonItem+RACCommandSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+RACCommandSupport.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Kyle LeNeau on 3/27/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIBarButtonItem+RACCommandSupport.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | 18 | static void * UIControlRACCommandKey = &UIControlRACCommandKey; 19 | static void * UIControlRACCommandSignalKey = &UIControlRACCommandSignalKey; 20 | 21 | @implementation UIBarButtonItem (RACCommandSupport) 22 | 23 | - (RACCommand *)rac_command { 24 | return objc_getAssociatedObject(self, UIControlRACCommandKey); 25 | } 26 | 27 | - (void)setRac_command:(RACCommand *)command { 28 | objc_setAssociatedObject(self, UIControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 29 | 30 | if (command == nil) return; 31 | 32 | // Check for stored signal in order to remove it and add a new one 33 | RACDisposable *existingSignal = objc_getAssociatedObject(self, UIControlRACCommandSignalKey); 34 | [existingSignal dispose]; 35 | 36 | RACDisposable *newSignal = [RACAbleWithStart(command, canExecute) toProperty:@keypath(self.enabled) onObject:self]; 37 | objc_setAssociatedObject(self, UIControlRACCommandSignalKey, newSignal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 38 | 39 | [self rac_hijackActionAndTargetIfNeeded]; 40 | } 41 | 42 | - (void)rac_hijackActionAndTargetIfNeeded { 43 | SEL hijackSelector = @selector(rac_commandPerformAction:); 44 | if (self.target == self && self.action == hijackSelector) return; 45 | 46 | if (self.target != nil) NSLog(@"WARNING: UIBarButtonItem.rac_command hijacks the control's existing target and action."); 47 | 48 | self.target = self; 49 | self.action = hijackSelector; 50 | } 51 | 52 | - (void)rac_commandPerformAction:(id)sender { 53 | [self.rac_command execute:sender]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACSelectorSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACSelectorSignal.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACSelectorSignal.h" 10 | #import "RACSubject.h" 11 | #import "NSObject+RACPropertySubscribing.h" 12 | #import "RACDisposable.h" 13 | #import 14 | 15 | static const void *RACObjectSelectorSignals = &RACObjectSelectorSignals; 16 | 17 | @implementation NSObject (RACSelectorSignal) 18 | 19 | static RACSignal *NSObjectRACSignalForSelector(id self, SEL _cmd, SEL selector) { 20 | NSCParameterAssert([NSStringFromSelector(selector) componentsSeparatedByString:@":"].count == 2); 21 | 22 | @synchronized(self) { 23 | NSMutableDictionary *selectorSignals = objc_getAssociatedObject(self, RACObjectSelectorSignals); 24 | if (selectorSignals == nil) { 25 | selectorSignals = [NSMutableDictionary dictionary]; 26 | objc_setAssociatedObject(self, RACObjectSelectorSignals, selectorSignals, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | } 28 | 29 | NSString *key = NSStringFromSelector(selector); 30 | RACSubject *subject = selectorSignals[key]; 31 | if (subject != nil) return subject; 32 | 33 | subject = [RACSubject subject]; 34 | IMP imp = imp_implementationWithBlock(^(id self, id arg) { 35 | [subject sendNext:arg]; 36 | }); 37 | 38 | BOOL success = class_addMethod(object_getClass(self), selector, imp, "v@:@"); 39 | NSCAssert(success, @"%@ is already implemented on %@. %@ will not replace the existing implementation.", NSStringFromSelector(selector), self, NSStringFromSelector(_cmd)); 40 | if (!success) return nil; 41 | 42 | selectorSignals[key] = subject; 43 | 44 | [self rac_addDeallocDisposable:[RACDisposable disposableWithBlock:^{ 45 | [subject sendCompleted]; 46 | }]]; 47 | 48 | return subject; 49 | } 50 | } 51 | 52 | - (RACSignal *)rac_signalForSelector:(SEL)selector { 53 | return NSObjectRACSignalForSelector(self, _cmd, selector); 54 | } 55 | 56 | + (RACSignal *)rac_signalForSelector:(SEL)selector { 57 | return NSObjectRACSignalForSelector(self, _cmd, selector); 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /RACExtensions/NSTask+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTask+RACSupport.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern NSString * const NSTaskRACSupportErrorDomain; 13 | 14 | // The NSData from the standard output. 15 | extern NSString * const NSTaskRACSupportOutputData; 16 | 17 | // The NSData from the standard error. 18 | extern NSString * const NSTaskRACSupportErrorData; 19 | 20 | // The NSString created from the output data. May be nil if a string couldn't 21 | // be made from the data. 22 | extern NSString * const NSTaskRACSupportOutputString; 23 | 24 | // The NSString created from the error data. May be nil if a string couldn't 25 | // be made from the data. 26 | extern NSString * const NSTaskRACSupportErrorString; 27 | 28 | // An NSArray of the task's arguments. 29 | extern NSString * const NSTaskRACSupportTaskArguments; 30 | 31 | // The task itself. 32 | extern NSString * const NSTaskRACSupportTask; 33 | 34 | extern const NSInteger NSTaskRACSupportNonZeroTerminationStatus; 35 | 36 | @interface NSTask (RACSupport) 37 | 38 | // Returns a signal to the standard output. Does not start the task. 39 | - (RACSignal *)rac_standardOutput; 40 | 41 | // Returns a signal to the standard error. Does not start the task. 42 | - (RACSignal *)rac_standardError; 43 | 44 | // Returns a signal that sends a `RACUnit.defaultUnit` and completes when the 45 | // task completes. 46 | - (RACSignal *)rac_completion; 47 | 48 | // Runs the task asychronously on the main queue. It aggregates all the data 49 | // from standard output and sends it once the task completes, scheduled on the 50 | // given scheduler. If the task exists with a non-zero status, it sends an 51 | // error. The error's userInfo contains objects of the keys 52 | // NSTaskRACSupportOutputData, NSTaskRACSupportErrorData, and NSTaskRACSupportTask. 53 | // 54 | // scheduler - cannot be nil. 55 | - (RACSignal *)rac_runWithScheduler:(RACScheduler *)scheduler; 56 | 57 | // Calls -rac_runWithScheduler: with the immediate scheduler. 58 | - (RACSignal *)rac_run; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACTupleSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTupleSequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTupleSequence.h" 10 | #import "RACTuple.h" 11 | 12 | @interface RACTupleSequence () 13 | 14 | // The array being sequenced, as taken from RACTuple.backingArray. 15 | @property (nonatomic, strong, readonly) NSArray *tupleBackingArray; 16 | 17 | // The index in the array from which the sequence starts. 18 | @property (nonatomic, assign, readonly) NSUInteger offset; 19 | 20 | @end 21 | 22 | @implementation RACTupleSequence 23 | 24 | #pragma mark Lifecycle 25 | 26 | + (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset { 27 | NSCParameterAssert(offset <= backingArray.count); 28 | 29 | if (offset == backingArray.count) return self.empty; 30 | 31 | RACTupleSequence *seq = [[self alloc] init]; 32 | seq->_tupleBackingArray = backingArray; 33 | seq->_offset = offset; 34 | return seq; 35 | } 36 | 37 | #pragma mark RACSequence 38 | 39 | - (id)head { 40 | id object = [self.tupleBackingArray objectAtIndex:self.offset]; 41 | return (object == RACTupleNil.tupleNil ? NSNull.null : object); 42 | } 43 | 44 | - (RACSequence *)tail { 45 | RACSequence *sequence = [self.class sequenceWithTupleBackingArray:self.tupleBackingArray offset:self.offset + 1]; 46 | sequence.name = self.name; 47 | return sequence; 48 | } 49 | 50 | - (NSArray *)array { 51 | NSRange range = NSMakeRange(self.offset, self.tupleBackingArray.count - self.offset); 52 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:range.length]; 53 | 54 | [self.tupleBackingArray enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] options:0 usingBlock:^(id object, NSUInteger index, BOOL *stop) { 55 | id mappedObject = (object == RACTupleNil.tupleNil ? NSNull.null : object); 56 | [array addObject:mappedObject]; 57 | }]; 58 | 59 | return array; 60 | } 61 | 62 | #pragma mark NSObject 63 | 64 | - (NSString *)description { 65 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@, tuple = %@ }", self.class, self, self.name, self.tupleBackingArray]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACUnarySequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnarySequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACUnarySequence.h" 10 | #import "EXTKeyPathCoding.h" 11 | #import "NSObject+RACDescription.h" 12 | 13 | @interface RACUnarySequence () 14 | 15 | // The single value stored in this sequence. 16 | @property (nonatomic, strong, readwrite) id head; 17 | 18 | @end 19 | 20 | @implementation RACUnarySequence 21 | 22 | #pragma mark Properties 23 | 24 | @synthesize head = _head; 25 | 26 | #pragma mark Lifecycle 27 | 28 | + (instancetype)return:(id)value { 29 | RACUnarySequence *sequence = [[self alloc] init]; 30 | sequence.head = value; 31 | return [sequence setNameWithFormat:@"+return: %@", [value rac_description]]; 32 | } 33 | 34 | #pragma mark RACSequence 35 | 36 | - (RACSequence *)tail { 37 | return nil; 38 | } 39 | 40 | - (instancetype)bind:(RACStreamBindBlock (^)(void))block { 41 | RACStreamBindBlock bindBlock = block(); 42 | BOOL stop = NO; 43 | 44 | RACSequence *result = (id)[bindBlock(self.head, &stop) setNameWithFormat:@"[%@] -bind:", self.name]; 45 | return result ?: self.class.empty; 46 | } 47 | 48 | #pragma mark NSCoding 49 | 50 | - (Class)classForCoder { 51 | // Unary sequences should be encoded as themselves, not array sequences. 52 | return self.class; 53 | } 54 | 55 | - (id)initWithCoder:(NSCoder *)coder { 56 | id value = [coder decodeObjectForKey:@keypath(self.head)]; 57 | return [self.class return:value]; 58 | } 59 | 60 | - (void)encodeWithCoder:(NSCoder *)coder { 61 | if (self.head != nil) [coder encodeObject:self.head forKey:@keypath(self.head)]; 62 | } 63 | 64 | #pragma mark NSObject 65 | 66 | - (NSString *)description { 67 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@ }", self.class, self, self.name, [self.head rac_description]]; 68 | } 69 | 70 | - (NSUInteger)hash { 71 | return [self.head hash]; 72 | } 73 | 74 | - (BOOL)isEqual:(RACUnarySequence *)seq { 75 | if (self == seq) return YES; 76 | if (![seq isKindOfClass:RACUnarySequence.class]) return NO; 77 | 78 | return self.head == seq.head || [(NSObject *)self.head isEqual:seq.head]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriber.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/1/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACDisposable; 12 | 13 | // Represents any object which can directly receive values from a RACSignal. 14 | // 15 | // You generally shouldn't need to implement this protocol. +[RACSignal 16 | // createSignal:], RACSignal's subscription methods, or RACSubject should work 17 | // for most uses. 18 | // 19 | // Implementors of this protocol may receive messages and values from multiple 20 | // threads simultaneously, and so should be thread-safe. Subscribers will also 21 | // be weakly referenced so implementations must allow that. 22 | @protocol RACSubscriber 23 | @required 24 | 25 | // Send the next value to subscribers. 26 | // 27 | // value - The value to send. This can be `nil`. 28 | - (void)sendNext:(id)value; 29 | 30 | // Send the error to subscribers. 31 | // 32 | // error - The error to send. This can be `nil`. 33 | // 34 | // This terminates the subscription, and invalidates the subscriber (such that 35 | // it cannot subscribe to anything else in the future). 36 | - (void)sendError:(NSError *)error; 37 | 38 | // Send completed to subscribers. 39 | // 40 | // This terminates the subscription, and invalidates the subscriber (such that 41 | // it cannot subscribe to anything else in the future). 42 | - (void)sendCompleted; 43 | 44 | // Sends the subscriber a disposable that represents one of its subscriptions. 45 | // 46 | // A subscriber may receive multiple disposables if it gets subscribed to 47 | // multiple signals; however, any error or completed events must terminate _all_ 48 | // subscriptions. 49 | - (void)didSubscribeWithDisposable:(RACDisposable *)disposable; 50 | 51 | @end 52 | 53 | // A simple block-based subscriber. 54 | // 55 | // You shouldn't need to interact with this class directly. Use 56 | // -[RACSignal subscribeNext:error:completed:] instead. 57 | @interface RACSubscriber : NSObject 58 | 59 | // Creates a new subscriber with the given blocks. 60 | + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa.xcodeproj/xcshareddata/xcschemes/ReactiveCocoa-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACQueueScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACQueueScheduler.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACQueueScheduler.h" 10 | #import "RACDisposable.h" 11 | #import "RACScheduler+Private.h" 12 | #import 13 | 14 | @interface RACQueueScheduler () 15 | @property (nonatomic, readonly) dispatch_queue_t queue; 16 | @end 17 | 18 | @implementation RACQueueScheduler 19 | 20 | #pragma mark Lifecycle 21 | 22 | - (void)dealloc { 23 | dispatch_release(_queue); 24 | } 25 | 26 | - (id)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue { 27 | NSCParameterAssert(targetQueue != NULL); 28 | 29 | _queue = dispatch_queue_create(name.UTF8String, DISPATCH_QUEUE_SERIAL); 30 | if (_queue == nil) return nil; 31 | 32 | dispatch_set_target_queue(_queue, targetQueue); 33 | 34 | return [super initWithName:name]; 35 | } 36 | 37 | #pragma mark Current Scheduler 38 | 39 | static void currentSchedulerRelease(void *context) { 40 | CFBridgingRelease(context); 41 | } 42 | 43 | - (void)performAsCurrentScheduler:(void (^)(void))block { 44 | NSCParameterAssert(block != NULL); 45 | 46 | dispatch_queue_set_specific(self.queue, RACSchedulerCurrentSchedulerKey, (void *)CFBridgingRetain(self), currentSchedulerRelease); 47 | block(); 48 | dispatch_queue_set_specific(self.queue, RACSchedulerCurrentSchedulerKey, nil, currentSchedulerRelease); 49 | } 50 | 51 | #pragma mark RACScheduler 52 | 53 | - (RACDisposable *)schedule:(void (^)(void))block { 54 | NSCParameterAssert(block != NULL); 55 | 56 | __block volatile uint32_t disposed = 0; 57 | 58 | dispatch_async(self.queue, ^{ 59 | if (disposed != 0) return; 60 | [self performAsCurrentScheduler:block]; 61 | }); 62 | 63 | return [RACDisposable disposableWithBlock:^{ 64 | OSAtomicOr32Barrier(1, &disposed); 65 | }]; 66 | } 67 | 68 | - (RACDisposable *)after:(dispatch_time_t)when schedule:(void (^)(void))block { 69 | NSCParameterAssert(block != NULL); 70 | 71 | __block volatile uint32_t disposed = 0; 72 | 73 | dispatch_after(when, self.queue, ^{ 74 | if (disposed != 0) return; 75 | [self performAsCurrentScheduler:block]; 76 | }); 77 | 78 | return [RACDisposable disposableWithBlock:^{ 79 | OSAtomicOr32Barrier(1, &disposed); 80 | }]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACSubscriber.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriber.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/1/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubscriber.h" 10 | #import "EXTScope.h" 11 | #import "RACCompoundDisposable.h" 12 | 13 | @interface RACSubscriber () 14 | 15 | @property (nonatomic, copy, readonly) void (^next)(id value); 16 | @property (nonatomic, copy, readonly) void (^error)(NSError *error); 17 | @property (nonatomic, copy, readonly) void (^completed)(void); 18 | @property (nonatomic, strong, readonly) RACCompoundDisposable *disposable; 19 | 20 | // This property should only be used while synchronized on self. 21 | @property (nonatomic, assign) BOOL disposed; 22 | 23 | @end 24 | 25 | @implementation RACSubscriber 26 | 27 | #pragma mark Lifecycle 28 | 29 | + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed { 30 | RACSubscriber *subscriber = [[self alloc] init]; 31 | 32 | subscriber->_next = [next copy]; 33 | subscriber->_error = [error copy]; 34 | subscriber->_completed = [completed copy]; 35 | 36 | return subscriber; 37 | } 38 | 39 | - (id)init { 40 | self = [super init]; 41 | if (self == nil) return nil; 42 | 43 | @weakify(self); 44 | 45 | RACDisposable *selfDisposable = [RACDisposable disposableWithBlock:^{ 46 | @strongify(self); 47 | 48 | @synchronized (self) { 49 | self.disposed = YES; 50 | } 51 | }]; 52 | 53 | _disposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ selfDisposable ]]; 54 | 55 | return self; 56 | } 57 | 58 | - (void)dealloc { 59 | [self.disposable dispose]; 60 | } 61 | 62 | #pragma mark RACSubscriber 63 | 64 | - (void)sendNext:(id)value { 65 | if (self.next == NULL) return; 66 | 67 | @synchronized (self) { 68 | if (self.disposed) return; 69 | self.next(value); 70 | } 71 | } 72 | 73 | - (void)sendError:(NSError *)e { 74 | @synchronized (self) { 75 | if (self.disposed) return; 76 | 77 | [self.disposable dispose]; 78 | if (self.error != NULL) self.error(e); 79 | } 80 | } 81 | 82 | - (void)sendCompleted { 83 | @synchronized (self) { 84 | if (self.disposed) return; 85 | 86 | [self.disposable dispose]; 87 | if (self.completed != NULL) self.completed(); 88 | } 89 | } 90 | 91 | - (void)didSubscribeWithDisposable:(RACDisposable *)d { 92 | if (d != nil) [self.disposable addDisposable:d]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACCompoundDisposableSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACCompoundDisposableSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACCompoundDisposable.h" 10 | 11 | SpecBegin(RACCompoundDisposable) 12 | 13 | it(@"should dispose of all its contained disposables", ^{ 14 | __block BOOL d1Disposed = NO; 15 | RACDisposable *d1 = [RACDisposable disposableWithBlock:^{ 16 | d1Disposed = YES; 17 | }]; 18 | 19 | __block BOOL d2Disposed = NO; 20 | RACDisposable *d2 = [RACDisposable disposableWithBlock:^{ 21 | d2Disposed = YES; 22 | }]; 23 | 24 | __block BOOL d3Disposed = NO; 25 | RACDisposable *d3 = [RACDisposable disposableWithBlock:^{ 26 | d3Disposed = YES; 27 | }]; 28 | 29 | RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ d1, d2 ]]; 30 | [disposable addDisposable:d3]; 31 | expect(d1Disposed).to.beFalsy(); 32 | expect(d2Disposed).to.beFalsy(); 33 | expect(d3Disposed).to.beFalsy(); 34 | 35 | [disposable dispose]; 36 | expect(d1Disposed).to.beTruthy(); 37 | expect(d2Disposed).to.beTruthy(); 38 | expect(d3Disposed).to.beTruthy(); 39 | }); 40 | 41 | it(@"should dispose of any added disposables immediately if it's already been disposed", ^{ 42 | RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; 43 | [disposable dispose]; 44 | 45 | __block BOOL disposed = NO; 46 | RACDisposable *d = [RACDisposable disposableWithBlock:^{ 47 | disposed = YES; 48 | }]; 49 | 50 | expect(disposed).to.beFalsy(); 51 | [disposable addDisposable:d]; 52 | expect(disposed).to.beTruthy(); 53 | }); 54 | 55 | it(@"should work when initialized with -init", ^{ 56 | RACCompoundDisposable *disposable = [[RACCompoundDisposable alloc] init]; 57 | 58 | __block BOOL disposed = NO; 59 | RACDisposable *d = [RACDisposable disposableWithBlock:^{ 60 | disposed = YES; 61 | }]; 62 | 63 | [disposable addDisposable:d]; 64 | expect(disposed).to.beFalsy(); 65 | 66 | [disposable dispose]; 67 | expect(disposed).to.beTruthy(); 68 | }); 69 | 70 | it(@"should allow disposables to be removed", ^{ 71 | RACCompoundDisposable *disposable = [[RACCompoundDisposable alloc] init]; 72 | 73 | __block BOOL disposed = NO; 74 | RACDisposable *d = [RACDisposable disposableWithBlock:^{ 75 | disposed = YES; 76 | }]; 77 | 78 | [disposable addDisposable:d]; 79 | [disposable removeDisposable:d]; 80 | 81 | [disposable dispose]; 82 | expect(disposed).to.beFalsy(); 83 | }); 84 | 85 | SpecEnd 86 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/ReactiveCocoa.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveCocoa.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/5/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | #import 36 | #import 37 | #import 38 | #import 39 | #import 40 | #import 41 | #import 42 | 43 | #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 44 | #import 45 | #import 46 | #import 47 | #import 48 | #import 49 | #import 50 | #elif TARGET_OS_MAC 51 | #import 52 | #import 53 | #import 54 | #import 55 | #import 56 | #endif 57 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACBinding.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACBinding.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 01/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACBinding.h" 10 | #import "RACDisposable.h" 11 | #import "RACTuple.h" 12 | #import "EXTScope.h" 13 | 14 | @interface RACBinding () 15 | 16 | // The signal exposed to callers. The property will behave like this signal 17 | // towards it's subscribers. 18 | @property (nonatomic, readonly, strong) RACSignal *exposedSignal; 19 | 20 | // The subscriber exposed to callers. The property will behave like this 21 | // subscriber towards the signals it's subscribed to. 22 | @property (nonatomic, readonly, strong) id exposedSubscriber; 23 | 24 | @end 25 | 26 | @implementation RACBinding 27 | 28 | #pragma mark RACSignal 29 | 30 | - (RACDisposable *)subscribe:(id)subscriber { 31 | return [self.exposedSignal subscribe:subscriber]; 32 | } 33 | 34 | #pragma mark 35 | 36 | - (void)sendNext:(id)value { 37 | [self.exposedSubscriber sendNext:value]; 38 | } 39 | 40 | - (void)sendError:(NSError *)error { 41 | [self.exposedSubscriber sendError:error]; 42 | } 43 | 44 | - (void)sendCompleted { 45 | [self.exposedSubscriber sendCompleted]; 46 | } 47 | 48 | - (void)didSubscribeWithDisposable:(RACDisposable *)disposable { 49 | [self.exposedSubscriber didSubscribeWithDisposable:disposable]; 50 | } 51 | 52 | #pragma mark API 53 | 54 | - (instancetype)initWithSignal:(RACSignal *)signal subscriber:(id)subscriber { 55 | self = [super init]; 56 | if (self == nil) return nil; 57 | 58 | @weakify(self); 59 | _exposedSignal = [RACSignal createSignal:^(id subscriber) { 60 | __block BOOL isFirstNext = YES; 61 | return [signal subscribeNext:^(RACTuple *x) { 62 | @strongify(self); 63 | if (isFirstNext || ![x.second isEqual:self]) { 64 | isFirstNext = NO; 65 | [subscriber sendNext:x.first]; 66 | } 67 | }]; 68 | }]; 69 | _exposedSubscriber = [RACSubscriber subscriberWithNext:^(id x) { 70 | @strongify(self); 71 | [subscriber sendNext:[RACTuple tupleWithObjects:x ?: RACTupleNil.tupleNil, self ?: RACTupleNil.tupleNil, nil]]; 72 | } error:nil completed:nil]; 73 | 74 | return self; 75 | } 76 | 77 | - (RACDisposable *)bindTo:(RACBinding *)binding { 78 | RACDisposable *bindingDisposable = [binding subscribe:self]; 79 | RACDisposable *selfDisposable = [[self skip:1] subscribe:binding]; 80 | return [RACDisposable disposableWithBlock:^{ 81 | [bindingDisposable dispose]; 82 | [selfDisposable dispose]; 83 | }]; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACDelegateProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACDelegateProxy.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/19/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACDelegateProxy.h" 10 | #import "RACObjCRuntime.h" 11 | #import "RACEventTrampoline.h" 12 | 13 | @interface RACDelegateProxy () 14 | 15 | @property (nonatomic, strong) Protocol *protocol; 16 | @property (nonatomic, weak) NSObject *delegator; 17 | @property (nonatomic, readonly, strong) NSMutableSet *trampolines; 18 | 19 | - (BOOL)trampolinesRespondToSelector:(SEL)aSelector; 20 | 21 | @end 22 | 23 | @implementation RACDelegateProxy 24 | 25 | + (instancetype)proxyWithProtocol:(Protocol *)protocol andDelegator:(NSObject *)delegator { 26 | if (![self conformsToProtocol:protocol]) { 27 | class_addProtocol(self.class, protocol); 28 | } 29 | 30 | RACDelegateProxy *proxy = [[self alloc] init]; 31 | proxy.protocol = protocol; 32 | proxy.delegator = delegator; 33 | return proxy; 34 | } 35 | 36 | - (instancetype)init { 37 | self = [super init]; 38 | if (self == nil) return nil; 39 | 40 | _trampolines = [[NSMutableSet alloc] init]; 41 | 42 | return self; 43 | } 44 | 45 | - (BOOL)respondsToSelector:(SEL)selector { 46 | // Add the original delegate to the autorelease pool, so it doesn't get 47 | // deallocated between this method call and -forwardInvocation:. 48 | __autoreleasing id actual = self.actualDelegate; 49 | if ([actual respondsToSelector:selector] || [self trampolinesRespondToSelector:selector]) return YES; 50 | 51 | return [super respondsToSelector:selector]; 52 | } 53 | 54 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 55 | return [NSMethodSignature signatureWithObjCTypes:[RACObjCRuntime getMethodTypesForMethod:selector inProtocol:self.protocol]]; 56 | } 57 | 58 | - (void)forwardInvocation:(NSInvocation *)invocation { 59 | SEL selector = invocation.selector; 60 | for (RACEventTrampoline *trampoline in self.trampolines) { 61 | [trampoline didGetDelegateEvent:selector sender:self.delegator]; 62 | } 63 | 64 | id actual = self.actualDelegate; 65 | if ([actual respondsToSelector:selector]) { 66 | [invocation invokeWithTarget:actual]; 67 | } 68 | } 69 | 70 | - (void)addTrampoline:(RACEventTrampoline *)trampoline { 71 | trampoline.proxy = self; 72 | [self.trampolines addObject:trampoline]; 73 | } 74 | 75 | - (BOOL)trampolinesRespondToSelector:(SEL)selector { 76 | for (RACEventTrampoline *trampoline in self.trampolines) { 77 | if (trampoline.delegateMethod == selector) { 78 | return YES; 79 | } 80 | } 81 | 82 | return NO; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACEventSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEventSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-01-07. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACEvent.h" 10 | 11 | SpecBegin(RACEvent) 12 | 13 | it(@"should return the singleton completed event", ^{ 14 | RACEvent *event = RACEvent.completedEvent; 15 | expect(event).notTo.beNil(); 16 | 17 | expect(event).to.beIdenticalTo(RACEvent.completedEvent); 18 | expect([event copy]).to.beIdenticalTo(event); 19 | 20 | expect(event.eventType).to.equal(RACEventTypeCompleted); 21 | expect(event.finished).to.beTruthy(); 22 | expect(event.error).to.beNil(); 23 | expect(event.value).to.beNil(); 24 | }); 25 | 26 | it(@"should return an error event", ^{ 27 | NSError *error = [NSError errorWithDomain:@"foo" code:1 userInfo:nil]; 28 | RACEvent *event = [RACEvent eventWithError:error]; 29 | expect(event).notTo.beNil(); 30 | 31 | expect(event).to.equal([RACEvent eventWithError:error]); 32 | expect([event copy]).to.equal(event); 33 | 34 | expect(event.eventType).to.equal(RACEventTypeError); 35 | expect(event.finished).to.beTruthy(); 36 | expect(event.error).to.equal(error); 37 | expect(event.value).to.beNil(); 38 | }); 39 | 40 | it(@"should return an error event with a nil error", ^{ 41 | RACEvent *event = [RACEvent eventWithError:nil]; 42 | expect(event).notTo.beNil(); 43 | 44 | expect(event).to.equal([RACEvent eventWithError:nil]); 45 | expect([event copy]).to.equal(event); 46 | 47 | expect(event.eventType).to.equal(RACEventTypeError); 48 | expect(event.finished).to.beTruthy(); 49 | expect(event.error).to.beNil(); 50 | expect(event.value).to.beNil(); 51 | }); 52 | 53 | it(@"should return a next event", ^{ 54 | NSString *value = @"foo"; 55 | RACEvent *event = [RACEvent eventWithValue:value]; 56 | expect(event).notTo.beNil(); 57 | 58 | expect(event).to.equal([RACEvent eventWithValue:value]); 59 | expect([event copy]).to.equal(event); 60 | 61 | expect(event.eventType).to.equal(RACEventTypeNext); 62 | expect(event.finished).to.beFalsy(); 63 | expect(event.error).to.beNil(); 64 | expect(event.value).to.equal(value); 65 | }); 66 | 67 | it(@"should return a next event with a nil value", ^{ 68 | RACEvent *event = [RACEvent eventWithValue:nil]; 69 | expect(event).notTo.beNil(); 70 | 71 | expect(event).to.equal([RACEvent eventWithValue:nil]); 72 | expect([event copy]).to.equal(event); 73 | 74 | expect(event.eventType).to.equal(RACEventTypeNext); 75 | expect(event.finished).to.beFalsy(); 76 | expect(event.error).to.beNil(); 77 | expect(event.value).to.beNil(); 78 | }); 79 | 80 | SpecEnd 81 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACObservablePropertySubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACObservablePropertySubject.h 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 27/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACPropertySubject.h" 10 | #import "EXTKeyPathCoding.h" 11 | #import "metamacros.h" 12 | 13 | // Convenience macro for creating bindings and binding them. 14 | // 15 | // If given just one argument, it's assumed to be a keypath or property on self. 16 | // If given two, the first argument is the object to which the keypath is 17 | // relative and the second is the keypath. 18 | // 19 | // If RACBind() is used as an lvalue (an assignee), the named property is bound 20 | // to the RACBinding provided on the right-hand side of the assignment. The 21 | // binding property's value is set to the value of the property being bound to, 22 | // then any changes to one property will be reflected on the other. 23 | // 24 | // If RACBind() is used as an rvalue, a RACBinding is returned. 25 | // 26 | // Examples: 27 | // RACBinding *binding = RACBind(self.property); 28 | // RACBind(self.property) = RACBind(otherObject, property); 29 | #define RACBind(...) metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(_RACBindObject(self, __VA_ARGS__))(_RACBindObject(__VA_ARGS__)) 30 | 31 | // Do not use this directly. Use the RACBind macro above. 32 | #define _RACBindObject(OBJ, KEYPATH) [RACObservablePropertySubject propertyWithTarget:OBJ keyPath:@keypath(OBJ, KEYPATH)][ @"binding" ] 33 | 34 | // A RACPropertySubject wrapper for KVO compliant key paths. 35 | // 36 | // New values of `keyPath` will be sent to the wrapper's subscribers and it's 37 | // bindings' subscribers. `keyPath` will be updated with values sent to the 38 | // wrapper or it's bindings. Subscribers of the wrapper or it's bindings will be 39 | // sent the current value of `keyPath`. 40 | // 41 | // Note: RACObservablePropertySubject is not thread-safe and should not observe 42 | // a property, or be bound to a RACProperty, whose value can be changed from 43 | // multiple threads at the same time. 44 | @interface RACObservablePropertySubject : RACPropertySubject 45 | 46 | // Returns a new RACPropertySubject wrapper for `keyPath` on `target` with a 47 | // starting value equal to the value of `keyPath` on `target`. 48 | + (instancetype)propertyWithTarget:(id)target keyPath:(NSString *)keyPath; 49 | 50 | @end 51 | 52 | // Methods needed for the convenience macro. Do not call explicitly. 53 | @interface RACObservablePropertySubject (RACBind) 54 | 55 | - (id)objectForKeyedSubscript:(id)key; 56 | - (void)setObject:(id)obj forKeyedSubscript:(id)key; 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/NSNotificationCenterRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenterRACSupportSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-12-07. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSNotificationCenter+RACSupport.h" 10 | 11 | static NSString * const TestNotification = @"TestNotification"; 12 | 13 | SpecBegin(NSNotificationCenterRACSupport) 14 | 15 | __block NSNotificationCenter *notificationCenter; 16 | 17 | beforeEach(^{ 18 | // The compiler gets confused and thinks you might be messaging 19 | // NSDistributedNotificationCenter otherwise. Wtf? 20 | notificationCenter = NSNotificationCenter.defaultCenter; 21 | }); 22 | 23 | it(@"should send the notification when posted by any object", ^{ 24 | RACSignal *signal = [notificationCenter rac_addObserverForName:TestNotification object:nil]; 25 | 26 | __block NSUInteger count = 0; 27 | [signal subscribeNext:^(NSNotification *notification) { 28 | ++count; 29 | 30 | expect(notification).to.beKindOf(NSNotification.class); 31 | expect(notification.name).to.equal(TestNotification); 32 | }]; 33 | 34 | expect(count).to.equal(0); 35 | 36 | [notificationCenter postNotificationName:TestNotification object:nil]; 37 | expect(count).to.equal(1); 38 | 39 | [notificationCenter postNotificationName:TestNotification object:self]; 40 | expect(count).to.equal(2); 41 | }); 42 | 43 | it(@"should send the notification when posted by a specific object", ^{ 44 | RACSignal *signal = [notificationCenter rac_addObserverForName:TestNotification object:self]; 45 | 46 | __block NSUInteger count = 0; 47 | [signal subscribeNext:^(NSNotification *notification) { 48 | ++count; 49 | 50 | expect(notification).to.beKindOf(NSNotification.class); 51 | expect(notification.name).to.equal(TestNotification); 52 | expect(notification.object).to.equal(self); 53 | }]; 54 | 55 | expect(count).to.equal(0); 56 | 57 | [notificationCenter postNotificationName:TestNotification object:nil]; 58 | expect(count).to.equal(0); 59 | 60 | [notificationCenter postNotificationName:TestNotification object:self]; 61 | expect(count).to.equal(1); 62 | }); 63 | 64 | it(@"shouldn't strongly capture the notification object", ^{ 65 | RACSignal *signal __attribute__((objc_precise_lifetime)); 66 | __block BOOL dealloced = NO; 67 | @autoreleasepool { 68 | NSObject *notificationObject = [[NSObject alloc] init]; 69 | [notificationObject rac_addDeallocDisposable:[RACDisposable disposableWithBlock:^{ 70 | dealloced = YES; 71 | }]]; 72 | signal = [notificationCenter rac_addObserverForName:TestNotification object:notificationObject]; 73 | } 74 | 75 | expect(dealloced).to.beTruthy(); 76 | }); 77 | 78 | SpecEnd 79 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACKVOTrampoline.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACKVOTrampoline.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 1/15/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACKVOTrampoline.h" 10 | #import "NSObject+RACKVOWrapper.h" 11 | #import "NSObject+RACKVOWrapperPrivate.h" 12 | 13 | static void *RACKVOWrapperContext = &RACKVOWrapperContext; 14 | 15 | @interface RACKVOTrampoline () 16 | 17 | // The keypath which the trampoline is observing. 18 | @property (nonatomic, readonly, copy) NSString *keyPath; 19 | 20 | // These properties should only be manipulated while synchronized on the 21 | // receiver. 22 | @property (nonatomic, readonly, copy) RACKVOBlock block; 23 | @property (nonatomic, readonly, unsafe_unretained) NSObject *target; 24 | @property (nonatomic, readonly, unsafe_unretained) NSObject *observer; 25 | 26 | @end 27 | 28 | @implementation RACKVOTrampoline 29 | 30 | #pragma mark Lifecycle 31 | 32 | - (id)initWithTarget:(NSObject *)target observer:(NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block { 33 | NSCParameterAssert(target != nil); 34 | NSCParameterAssert(keyPath != nil); 35 | NSCParameterAssert(block != nil); 36 | 37 | self = [super init]; 38 | if (self == nil) return nil; 39 | 40 | _keyPath = [keyPath copy]; 41 | 42 | _block = [block copy]; 43 | _target = target; 44 | _observer = observer; 45 | 46 | [self.target addObserver:self forKeyPath:self.keyPath options:options context:&RACKVOWrapperContext]; 47 | [self.target rac_addKVOTrampoline:self]; 48 | [self.observer rac_addKVOTrampoline:self]; 49 | 50 | return self; 51 | } 52 | 53 | - (void)dealloc { 54 | [self stopObserving]; 55 | } 56 | 57 | #pragma mark Observation 58 | 59 | - (void)stopObserving { 60 | NSObject *target; 61 | NSObject *observer; 62 | 63 | @synchronized (self) { 64 | _block = nil; 65 | 66 | target = self.target; 67 | observer = self.observer; 68 | 69 | _target = nil; 70 | _observer = nil; 71 | } 72 | 73 | [target rac_removeKVOTrampoline:self]; 74 | [observer rac_removeKVOTrampoline:self]; 75 | 76 | [target removeObserver:self forKeyPath:self.keyPath context:&RACKVOWrapperContext]; 77 | } 78 | 79 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 80 | if (context != &RACKVOWrapperContext) { 81 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 82 | return; 83 | } 84 | 85 | RACKVOBlock block; 86 | id observer; 87 | id target; 88 | 89 | @synchronized (self) { 90 | block = self.block; 91 | observer = self.observer; 92 | target = self.target; 93 | } 94 | 95 | if (block == nil) return; 96 | 97 | block(target, observer, change); 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Documentation/DifferencesFromRx.md: -------------------------------------------------------------------------------- 1 | # Differences from Rx 2 | 3 | ReactiveCocoa (RAC) is significantly inspired by .NET's [Reactive 4 | Extensions](http://msdn.microsoft.com/en-us/data/gg577609.aspx) (Rx), but it is not 5 | a direct port. Some concepts or interfaces presented in RAC may be initially 6 | confusing to a developer already familiar with Rx, but it's usually possible to 7 | express the same algorithms. 8 | 9 | Some of the differences, like the naming of methods and classes, are meant to 10 | keep RAC in line with existing Cocoa conventions. Other differences are intended 11 | as improvements over Rx, or may be inspired by other functional reactive 12 | programming paradigms (like the [Elm programming 13 | language](http://elm-lang.org)). 14 | 15 | Here, we'll attempt to document the high-level differences between RAC and Rx. 16 | 17 | ## Interfaces 18 | 19 | RAC does not offer protocols that correspond to the `IEnumerable` and 20 | `IObservable` interfaces in .NET. Instead, the functionality is covered by three 21 | main classes: 22 | 23 | * **[RACStream](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoaFramework/ReactiveCocoa/RACStream.h)** 24 | is an abstract class that implements stream operations using a few basic 25 | primitives. The equivalents to generic LINQ operators can generally be found 26 | on this class. 27 | * **[RACSignal](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoaFramework/ReactiveCocoa/RACSignal.h)** 28 | is a concrete subclass of `RACStream` that implements a _push-driven_ stream, 29 | much like `IObservable`. Time-based operators, or methods dealing with the 30 | `completed` and `error` events, can be found on this class or in the 31 | [RACSignal+Operations](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoaFramework/ReactiveCocoa/RACSignal%2BOperations.h) 32 | category upon it. 33 | * **[RACSequence](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoaFramework/ReactiveCocoa/RACSequence.h)** 34 | is a concrete subclass of `RACStream` that implements a _pull-driven_ stream, 35 | much like `IEnumerable`. 36 | 37 | ## Names of Stream Operations 38 | 39 | RAC generally uses LINQ-style naming for its stream methods. Most of the 40 | exceptions are inspired by significantly better alternatives in Haskell or Elm. 41 | 42 | Notable differences include: 43 | 44 | * `-map:` instead of `Select` 45 | * `-filter:` instead of `Where` 46 | * `-flatten` instead of `Merge` 47 | * `-flattenMap:` instead of `SelectMany` 48 | 49 | LINQ operators that go by different names in RAC (but behave more or less 50 | equivalently) will be referenced from method documentation, like so: 51 | 52 | ```objc 53 | // Maps `block` across the values in the receiver. 54 | // 55 | // This corresponds to the `Select` method in Rx. 56 | // 57 | // Returns a new stream with the mapped values. 58 | - (instancetype)map:(id (^)(id value))block; 59 | ``` 60 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACCompoundDisposable.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACCompoundDisposable.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACCompoundDisposable.h" 10 | #import "EXTScope.h" 11 | #import 12 | 13 | @interface RACCompoundDisposable () { 14 | // Used for synchronization. 15 | OSSpinLock _spinLock; 16 | } 17 | 18 | // These properties should only be accessed while _spinLock is held. 19 | @property (nonatomic, strong) NSMutableArray *disposables; 20 | @property (nonatomic, assign, getter = isDisposed) BOOL disposed; 21 | 22 | @end 23 | 24 | @implementation RACCompoundDisposable 25 | 26 | #pragma mark Initializers 27 | 28 | + (instancetype)compoundDisposable { 29 | return [[self alloc] initWithDisposables:nil]; 30 | } 31 | 32 | + (instancetype)compoundDisposableWithDisposables:(NSArray *)disposables { 33 | return [[self alloc] initWithDisposables:disposables]; 34 | } 35 | 36 | - (id)init { 37 | self = [super init]; 38 | if (self == nil) return nil; 39 | 40 | _disposables = [NSMutableArray array]; 41 | 42 | return self; 43 | } 44 | 45 | - (id)initWithDisposables:(NSArray *)disposables { 46 | self = [self init]; 47 | if (self == nil) return nil; 48 | 49 | if (disposables != nil) [self.disposables addObjectsFromArray:disposables]; 50 | 51 | return self; 52 | } 53 | 54 | #pragma mark Compound 55 | 56 | - (void)addDisposable:(RACDisposable *)disposable { 57 | NSCParameterAssert(disposable != nil); 58 | NSCParameterAssert(disposable != self); 59 | 60 | BOOL shouldDispose = NO; 61 | 62 | { 63 | OSSpinLockLock(&_spinLock); 64 | 65 | // Ensures exception safety. 66 | @onExit { 67 | OSSpinLockUnlock(&_spinLock); 68 | }; 69 | 70 | if (self.disposed) { 71 | shouldDispose = YES; 72 | } else { 73 | [self.disposables addObject:disposable]; 74 | } 75 | } 76 | 77 | // Performed outside of the lock in case the compound disposable is used 78 | // recursively. 79 | if (shouldDispose) [disposable dispose]; 80 | } 81 | 82 | - (void)removeDisposable:(RACDisposable *)disposable { 83 | if (disposable == nil) return; 84 | 85 | OSSpinLockLock(&_spinLock); 86 | 87 | // Ensures exception safety. 88 | @onExit { 89 | OSSpinLockUnlock(&_spinLock); 90 | }; 91 | 92 | [self.disposables removeObjectIdenticalTo:disposable]; 93 | } 94 | 95 | #pragma mark RACDisposable 96 | 97 | - (void)dispose { 98 | NSArray *disposables = nil; 99 | 100 | { 101 | OSSpinLockLock(&_spinLock); 102 | 103 | // Ensures exception safety. 104 | @onExit { 105 | OSSpinLockUnlock(&_spinLock); 106 | }; 107 | 108 | self.disposed = YES; 109 | 110 | disposables = self.disposables; 111 | self.disposables = nil; 112 | } 113 | 114 | // Performed outside of the lock in case the compound disposable is used 115 | // recursively. 116 | [disposables makeObjectsPerformSelector:@selector(dispose)]; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEvent.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2013-01-07. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACEvent.h" 10 | 11 | @interface RACEvent () 12 | 13 | // An object associated with this event. This will be used for the error and 14 | // value properties. 15 | @property (nonatomic, strong, readonly) id object; 16 | 17 | // Initializes the receiver with the given type and object. 18 | - (id)initWithEventType:(RACEventType)type object:(id)object; 19 | 20 | @end 21 | 22 | @implementation RACEvent 23 | 24 | #pragma mark Properties 25 | 26 | - (BOOL)isFinished { 27 | return self.eventType == RACEventTypeCompleted || self.eventType == RACEventTypeError; 28 | } 29 | 30 | - (NSError *)error { 31 | return (self.eventType == RACEventTypeError ? self.object : nil); 32 | } 33 | 34 | - (id)value { 35 | return (self.eventType == RACEventTypeNext ? self.object : nil); 36 | } 37 | 38 | #pragma mark Lifecycle 39 | 40 | + (instancetype)completedEvent { 41 | static dispatch_once_t pred; 42 | static id singleton; 43 | 44 | dispatch_once(&pred, ^{ 45 | singleton = [[self alloc] initWithEventType:RACEventTypeCompleted object:nil]; 46 | }); 47 | 48 | return singleton; 49 | } 50 | 51 | + (instancetype)eventWithError:(NSError *)error { 52 | return [[self alloc] initWithEventType:RACEventTypeError object:error]; 53 | } 54 | 55 | + (instancetype)eventWithValue:(id)value { 56 | return [[self alloc] initWithEventType:RACEventTypeNext object:value]; 57 | } 58 | 59 | - (id)initWithEventType:(RACEventType)type object:(id)object { 60 | self = [super init]; 61 | if (self == nil) return nil; 62 | 63 | _eventType = type; 64 | _object = object; 65 | 66 | return self; 67 | } 68 | 69 | #pragma mark NSCopying 70 | 71 | - (id)copyWithZone:(NSZone *)zone { 72 | return self; 73 | } 74 | 75 | #pragma mark NSObject 76 | 77 | - (NSString *)description { 78 | NSString *eventDescription = nil; 79 | 80 | switch (self.eventType) { 81 | case RACEventTypeCompleted: 82 | eventDescription = @"completed"; 83 | break; 84 | 85 | case RACEventTypeError: 86 | eventDescription = [NSString stringWithFormat:@"error = %@", self.object]; 87 | break; 88 | 89 | case RACEventTypeNext: 90 | eventDescription = [NSString stringWithFormat:@"next = %@", self.object]; 91 | break; 92 | 93 | default: 94 | NSCAssert(NO, @"Unrecognized event type: %i", (int)self.eventType); 95 | } 96 | 97 | return [NSString stringWithFormat:@"<%@: %p>{ %@ }", self.class, self, eventDescription]; 98 | } 99 | 100 | - (NSUInteger)hash { 101 | return self.eventType ^ [self.object hash]; 102 | } 103 | 104 | - (BOOL)isEqual:(id)event { 105 | if (event == self) return YES; 106 | if (![event isKindOfClass:RACEvent.class]) return NO; 107 | if (self.eventType != [event eventType]) return NO; 108 | 109 | // Catches the nil case too. 110 | return self.object == [event object] || [self.object isEqual:[event object]]; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACBacktraceSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACBacktraceSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-12-24. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACBacktrace.h" 10 | #import "RACScheduler.h" 11 | 12 | #ifdef DEBUG 13 | 14 | static RACBacktrace *previousBacktrace; 15 | 16 | static void capturePreviousBacktrace(void *context) { 17 | previousBacktrace = [RACBacktrace captureBacktrace].previousThreadBacktrace; 18 | } 19 | 20 | SpecBegin(RACBacktrace) 21 | 22 | __block dispatch_block_t block; 23 | 24 | beforeEach(^{ 25 | expect([RACBacktrace captureBacktrace].previousThreadBacktrace).to.beNil(); 26 | previousBacktrace = nil; 27 | 28 | block = ^{ 29 | capturePreviousBacktrace(NULL); 30 | }; 31 | }); 32 | 33 | it(@"should capture the current backtrace", ^{ 34 | RACBacktrace *backtrace = [RACBacktrace captureBacktrace]; 35 | expect(backtrace).notTo.beNil(); 36 | }); 37 | 38 | describe(@"with a GCD queue", ^{ 39 | __block dispatch_queue_t queue; 40 | 41 | beforeEach(^{ 42 | queue = dispatch_queue_create("com.github.ReactiveCocoa.RACBacktraceSpec", DISPATCH_QUEUE_SERIAL); 43 | }); 44 | 45 | afterEach(^{ 46 | dispatch_barrier_sync(queue, ^{}); 47 | dispatch_release(queue); 48 | }); 49 | 50 | it(@"should trace across dispatch_async", ^{ 51 | dispatch_async(queue, block); 52 | expect(previousBacktrace).willNot.beNil(); 53 | }); 54 | 55 | it(@"should trace across dispatch_async to the main thread", ^{ 56 | dispatch_async(queue, ^{ 57 | dispatch_async(dispatch_get_main_queue(), block); 58 | }); 59 | 60 | expect(previousBacktrace).willNot.beNil(); 61 | }); 62 | 63 | it(@"should trace across dispatch_async_f", ^{ 64 | dispatch_async_f(queue, NULL, &capturePreviousBacktrace); 65 | expect(previousBacktrace).willNot.beNil(); 66 | }); 67 | 68 | it(@"should trace across dispatch_barrier_async", ^{ 69 | dispatch_barrier_async(queue, block); 70 | expect(previousBacktrace).willNot.beNil(); 71 | }); 72 | 73 | it(@"should trace across dispatch_barrier_async_f", ^{ 74 | dispatch_barrier_async_f(queue, NULL, &capturePreviousBacktrace); 75 | expect(previousBacktrace).willNot.beNil(); 76 | }); 77 | 78 | it(@"should trace across dispatch_after", ^{ 79 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1), queue, block); 80 | expect(previousBacktrace).willNot.beNil(); 81 | }); 82 | 83 | it(@"should trace across dispatch_after_f", ^{ 84 | dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 1), queue, NULL, &capturePreviousBacktrace); 85 | expect(previousBacktrace).willNot.beNil(); 86 | }); 87 | }); 88 | 89 | it(@"should trace across a RACScheduler", ^{ 90 | [[RACScheduler scheduler] schedule:block]; 91 | expect(previousBacktrace).willNot.beNil(); 92 | }); 93 | 94 | it(@"should trace across an NSOperationQueue", ^{ 95 | NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 96 | [queue addOperationWithBlock:block]; 97 | expect(previousBacktrace).willNot.beNil(); 98 | }); 99 | 100 | SpecEnd 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACPropertySubject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySubject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Uri Baghin on 16/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACPropertySubject+Private.h" 10 | #import "RACBinding+Private.h" 11 | #import "RACDisposable.h" 12 | #import "RACReplaySubject.h" 13 | #import "RACTuple.h" 14 | #import "EXTScope.h" 15 | 16 | @interface RACPropertySubject () 17 | 18 | // The signal passed to `-initWithSignal:subscriber:`. Refer to the method's 19 | // docs for details. 20 | @property (nonatomic, readonly, strong) RACSignal *signal; 21 | 22 | // The subscriber passed to `-initWithSignal:subscriber:`. Refer to the method's 23 | // docs for details. 24 | @property (nonatomic, readonly, strong) id subscriber; 25 | 26 | // The signal exposed to callers. The property will behave like this signal 27 | // towards it's subscribers. 28 | @property (nonatomic, readonly, strong) RACSignal *exposedSignal; 29 | 30 | // The subscriber exposed to callers. The property will behave like this 31 | // subscriber towards the signals it's subscribed to. 32 | @property (nonatomic, readonly, strong) id exposedSubscriber; 33 | 34 | @end 35 | 36 | @implementation RACPropertySubject 37 | 38 | #pragma mark NSObject 39 | 40 | - (id)init { 41 | RACReplaySubject *backing = [RACReplaySubject replaySubjectWithCapacity:1]; 42 | [backing sendNext:[RACTuple tupleWithObjects:RACTupleNil.tupleNil, RACTupleNil.tupleNil, nil]]; 43 | return [self initWithSignal:backing subscriber:backing]; 44 | } 45 | 46 | #pragma mark RACSignal 47 | 48 | - (RACDisposable *)subscribe:(id)subscriber { 49 | return [self.exposedSignal subscribe:subscriber]; 50 | } 51 | 52 | #pragma mark 53 | 54 | - (void)sendNext:(id)value { 55 | [self.exposedSubscriber sendNext:value]; 56 | } 57 | 58 | - (void)sendError:(NSError *)error { 59 | [self.exposedSubscriber sendError:error]; 60 | } 61 | 62 | - (void)sendCompleted { 63 | [self.exposedSubscriber sendCompleted]; 64 | } 65 | 66 | - (void)didSubscribeWithDisposable:(RACDisposable *)disposable { 67 | [self.exposedSubscriber didSubscribeWithDisposable:disposable]; 68 | } 69 | 70 | #pragma mark API 71 | 72 | - (instancetype)initWithSignal:(RACSignal *)signal subscriber:(id)subscriber { 73 | self = [super init]; 74 | if (self == nil) return nil; 75 | 76 | _signal = signal; 77 | _subscriber = subscriber; 78 | 79 | @weakify(self); 80 | _exposedSignal = [_signal map:^(RACTuple *value) { 81 | return value.first; 82 | }]; 83 | _exposedSubscriber = [RACSubscriber subscriberWithNext:^(id x) { 84 | [subscriber sendNext:[RACTuple tupleWithObjects:x, RACTupleNil.tupleNil, nil]]; 85 | } error:^(NSError *error) { 86 | @strongify(self); 87 | NSCAssert(NO, @"Received error in RACPropertySubject %@: %@", self, error); 88 | 89 | // Log the error if we're running with assertions disabled. 90 | NSLog(@"Received error in RACPropertySubject %@: %@", self, error); 91 | } completed:nil]; 92 | 93 | return self; 94 | } 95 | 96 | + (instancetype)property { 97 | return [self subject]; 98 | } 99 | 100 | - (RACBinding *)binding { 101 | return [[RACBinding alloc] initWithSignal:self.signal subscriber:self.subscriber]; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/NSObject+RACKVOWrapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACKVOWrapper.m 3 | // GitHub 4 | // 5 | // Created by Josh Abernathy on 10/11/11. 6 | // Copyright (c) 2011 GitHub. All rights reserved. 7 | // 8 | 9 | #import "NSObject+RACKVOWrapper.h" 10 | #import "NSObject+RACKVOWrapperPrivate.h" 11 | #import "RACSwizzling.h" 12 | #import "RACKVOTrampoline.h" 13 | #import 14 | 15 | static void *RACKVOTrampolinesKey = &RACKVOTrampolinesKey; 16 | 17 | static NSMutableSet *swizzledClasses() { 18 | static dispatch_once_t onceToken; 19 | static NSMutableSet *swizzledClasses = nil; 20 | dispatch_once(&onceToken, ^{ 21 | swizzledClasses = [[NSMutableSet alloc] init]; 22 | }); 23 | 24 | return swizzledClasses; 25 | } 26 | 27 | @implementation NSObject (RACKVOWrapper) 28 | 29 | - (RACKVOTrampoline *)rac_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block { 30 | void (^swizzle)(Class) = ^(Class classToSwizzle){ 31 | NSString *className = NSStringFromClass(classToSwizzle); 32 | if ([swizzledClasses() containsObject:className]) return; 33 | 34 | SEL deallocSelector = sel_registerName("dealloc"); 35 | 36 | Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector); 37 | void (*originalDealloc)(id, SEL) = (__typeof__(originalDealloc))method_getImplementation(deallocMethod); 38 | 39 | id newDealloc = ^(__unsafe_unretained NSObject *self) { 40 | NSSet *trampolines; 41 | 42 | @synchronized (self) { 43 | trampolines = [self.RACKVOTrampolines copy]; 44 | self.RACKVOTrampolines = nil; 45 | } 46 | 47 | // If we're currently delivering a KVO callback then niling 48 | // the trampoline set might not dealloc the trampoline and 49 | // therefore make them be dealloc'd. So we need to manually 50 | // stop observing on all of them as well. 51 | [trampolines makeObjectsPerformSelector:@selector(stopObserving)]; 52 | 53 | originalDealloc(self, deallocSelector); 54 | }; 55 | 56 | class_replaceMethod(classToSwizzle, deallocSelector, imp_implementationWithBlock(newDealloc), method_getTypeEncoding(deallocMethod)); 57 | 58 | [swizzledClasses() addObject:className]; 59 | }; 60 | 61 | // We swizzle the dealloc for both the object being observed and the 62 | // observer of the observation. Because when either gets dealloc'd, we need 63 | // to tear down the observation. 64 | @synchronized (swizzledClasses()) { 65 | swizzle(self.class); 66 | swizzle(observer.class); 67 | } 68 | 69 | return [[RACKVOTrampoline alloc] initWithTarget:self observer:observer keyPath:keyPath options:options block:block]; 70 | } 71 | 72 | - (void)rac_addKVOTrampoline:(RACKVOTrampoline *)trampoline { 73 | NSCParameterAssert(trampoline != nil); 74 | 75 | @synchronized (self) { 76 | if (self.RACKVOTrampolines == nil) { 77 | self.RACKVOTrampolines = [NSMutableArray array]; 78 | } 79 | 80 | [self.RACKVOTrampolines addObject:trampoline]; 81 | } 82 | } 83 | 84 | - (void)rac_removeKVOTrampoline:(RACKVOTrampoline *)trampoline { 85 | @synchronized (self) { 86 | [self.RACKVOTrampolines removeObject:trampoline]; 87 | } 88 | } 89 | 90 | - (NSMutableArray *)RACKVOTrampolines { 91 | return objc_getAssociatedObject(self, RACKVOTrampolinesKey); 92 | } 93 | 94 | - (void)setRACKVOTrampolines:(NSMutableArray *)set { 95 | objc_setAssociatedObject(self, RACKVOTrampolinesKey, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACSubscriberSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriberSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-27. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubscriberExamples.h" 10 | 11 | #import "RACSubscriber.h" 12 | #import 13 | 14 | SpecBegin(RACSubscriber) 15 | 16 | __block RACSubscriber *subscriber; 17 | __block NSMutableArray *values; 18 | 19 | __block volatile BOOL finished; 20 | __block volatile int32_t nextsAfterFinished; 21 | 22 | __block BOOL success; 23 | __block NSError *error; 24 | 25 | beforeEach(^{ 26 | values = [NSMutableArray array]; 27 | 28 | finished = NO; 29 | nextsAfterFinished = 0; 30 | 31 | success = YES; 32 | error = nil; 33 | 34 | subscriber = [RACSubscriber subscriberWithNext:^(id value) { 35 | if (finished) OSAtomicIncrement32Barrier(&nextsAfterFinished); 36 | 37 | [values addObject:value]; 38 | } error:^(NSError *e) { 39 | error = e; 40 | success = NO; 41 | } completed:^{ 42 | success = YES; 43 | }]; 44 | }); 45 | 46 | itShouldBehaveLike(RACSubscriberExamples, ^{ 47 | return @{ 48 | RACSubscriberExampleSubscriber: subscriber, 49 | RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy], 50 | RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy], 51 | RACSubscriberExampleSuccessBlock: [^{ return success; } copy] 52 | }; 53 | }); 54 | 55 | describe(@"finishing", ^{ 56 | __block void (^sendValues)(void); 57 | __block BOOL expectedSuccess; 58 | 59 | __block dispatch_group_t dispatchGroup; 60 | __block dispatch_queue_t concurrentQueue; 61 | 62 | beforeEach(^{ 63 | dispatchGroup = dispatch_group_create(); 64 | expect(dispatchGroup).notTo.beNil(); 65 | 66 | concurrentQueue = dispatch_queue_create("com.github.ReactiveCocoa.RACSubscriberSpec", DISPATCH_QUEUE_CONCURRENT); 67 | expect(concurrentQueue).notTo.beNil(); 68 | 69 | dispatch_suspend(concurrentQueue); 70 | 71 | sendValues = [^{ 72 | for (NSUInteger i = 0; i < 15; i++) { 73 | dispatch_group_async(dispatchGroup, concurrentQueue, ^{ 74 | [subscriber sendNext:@(i)]; 75 | }); 76 | } 77 | } copy]; 78 | 79 | sendValues(); 80 | }); 81 | 82 | afterEach(^{ 83 | sendValues(); 84 | dispatch_resume(concurrentQueue); 85 | 86 | // Time out after one second. 87 | dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); 88 | expect(dispatch_group_wait(dispatchGroup, time)).to.equal(0); 89 | 90 | dispatch_release(dispatchGroup); 91 | dispatchGroup = NULL; 92 | 93 | dispatch_release(concurrentQueue); 94 | concurrentQueue = NULL; 95 | 96 | expect(nextsAfterFinished).to.equal(0); 97 | 98 | if (expectedSuccess) { 99 | expect(success).to.beTruthy(); 100 | expect(error).to.beNil(); 101 | } else { 102 | expect(success).to.beFalsy(); 103 | } 104 | }); 105 | 106 | it(@"should never invoke next after sending completed", ^{ 107 | expectedSuccess = YES; 108 | 109 | dispatch_group_async(dispatchGroup, concurrentQueue, ^{ 110 | [subscriber sendCompleted]; 111 | 112 | finished = YES; 113 | OSMemoryBarrier(); 114 | }); 115 | }); 116 | 117 | it(@"should never invoke next after sending error", ^{ 118 | expectedSuccess = NO; 119 | 120 | dispatch_group_async(dispatchGroup, concurrentQueue, ^{ 121 | [subscriber sendError:nil]; 122 | 123 | finished = YES; 124 | OSMemoryBarrier(); 125 | }); 126 | }); 127 | }); 128 | 129 | SpecEnd 130 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACPropertySignalExamples.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySignalExamples.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 9/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTestObject.h" 10 | 11 | #import "EXTKeyPathCoding.h" 12 | #import "NSObject+RACPropertySubscribing.h" 13 | #import "RACDisposable.h" 14 | #import "RACSubject.h" 15 | 16 | NSString * const RACPropertySignalExamples = @"RACPropertySignalExamples"; 17 | NSString * const RACPropertySignalExamplesSetupBlock = @"RACPropertySignalExamplesSetupBlock"; 18 | 19 | SharedExampleGroupsBegin(RACPropertySignalExamples) 20 | 21 | sharedExamplesFor(RACPropertySignalExamples, ^(NSDictionary *data) { 22 | __block RACTestObject *testObject = nil; 23 | __block void (^setupBlock)(RACTestObject *, NSString *keyPath, RACSignal *); 24 | 25 | beforeEach(^{ 26 | setupBlock = data[RACPropertySignalExamplesSetupBlock]; 27 | testObject = [[RACTestObject alloc] init]; 28 | }); 29 | 30 | it(@"should set the value of the property with the latest value from the signal", ^{ 31 | RACSubject *subject = [RACSubject subject]; 32 | setupBlock(testObject, @keypath(testObject.objectValue), subject); 33 | expect(testObject.objectValue).to.beNil(); 34 | 35 | [subject sendNext:@1]; 36 | expect(testObject.objectValue).to.equal(@1); 37 | 38 | [subject sendNext:@2]; 39 | expect(testObject.objectValue).to.equal(@2); 40 | 41 | [subject sendNext:nil]; 42 | expect(testObject.objectValue).to.beNil(); 43 | }); 44 | 45 | it(@"should set the value of a non-object property with the latest value from the signal", ^{ 46 | RACSubject *subject = [RACSubject subject]; 47 | setupBlock(testObject, @keypath(testObject.integerValue), subject); 48 | expect(testObject.integerValue).to.equal(0); 49 | 50 | [subject sendNext:@1]; 51 | expect(testObject.integerValue).to.equal(1); 52 | 53 | [subject sendNext:@2]; 54 | expect(testObject.integerValue).to.equal(2); 55 | 56 | [subject sendNext:@0]; 57 | expect(testObject.integerValue).to.equal(0); 58 | 59 | [subject sendNext:nil]; 60 | expect(testObject.integerValue).to.equal(0); 61 | }); 62 | 63 | it(@"should retain intermediate signals when binding", ^{ 64 | RACSubject *subject = [RACSubject subject]; 65 | expect(subject).notTo.beNil(); 66 | 67 | __block BOOL deallocd = NO; 68 | 69 | @autoreleasepool { 70 | @autoreleasepool { 71 | RACSignal *intermediateSignal = [subject map:^(NSNumber *num) { 72 | return @(num.integerValue + 1); 73 | }]; 74 | 75 | expect(intermediateSignal).notTo.beNil(); 76 | 77 | [intermediateSignal rac_addDeallocDisposable:[RACDisposable disposableWithBlock:^{ 78 | deallocd = YES; 79 | }]]; 80 | 81 | setupBlock(testObject, @keypath(testObject.integerValue), intermediateSignal); 82 | } 83 | 84 | // Spin the run loop to account for RAC magic that retains the 85 | // signal for a single iteration. 86 | [NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; 87 | } 88 | 89 | expect(deallocd).to.beFalsy(); 90 | 91 | [subject sendNext:@5]; 92 | expect(testObject.integerValue).to.equal(6); 93 | 94 | [subject sendNext:@6]; 95 | expect(testObject.integerValue).to.equal(7); 96 | 97 | expect(deallocd).to.beFalsy(); 98 | [subject sendCompleted]; 99 | 100 | // Can't test deallocd again, because it's legal for the chain to be 101 | // retained until the object or the original signal is destroyed. 102 | }); 103 | }); 104 | 105 | SharedExampleGroupsEnd 106 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACReplaySubject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACReplaySubject.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 3/14/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACReplaySubject.h" 10 | #import "RACDisposable.h" 11 | #import "RACScheduler+Private.h" 12 | #import "RACSubscriber.h" 13 | #import "RACTuple.h" 14 | #import "RACCompoundDisposable.h" 15 | #import 16 | 17 | const NSUInteger RACReplaySubjectUnlimitedCapacity = 0; 18 | 19 | @interface RACReplaySubject () 20 | 21 | @property (nonatomic, assign, readonly) NSUInteger capacity; 22 | 23 | // These properties should only be modified while synchronized on self. 24 | @property (nonatomic, strong, readonly) NSMutableArray *valuesReceived; 25 | @property (nonatomic, assign) BOOL hasCompleted; 26 | @property (nonatomic, assign) BOOL hasError; 27 | @property (nonatomic, strong) NSError *error; 28 | 29 | @end 30 | 31 | 32 | @implementation RACReplaySubject 33 | 34 | #pragma mark Lifecycle 35 | 36 | + (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity { 37 | return [[self alloc] initWithCapacity:capacity]; 38 | } 39 | 40 | - (instancetype)init { 41 | return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity]; 42 | } 43 | 44 | - (instancetype)initWithCapacity:(NSUInteger)capacity { 45 | self = [super init]; 46 | if (self == nil) return nil; 47 | 48 | _capacity = capacity; 49 | _valuesReceived = [NSMutableArray arrayWithCapacity:capacity]; 50 | 51 | return self; 52 | } 53 | 54 | #pragma mark RACSignal 55 | 56 | - (RACDisposable *)subscribe:(id)subscriber { 57 | __block volatile uint32_t disposed = 0; 58 | 59 | RACDisposable *stopDisposable = [RACDisposable disposableWithBlock:^{ 60 | OSAtomicOr32Barrier(1, &disposed); 61 | }]; 62 | 63 | RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ stopDisposable ]]; 64 | RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ 65 | @synchronized (self) { 66 | for (id value in self.valuesReceived) { 67 | if (disposed != 0) return; 68 | 69 | [subscriber sendNext:([value isKindOfClass:RACTupleNil.class] ? nil : value)]; 70 | } 71 | 72 | if (disposed != 0) return; 73 | 74 | if (self.hasCompleted) { 75 | [subscriber sendCompleted]; 76 | } else if (self.hasError) { 77 | [subscriber sendError:self.error]; 78 | } else { 79 | RACDisposable *subscriptionDisposable = [super subscribe:subscriber]; 80 | [compoundDisposable addDisposable:subscriptionDisposable]; 81 | } 82 | } 83 | }]; 84 | 85 | if (schedulingDisposable != nil) [compoundDisposable addDisposable:schedulingDisposable]; 86 | 87 | return compoundDisposable; 88 | } 89 | 90 | #pragma mark RACSubscriber 91 | 92 | - (void)sendNext:(id)value { 93 | @synchronized (self) { 94 | [self.valuesReceived addObject:value ?: RACTupleNil.tupleNil]; 95 | [super sendNext:value]; 96 | 97 | if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) { 98 | [self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)]; 99 | } 100 | } 101 | } 102 | 103 | - (void)sendCompleted { 104 | @synchronized (self) { 105 | self.hasCompleted = YES; 106 | [super sendCompleted]; 107 | } 108 | } 109 | 110 | - (void)sendError:(NSError *)e { 111 | @synchronized (self) { 112 | self.hasError = YES; 113 | self.error = e; 114 | [super sendError:e]; 115 | } 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoaTests/RACMulticastConnectionSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACMulticastConnectionSpec.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Josh Abernathy on 10/8/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACMulticastConnection.h" 10 | #import "RACDisposable.h" 11 | #import "RACSignal+Operations.h" 12 | #import "RACSubscriber.h" 13 | #import "RACReplaySubject.h" 14 | 15 | SpecBegin(RACMulticastConnection) 16 | 17 | __block NSUInteger subscriptionCount = 0; 18 | __block RACMulticastConnection *connection; 19 | __block BOOL disposed = NO; 20 | 21 | beforeEach(^{ 22 | subscriptionCount = 0; 23 | disposed = NO; 24 | connection = [[RACSignal createSignal:^(id subscriber) { 25 | subscriptionCount++; 26 | return [RACDisposable disposableWithBlock:^{ 27 | disposed = YES; 28 | }]; 29 | }] publish]; 30 | expect(subscriptionCount).to.equal(0); 31 | }); 32 | 33 | describe(@"-connect", ^{ 34 | it(@"should subscribe to the underlying signal", ^{ 35 | [connection connect]; 36 | expect(subscriptionCount).to.equal(1); 37 | }); 38 | 39 | it(@"should return the same disposable for each invocation", ^{ 40 | RACDisposable *d1 = [connection connect]; 41 | RACDisposable *d2 = [connection connect]; 42 | expect(d1).to.equal(d2); 43 | expect(subscriptionCount).to.equal(1); 44 | }); 45 | 46 | it(@"shouldn't reconnect after disposal", ^{ 47 | RACDisposable *disposable1 = [connection connect]; 48 | expect(subscriptionCount).to.equal(1); 49 | 50 | [disposable1 dispose]; 51 | 52 | RACDisposable *disposable2 = [connection connect]; 53 | expect(subscriptionCount).to.equal(1); 54 | expect(disposable1).to.equal(disposable2); 55 | }); 56 | }); 57 | 58 | describe(@"-autoconnect", ^{ 59 | __block RACSignal *autoconnectedSignal; 60 | 61 | beforeEach(^{ 62 | autoconnectedSignal = [connection autoconnect]; 63 | }); 64 | 65 | it(@"should subscribe to the multicasted signal on the first subscription", ^{ 66 | expect(subscriptionCount).to.equal(0); 67 | 68 | [autoconnectedSignal subscribeNext:^(id x) {}]; 69 | expect(subscriptionCount).to.equal(1); 70 | 71 | [autoconnectedSignal subscribeNext:^(id x) {}]; 72 | expect(subscriptionCount).to.equal(1); 73 | }); 74 | 75 | it(@"should dispose of the multicasted subscription when the signal has no subscribers", ^{ 76 | RACDisposable *disposable = [autoconnectedSignal subscribeNext:^(id x) {}]; 77 | 78 | expect(disposed).to.beFalsy(); 79 | [disposable dispose]; 80 | expect(disposed).to.beTruthy(); 81 | }); 82 | 83 | it(@"shouldn't reconnect after disposal", ^{ 84 | RACDisposable *disposable = [autoconnectedSignal subscribeNext:^(id x) {}]; 85 | expect(subscriptionCount).to.equal(1); 86 | [disposable dispose]; 87 | 88 | disposable = [autoconnectedSignal subscribeNext:^(id x) {}]; 89 | expect(subscriptionCount).to.equal(1); 90 | [disposable dispose]; 91 | }); 92 | 93 | it(@"should replay values after disposal when multicasted to a replay subject", ^{ 94 | RACSubject *subject = [RACSubject subject]; 95 | RACSignal *signal = [[subject multicast:[RACReplaySubject subject]] autoconnect]; 96 | 97 | NSMutableArray *results1 = [NSMutableArray array]; 98 | RACDisposable *disposable = [signal subscribeNext:^(id x) { 99 | [results1 addObject:x]; 100 | }]; 101 | 102 | [subject sendNext:@1]; 103 | [subject sendNext:@2]; 104 | 105 | expect(results1).to.equal((@[ @1, @2 ])); 106 | [disposable dispose]; 107 | 108 | NSMutableArray *results2 = [NSMutableArray array]; 109 | [signal subscribeNext:^(id x) { 110 | [results2 addObject:x]; 111 | }]; 112 | expect(results2).will.equal((@[ @1, @2 ])); 113 | }); 114 | }); 115 | 116 | SpecEnd 117 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACArraySequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACArraySequence.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACArraySequence.h" 10 | 11 | @interface RACArraySequence () 12 | 13 | // Redeclared from the superclass and marked deprecated to prevent using `array` 14 | // where `backingArray` is intended. 15 | @property (nonatomic, copy, readonly) NSArray *array __attribute__((deprecated)); 16 | 17 | // The array being sequenced. 18 | @property (nonatomic, copy, readonly) NSArray *backingArray; 19 | 20 | // The index in the array from which the sequence starts. 21 | @property (nonatomic, assign, readonly) NSUInteger offset; 22 | 23 | @end 24 | 25 | @implementation RACArraySequence 26 | 27 | #pragma mark Lifecycle 28 | 29 | + (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset { 30 | NSCParameterAssert(offset <= array.count); 31 | 32 | if (offset == array.count) return self.empty; 33 | 34 | RACArraySequence *seq = [[self alloc] init]; 35 | seq->_backingArray = [array copy]; 36 | seq->_offset = offset; 37 | return seq; 38 | } 39 | 40 | #pragma mark RACSequence 41 | 42 | - (id)head { 43 | return [self.backingArray objectAtIndex:self.offset]; 44 | } 45 | 46 | - (RACSequence *)tail { 47 | RACSequence *sequence = [self.class sequenceWithArray:self.backingArray offset:self.offset + 1]; 48 | sequence.name = self.name; 49 | return sequence; 50 | } 51 | 52 | #pragma mark NSFastEnumeration 53 | 54 | - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id[])stackbuf count:(NSUInteger)len { 55 | NSCParameterAssert(len > 0); 56 | 57 | if (state->state >= self.backingArray.count) { 58 | // Enumeration has completed. 59 | return 0; 60 | } 61 | 62 | if (state->state == 0) { 63 | state->state = self.offset; 64 | 65 | // Since a sequence doesn't mutate, this just needs to be set to 66 | // something non-NULL. 67 | state->mutationsPtr = state->extra; 68 | } 69 | 70 | state->itemsPtr = stackbuf; 71 | 72 | NSUInteger startIndex = state->state; 73 | NSUInteger index = 0; 74 | 75 | for (id value in self.backingArray) { 76 | // Constructing an index set for -enumerateObjectsAtIndexes: can actually be 77 | // slower than just skipping the items we don't care about. 78 | if (index < startIndex) { 79 | ++index; 80 | continue; 81 | } 82 | 83 | stackbuf[index - startIndex] = value; 84 | 85 | ++index; 86 | if (index - startIndex >= len) break; 87 | } 88 | 89 | NSCAssert(index > startIndex, @"Final index (%lu) should be greater than start index (%lu)", (unsigned long)index, (unsigned long)startIndex); 90 | 91 | state->state = index; 92 | return index - startIndex; 93 | } 94 | 95 | #pragma clang diagnostic push 96 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 97 | - (NSArray *)array { 98 | return [self.backingArray subarrayWithRange:NSMakeRange(self.offset, self.backingArray.count - self.offset)]; 99 | } 100 | #pragma clang diagnostic pop 101 | 102 | #pragma mark NSCoding 103 | 104 | - (id)initWithCoder:(NSCoder *)coder { 105 | self = [super initWithCoder:coder]; 106 | if (self == nil) return nil; 107 | 108 | _backingArray = [coder decodeObjectForKey:@"array"]; 109 | _offset = 0; 110 | 111 | return self; 112 | } 113 | 114 | - (void)encodeWithCoder:(NSCoder *)coder { 115 | // Encoding is handled in RACSequence. 116 | [super encodeWithCoder:coder]; 117 | } 118 | 119 | #pragma mark NSObject 120 | 121 | - (NSString *)description { 122 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@, array = %@ }", self.class, self, self.name, self.backingArray]; 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /ReactiveCocoaFramework/ReactiveCocoa/RACEventTrampoline.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEventTrampoline.m 3 | // ReactiveCocoa 4 | // 5 | // Created by Cody Krieger on 5/18/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACEventTrampoline.h" 10 | #import "RACSwizzling.h" 11 | #import "RACObjCRuntime.h" 12 | #import "RACDelegateProxy.h" 13 | #import 14 | 15 | const char *RACEventTrampolinesKey = "RACEventTrampolinesKey"; 16 | 17 | static NSMutableDictionary *swizzledClasses() { 18 | static dispatch_once_t onceToken; 19 | static NSMutableDictionary *swizzledClasses = nil; 20 | dispatch_once(&onceToken, ^{ 21 | swizzledClasses = [[NSMutableDictionary alloc] init]; 22 | }); 23 | 24 | return swizzledClasses; 25 | } 26 | 27 | @implementation UITextView (RACSignalSupport) 28 | 29 | - (void)rac_setDelegate:(id)delegate { 30 | Class proxyClass = [RACDelegateProxy class]; 31 | 32 | if ([delegate isKindOfClass:proxyClass]) { 33 | id oldDelegate = [self delegate]; 34 | [(RACDelegateProxy *)delegate setActualDelegate:oldDelegate]; 35 | 36 | [self rac_setDelegate:delegate]; 37 | } else if ([self.delegate isKindOfClass:proxyClass]) { 38 | [(RACDelegateProxy *)self.delegate setActualDelegate:delegate]; 39 | } else { 40 | [self rac_setDelegate:delegate]; 41 | } 42 | } 43 | 44 | @end 45 | 46 | 47 | @implementation RACEventTrampoline 48 | 49 | @synthesize proxy; 50 | @synthesize delegateMethod; 51 | 52 | + (instancetype)trampolineForControl:(UIControl *)control controlEvents:(UIControlEvents)controlEvents { 53 | RACEventTrampoline *trampoline = [[self alloc] init]; 54 | [control addTarget:trampoline action:@selector(didGetControlEvent:) forControlEvents:controlEvents]; 55 | return trampoline; 56 | } 57 | 58 | + (instancetype)trampolineForGestureRecognizer:(UIGestureRecognizer *)gesture { 59 | RACEventTrampoline *trampoline = [[self alloc] init]; 60 | [gesture addTarget:trampoline action:@selector(didGetControlEvent:)]; 61 | 62 | return trampoline; 63 | } 64 | 65 | + (instancetype)trampolineForTextView:(UITextView *)textView delegateMethod:(SEL)method { 66 | RACEventTrampoline *trampoline = [[self alloc] init]; 67 | [trampoline setDelegateMethod:method]; 68 | 69 | @synchronized(swizzledClasses()) { 70 | Class class = [textView class]; 71 | NSString *keyName = NSStringFromClass(class); 72 | if ([swizzledClasses() objectForKey:keyName] == nil) { 73 | RACSwizzle(class, @selector(setDelegate:), @selector(rac_setDelegate:)); 74 | [swizzledClasses() setObject:[NSNull null] forKey:keyName]; 75 | } 76 | } 77 | 78 | if ([[textView delegate] isKindOfClass:[RACDelegateProxy class]]) { 79 | [(RACDelegateProxy *)textView.delegate addTrampoline:trampoline]; 80 | } else { 81 | Protocol *protocol = @protocol(UITextViewDelegate); 82 | 83 | RACDelegateProxy *proxy = [RACDelegateProxy proxyWithProtocol:protocol andDelegator:textView]; 84 | [proxy addTrampoline:trampoline]; 85 | 86 | [textView setDelegate:(id)proxy]; 87 | } 88 | 89 | return trampoline; 90 | } 91 | 92 | - (void)dealloc { 93 | [_subject sendCompleted]; 94 | } 95 | 96 | - (id)init { 97 | self = [super init]; 98 | if(self == nil) return nil; 99 | 100 | self.subject = [RACSubject subject]; 101 | 102 | return self; 103 | } 104 | 105 | - (void)didGetControlEvent:(id)sender { 106 | [self.subject sendNext:sender]; 107 | } 108 | 109 | - (void)didGetDelegateEvent:(SEL)receivedEvent sender:(id)sender { 110 | if (receivedEvent == delegateMethod) { 111 | [self didGetControlEvent:sender]; 112 | } 113 | } 114 | 115 | @end 116 | --------------------------------------------------------------------------------