├── ReactiveObjCTests ├── test-data.json ├── RACTestExampleScheduler.h ├── RACTestUIButton.h ├── RACSequenceExamples.h ├── NSObjectRACPropertySubscribingExamples.h ├── RACPropertySignalExamples.h ├── RACControlCommandExamples.h ├── NSEnumeratorRACSequenceAdditionsSpec.m ├── RACTestUIButton.m ├── Info.plist ├── RACSubclassObject.h ├── RACSubscriberExamples.h ├── RACStreamExamples.h ├── RACTestExampleScheduler.m ├── UIActionSheetRACSupportSpec.m ├── UIButtonRACSupportSpec.m ├── NSControllerRACSupportSpec.m ├── RACSubscriptingAssignmentTrampolineSpec.m ├── RACSwiftBridgingSpec.swift ├── UIBarButtonItemRACSupportSpec.m ├── RACSubclassObject.m ├── NSObjectRACAppKitBindingsSpec.m ├── NSURLConnectionRACSupportSpec.m ├── RACChannelExamples.h ├── RACBlockTrampolineSpec.m ├── UIAlertViewRACSupportSpec.m ├── RACTargetQueueSchedulerSpec.m ├── UIImagePickerControllerRACSupportSpec.m ├── NSStringRACKeyPathUtilitiesSpec.m ├── RACDisposableSpec.m └── RACChannelSpec.m ├── Cartfile.private ├── Instruments ├── Signal Events.tracetemplate ├── Disposable Growth.tracetemplate └── README.md ├── Cartfile.resolved ├── Documentation └── README.md ├── ReactiveObjC ├── RACCompoundDisposableProvider.d ├── RACSignalProvider.d ├── RACAnnotations.h ├── RACEagerSequence.h ├── RACEmptySequence.h ├── RACImmediateScheduler.h ├── RACUnarySequence.h ├── RACValueTransformer.h ├── RACErrorSignal.h ├── RACIndexSetSequence.h ├── RACEmptySignal.h ├── NSArray+RACSequenceAdditions.m ├── RACMulticastConnection+Private.h ├── NSString+RACSequenceAdditions.m ├── NSIndexSet+RACSequenceAdditions.m ├── RACDynamicSignal.h ├── RACReturnSignal.h ├── RACUnit.m ├── NSSet+RACSequenceAdditions.m ├── RACSubscriptionScheduler.h ├── RACUnit.h ├── NSOrderedSet+RACSequenceAdditions.m ├── RACSignalSequence.h ├── RACSubscriber+Private.h ├── NSEnumerator+RACSequenceAdditions.m ├── RACGroupedSignal.m ├── NSFileHandle+RACSupport.h ├── RACGroupedSignal.h ├── UISwitch+RACSignalSupport.m ├── NSObject+RACDescription.h ├── UIGestureRecognizer+RACSignalSupport.h ├── NSText+RACSignalSupport.h ├── RACArraySequence.h ├── RACTupleSequence.h ├── RACStringSequence.h ├── UISlider+RACSignalSupport.m ├── UIStepper+RACSignalSupport.m ├── RACScopedDisposable.h ├── UIDatePicker+RACSignalSupport.m ├── NSNotificationCenter+RACSupport.h ├── UIControl+RACSignalSupport.h ├── RACQueueScheduler.h ├── UISegmentedControl+RACSignalSupport.m ├── NSEnumerator+RACSequenceAdditions.h ├── RACBehaviorSubject.h ├── RACScopedDisposable.m ├── NSSet+RACSequenceAdditions.h ├── NSArray+RACSequenceAdditions.h ├── NSOrderedSet+RACSequenceAdditions.h ├── NSIndexSet+RACSequenceAdditions.h ├── RACStream+Private.h ├── NSData+RACSupport.h ├── UIButton+RACCommandSupport.h ├── NSString+RACSequenceAdditions.h ├── UISwitch+RACSignalSupport.h ├── NSControl+RACTextSignalSupport.h ├── RACDynamicSequence.h ├── NSString+RACSupport.h ├── NSControl+RACCommandSupport.h ├── Deprecations+Removals.swift ├── RACReplaySubject.h ├── UIBarButtonItem+RACCommandSupport.h ├── UIRefreshControl+RACCommandSupport.h ├── UISlider+RACSignalSupport.h ├── UIDatePicker+RACSignalSupport.h ├── UIStepper+RACSignalSupport.h ├── RACSubject.h ├── RACTargetQueueScheduler.h ├── NSObject+RACDeallocating.h ├── NSDictionary+RACSequenceAdditions.m ├── RACValueTransformer.m ├── UITableViewCell+RACSignalSupport.h ├── UISegmentedControl+RACSignalSupport.h ├── RACTargetQueueScheduler.m ├── MKAnnotationView+RACSignalSupport.h ├── MKAnnotationView+RACSignalSupport.m ├── UITableViewCell+RACSignalSupport.m ├── NSUserDefaults+RACSupport.h ├── Info.plist ├── RACDelegateProxy.h ├── UICollectionReusableView+RACSignalSupport.h ├── UICollectionReusableView+RACSignalSupport.m ├── UITableViewHeaderFooterView+RACSignalSupport.h ├── NSURLConnection+RACSupport.h ├── NSString+RACKeyPathUtilities.m ├── UITableViewHeaderFooterView+RACSignalSupport.m ├── NSData+RACSupport.m ├── UITextField+RACSignalSupport.h ├── NSNotificationCenter+RACSupport.m ├── RACBlockTrampoline.h ├── RACErrorSignal.m ├── RACPassthroughSubscriber.h ├── RACScheduler+Subclass.h ├── NSString+RACKeyPathUtilities.h ├── NSString+RACSupport.m ├── RACDisposable.h ├── NSText+RACSignalSupport.m ├── NSObject+RACAppKitBindings.h ├── NSObject+RACDescription.m ├── UIGestureRecognizer+RACSignalSupport.m ├── RACScheduler+Private.h ├── RACSubscriptingAssignmentTrampoline.m ├── UITextField+RACSignalSupport.m ├── NSControl+RACTextSignalSupport.m ├── RACKVOTrampoline.h ├── NSFileHandle+RACSupport.m ├── UIControl+RACSignalSupportPrivate.h ├── UIActionSheet+RACSignalSupport.h ├── NSDictionary+RACSequenceAdditions.h ├── UITextView+RACSignalSupport.h ├── RACKVOProxy.h ├── UIImagePickerController+RACSignalSupport.h ├── UIControl+RACSignalSupport.m ├── RACEmptySignal.m ├── RACBehaviorSubject.m ├── RACImmediateScheduler.m ├── RACQueueScheduler+Subclass.h ├── RACTestScheduler.h ├── UIActionSheet+RACSignalSupport.m ├── UIControl+RACSignalSupportPrivate.m ├── RACEmptySequence.m ├── RACSubscriptionScheduler.m ├── UITextView+RACSignalSupport.m ├── NSUserDefaults+RACSupport.m ├── RACDynamicSignal.m ├── RACSerialDisposable.h ├── UIImagePickerController+RACSignalSupport.m ├── UIBarButtonItem+RACCommandSupport.m ├── UIButton+RACCommandSupport.m ├── RACSubscriber.h ├── RACKVOProxy.m ├── RACEvent.h ├── RACCompoundDisposable.h ├── RACStringSequence.m ├── UIAlertView+RACSignalSupport.m ├── NSControl+RACCommandSupport.m ├── NSInvocation+RACTypeParsing.h ├── RACSignalSequence.m ├── NSURLConnection+RACSupport.m ├── RACEagerSequence.m ├── UIAlertView+RACSignalSupport.h ├── NSObject+RACKVOWrapper.h ├── NSObject+RACLifting.h ├── RACMulticastConnection.h ├── UIRefreshControl+RACCommandSupport.m ├── RACTupleSequence.m ├── RACDisposable.m ├── RACDelegateProxy.m ├── RACReturnSignal.m └── RACUnarySequence.m ├── .gitignore ├── .gitmodules ├── ReactiveObjC.xcworkspace └── contents.xcworkspacedata ├── script ├── update-version └── build ├── LICENSE.md └── CONTRIBUTING.md /ReactiveObjCTests/test-data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "item": 1 }, 3 | { "item": 2 }, 4 | { "item": 3 } 5 | ] 6 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "jspahrsummers/xcconfigs" "3d9d996" 2 | github "Quick/Quick" ~> 2.0 3 | github "Quick/Nimble" ~> 8.0.0 4 | -------------------------------------------------------------------------------- /Instruments/Signal Events.tracetemplate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveCocoa/ReactiveObjC/HEAD/Instruments/Signal Events.tracetemplate -------------------------------------------------------------------------------- /Instruments/Disposable Growth.tracetemplate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReactiveCocoa/ReactiveObjC/HEAD/Instruments/Disposable Growth.tracetemplate -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v8.0.1" 2 | github "Quick/Quick" "v2.0.0" 3 | github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0" 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACCompoundDisposableProvider.d: -------------------------------------------------------------------------------- 1 | provider RACCompoundDisposable { 2 | probe added(char *compoundDisposable, char *disposable, long newTotal); 3 | probe removed(char *compoundDisposable, char *disposable, long newTotal); 4 | }; 5 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSignalProvider.d: -------------------------------------------------------------------------------- 1 | provider RACSignal { 2 | probe next(char *signal, char *subscriber, char *valueDescription); 3 | probe completed(char *signal, char *subscriber); 4 | probe error(char *signal, char *subscriber, char *errorDescription); 5 | }; 6 | -------------------------------------------------------------------------------- /ReactiveObjC/RACAnnotations.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACAnnotations.h 3 | // ReactiveObjC 4 | // 5 | // Created by Eric Horacek on 3/31/17. 6 | // Copyright © 2017 GitHub. All rights reserved. 7 | // 8 | 9 | #ifndef RAC_WARN_UNUSED_RESULT 10 | #define RAC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) 11 | #endif 12 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEagerSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEagerSequence.h 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /.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 | PlaygroundUtility.remap 17 | 18 | # SwiftPM 19 | .build 20 | 21 | # Carthage 22 | Carthage/Build 23 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEmptySequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEmptySequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 10 | 11 | // Private class representing an empty sequence. 12 | @interface RACEmptySequence : RACSequence 13 | 14 | + (RACEmptySequence *)empty; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ReactiveObjC/RACImmediateScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACImmediateScheduler.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACScheduler.h" 10 | 11 | // A private scheduler which immediately executes its scheduled blocks. 12 | @interface RACImmediateScheduler : RACScheduler 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage/Checkouts/Nimble"] 2 | path = Carthage/Checkouts/Nimble 3 | url = https://github.com/Quick/Nimble.git 4 | [submodule "Carthage/Checkouts/Quick"] 5 | path = Carthage/Checkouts/Quick 6 | url = https://github.com/Quick/Quick.git 7 | [submodule "Carthage/Checkouts/xcconfigs"] 8 | path = Carthage/Checkouts/xcconfigs 9 | url = https://github.com/jspahrsummers/xcconfigs.git 10 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACTestExampleScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestExampleScheduler.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 6/7/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RACTestExampleScheduler : RACQueueScheduler 12 | 13 | - (instancetype)initWithQueue:(dispatch_queue_t)queue; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACTestUIButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestUIButton.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-06-15. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Enables use of -sendActionsForControlEvents: in unit tests. 12 | @interface RACTestUIButton : UIButton 13 | 14 | + (instancetype)button; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ReactiveObjC/RACUnarySequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnarySequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 10 | 11 | // Private class representing a sequence of exactly one value. 12 | @interface RACUnarySequence : RACSequence 13 | 14 | + (RACUnarySequence *)return:(id)value; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ReactiveObjC.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ReactiveObjC/RACValueTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACValueTransformer.h 3 | // ReactiveObjC 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 | // A private block based transformer. 12 | @interface RACValueTransformer : NSValueTransformer 13 | 14 | + (instancetype)transformerWithBlock:(id (^)(id value))block; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ReactiveObjC/RACErrorSignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACErrorSignal.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignal.h" 10 | 11 | // A private `RACSignal` subclass that synchronously sends an error to any 12 | // subscriber. 13 | @interface RACErrorSignal : RACSignal 14 | 15 | + (RACSignal *)error:(NSError *)error; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveObjC/RACIndexSetSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACIndexSetSequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Sergey Gavrilyuk on 12/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 10 | 11 | // Private class that adapts an array to the RACSequence interface. 12 | @interface RACIndexSetSequence : RACSequence 13 | 14 | + (RACSequence *)sequenceWithIndexSet:(NSIndexSet *)indexSet; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEmptySignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEmptySignal.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignal.h" 10 | 11 | // A private `RACSignal` subclasses that synchronously sends completed to any 12 | // subscribers. 13 | @interface RACEmptySignal<__covariant ValueType> : RACSignal 14 | 15 | + (RACSignal *)empty; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveObjC/NSArray+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+RACSequenceAdditions.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACMulticastConnection+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACMulticastConnection+Private.h 3 | // ReactiveObjC 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 | 11 | @class RACSubject; 12 | 13 | @interface RACMulticastConnection<__covariant ValueType> () 14 | 15 | - (instancetype)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveObjC/NSString+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSequenceAdditions.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/NSIndexSet+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSIndexSet+RACSequenceAdditions.m 3 | // ReactiveObjC 4 | // 5 | // Created by Sergey Gavrilyuk on 12/17/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSIndexSet+RACSequenceAdditions.h" 10 | #import "RACIndexSetSequence.h" 11 | 12 | @implementation NSIndexSet (RACSequenceAdditions) 13 | 14 | - (RACSequence *)rac_sequence { 15 | return [RACIndexSetSequence sequenceWithIndexSet:self]; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACSequenceExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSequenceExamples.h 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDynamicSignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDynamicSignal.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignal.h" 10 | 11 | // A private `RACSignal` subclasses that implements its subscription behavior 12 | // using a block. 13 | @interface RACDynamicSignal : RACSignal 14 | 15 | + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveObjC/RACReturnSignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACReturnSignal.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignal.h" 10 | 11 | // A private `RACSignal` subclasses that synchronously sends a value to any 12 | // subscribers, then completes. 13 | @interface RACReturnSignal<__covariant ValueType> : RACSignal 14 | 15 | + (RACSignal *)return:(ValueType)value; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveObjC/RACUnit.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnit.m 3 | // ReactiveObjC 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 | @implementation RACUnit 12 | 13 | #pragma mark API 14 | 15 | + (RACUnit *)defaultUnit { 16 | static dispatch_once_t onceToken; 17 | static RACUnit *defaultUnit = nil; 18 | dispatch_once(&onceToken, ^{ 19 | defaultUnit = [[self alloc] init]; 20 | }); 21 | 22 | return defaultUnit; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ReactiveObjC/NSSet+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+RACSequenceAdditions.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSubscriptionScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptionScheduler.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACScheduler.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | // A private scheduler used only for subscriptions. See the private 14 | // +[RACScheduler subscriptionScheduler] method for more information. 15 | @interface RACSubscriptionScheduler : RACScheduler 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /ReactiveObjC/RACUnit.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnit.h 3 | // ReactiveObjC 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 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A unit represents an empty value. 14 | /// 15 | /// It should never be necessary to create a unit yourself. Just use +defaultUnit. 16 | @interface RACUnit : NSObject 17 | 18 | /// A singleton instance. 19 | + (RACUnit *)defaultUnit; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /ReactiveObjC/NSOrderedSet+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSOrderedSet+RACSequenceAdditions.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSignalSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSignalSequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2012-11-09. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 10 | 11 | @class RACSignal<__covariant ValueType>; 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSubscriber+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriber+Private.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-06-13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubscriber.h" 10 | 11 | // A simple block-based subscriber. 12 | @interface RACSubscriber : NSObject 13 | 14 | // Creates a new subscriber with the given blocks. 15 | + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveObjC/NSEnumerator+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSEnumerator+RACSequenceAdditions.m 3 | // ReactiveObjC 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 | 12 | @implementation NSEnumerator (RACSequenceAdditions) 13 | 14 | - (RACSequence *)rac_sequence { 15 | return [RACSequence sequenceWithHeadBlock:^{ 16 | return [self nextObject]; 17 | } tailBlock:^{ 18 | return self.rac_sequence; 19 | }]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ReactiveObjC/RACGroupedSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACGroupedSignal.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/NSFileHandle+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileHandle+RACSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSFileHandle (RACSupport) 16 | 17 | // Read any available data in the background and send it. Completes when data 18 | // length is <= 0. 19 | - (RACSignal *)rac_readInBackground; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /ReactiveObjC/RACGroupedSignal.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACGroupedSignal.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 5/2/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubject.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A grouped signal is used by -[RACSignal groupBy:transform:]. 14 | @interface RACGroupedSignal : RACSubject 15 | 16 | /// The key shared by the group. 17 | @property (nonatomic, readonly, copy) id key; 18 | 19 | + (instancetype)signalWithKey:(id)key; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /ReactiveObjC/UISwitch+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UISwitch+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UISwitch+RACSignalSupport.h" 10 | #import 11 | #import "UIControl+RACSignalSupportPrivate.h" 12 | 13 | @implementation UISwitch (RACSignalSupport) 14 | 15 | - (RACChannelTerminal *)rac_newOnChannel { 16 | return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.on) nilValue:@NO]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveObjC/NSObject+RACDescription.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDescription.h 3 | // ReactiveObjC 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 | // A simplified description of the object, which does not invoke -description 12 | // (and thus should be much faster in many cases). 13 | // 14 | // This is for debugging purposes only, and will return a constant string 15 | // unless the RAC_DEBUG_SIGNAL_NAMES environment variable is set. 16 | NSString *RACDescription(id object); 17 | -------------------------------------------------------------------------------- /ReactiveObjC/UIGestureRecognizer+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+RACSignalSupport.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIGestureRecognizer (RACSignalSupport) 16 | 17 | /// Returns a signal that sends the receiver when its gesture occurs. 18 | - (RACSignal<__kindof UIGestureRecognizer *> *)rac_gestureSignal; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Instruments/README.md: -------------------------------------------------------------------------------- 1 | This folder contains Instruments templates to make it easier to debug 2 | code using ReactiveCocoa. 3 | 4 | To get started with a template, simply double-click it. 5 | 6 | ### Signal Names 7 | 8 | The `name` property of `RACSignal` requires that the `RAC_DEBUG_SIGNAL_NAMES` 9 | environment variable be set, which means that you won't have access to 10 | meaningful names in Instruments by default. 11 | 12 | To add signal names, open your application's scheme in Xcode, select the Profile 13 | action, and add `RAC_DEBUG_SIGNAL_NAMES` with a value of `1` to the list of 14 | environment variables. 15 | -------------------------------------------------------------------------------- /ReactiveObjC/NSText+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSText+RACSignalSupport.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSText (RACSignalSupport) 16 | 17 | /// Returns a signal which sends the current `string` of the receiver, then the 18 | /// new value any time it changes. 19 | - (RACSignal *)rac_textSignal; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /ReactiveObjC/RACArraySequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACArraySequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 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 | + (RACSequence *)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveObjC/RACTupleSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTupleSequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-05-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 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 a 15 | // RACTuple), starting from the given offset. 16 | + (RACSequence *)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ReactiveObjC/RACStringSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACStringSequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 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 | -------------------------------------------------------------------------------- /ReactiveObjC/UISlider+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UISlider+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UISlider+RACSignalSupport.h" 10 | #import 11 | #import "UIControl+RACSignalSupportPrivate.h" 12 | 13 | @implementation UISlider (RACSignalSupport) 14 | 15 | - (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue { 16 | return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.value) nilValue:nilValue]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveObjC/UIStepper+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIStepper+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIStepper+RACSignalSupport.h" 10 | #import 11 | #import "UIControl+RACSignalSupportPrivate.h" 12 | 13 | @implementation UIStepper (RACSignalSupport) 14 | 15 | - (RACChannelTerminal *)rac_newValueChannelWithNilValue:(NSNumber *)nilValue { 16 | return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.value) nilValue:nilValue]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveObjC/RACScopedDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACScopedDisposable.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 3/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACDisposable.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A disposable that calls its own -dispose when it is dealloc'd. 14 | @interface RACScopedDisposable : RACDisposable 15 | 16 | /// Creates a new scoped disposable that will also dispose of the given 17 | /// disposable when it is dealloc'd. 18 | + (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /ReactiveObjC/UIDatePicker+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIDatePicker+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIDatePicker+RACSignalSupport.h" 10 | #import 11 | #import "UIControl+RACSignalSupportPrivate.h" 12 | 13 | @implementation UIDatePicker (RACSignalSupport) 14 | 15 | - (RACChannelTerminal *)rac_newDateChannelWithNilValue:(NSDate *)nilValue { 16 | return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.date) nilValue:nilValue]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveObjC/NSNotificationCenter+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+RACSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 5/10/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSNotificationCenter (RACSupport) 16 | 17 | // Sends the NSNotification every time the notification is posted. 18 | - (RACSignal *)rac_addObserverForName:(nullable NSString *)notificationName object:(nullable id)object; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /ReactiveObjC/UIControl+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+RACSignalSupport.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIControl (RACSignalSupport) 16 | 17 | /// Creates and returns a signal that sends the sender of the control event 18 | /// whenever one of the control events is triggered. 19 | - (RACSignal<__kindof UIControl *> *)rac_signalForControlEvents:(UIControlEvents)controlEvents; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /ReactiveObjC/RACQueueScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACQueueScheduler.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACScheduler.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// An abstract scheduler which asynchronously enqueues all its work to a Grand 14 | /// Central Dispatch queue. 15 | /// 16 | /// Because RACQueueScheduler is abstract, it should not be instantiated 17 | /// directly. Create a subclass using the `RACQueueScheduler+Subclass.h` 18 | /// interface and use that instead. 19 | @interface RACQueueScheduler : RACScheduler 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /ReactiveObjC/UISegmentedControl+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UISegmentedControl+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UISegmentedControl+RACSignalSupport.h" 10 | #import 11 | #import "UIControl+RACSignalSupportPrivate.h" 12 | 13 | @implementation UISegmentedControl (RACSignalSupport) 14 | 15 | - (RACChannelTerminal *)rac_newSelectedSegmentIndexChannelWithNilValue:(NSNumber *)nilValue { 16 | return [self rac_channelForControlEvents:UIControlEventValueChanged key:@keypath(self.selectedSegmentIndex) nilValue:nilValue]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ReactiveObjCTests/NSObjectRACPropertySubscribingExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectRACPropertySubscribingExamples.h 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/NSEnumerator+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSEnumerator+RACSequenceAdditions.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSEnumerator (RACSequenceAdditions) 16 | 17 | /// Creates and returns a sequence corresponding to the receiver. 18 | /// 19 | /// The receiver is exhausted lazily as the sequence is enumerated. 20 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACPropertySignalExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACPropertySignalExamples.h 3 | // ReactiveObjC 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 | // 14 | // void (^)(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) 15 | // 16 | // and should tie the value of the key path on testObject to signal. `nilValue` 17 | // will be used when the signal sends a `nil` value. 18 | extern NSString * const RACPropertySignalExamplesSetupBlock; 19 | -------------------------------------------------------------------------------- /ReactiveObjC/RACBehaviorSubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBehaviorSubject.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 3/16/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubject.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A behavior subject sends the last value it received when it is subscribed to. 14 | @interface RACBehaviorSubject : RACSubject 15 | 16 | /// Creates a new behavior subject with a default value. If it hasn't received 17 | /// any values when it gets subscribed to, it sends the default value. 18 | + (instancetype)behaviorSubjectWithDefaultValue:(nullable ValueType)value; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /ReactiveObjC/RACScopedDisposable.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACScopedDisposable.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/NSSet+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+RACSequenceAdditions.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSSet<__covariant ObjectType> (RACSequenceAdditions) 16 | 17 | /// Creates and returns a sequence corresponding to the receiver. 18 | /// 19 | /// Mutating the receiver will not affect the sequence after it's been created. 20 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACControlCommandExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACControlCommandExamples.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-08-15. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | // The name of the shared examples for any control class that has 10 | // `rac_command` and `isEnabled` properties. 11 | extern NSString * const RACControlCommandExamples; 12 | 13 | // The control to test. 14 | extern NSString * const RACControlCommandExampleControl; 15 | 16 | // A block of type `void (^)(id control)` which should activate the 17 | // `rac_command` of the `control` by manipulating the control itself. 18 | extern NSString * const RACControlCommandExampleActivateBlock; 19 | -------------------------------------------------------------------------------- /ReactiveObjC/NSArray+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+RACSequenceAdditions.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSArray<__covariant ObjectType> (RACSequenceAdditions) 16 | 17 | /// Creates and returns a sequence corresponding to the receiver. 18 | /// 19 | /// Mutating the receiver will not affect the sequence after it's been created. 20 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /ReactiveObjC/NSOrderedSet+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSOrderedSet+RACSequenceAdditions.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSOrderedSet<__covariant ObjectType> (RACSequenceAdditions) 16 | 17 | /// Creates and returns a sequence corresponding to the receiver. 18 | /// 19 | /// Mutating the receiver will not affect the sequence after it's been created. 20 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /ReactiveObjC/NSIndexSet+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSIndexSet+RACSequenceAdditions.h 3 | // ReactiveObjC 4 | // 5 | // Created by Sergey Gavrilyuk on 12/17/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSequence<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSIndexSet (RACSequenceAdditions) 16 | 17 | /// Creates and returns a sequence of indexes (as `NSNumber`s) corresponding to 18 | /// the receiver. 19 | /// 20 | /// Mutating the receiver will not affect the sequence after it's been created. 21 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /ReactiveObjC/RACStream+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACStream+Private.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-07-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACStream.h" 10 | 11 | @interface RACStream () 12 | 13 | // Combines a list of streams using the logic of the given block. 14 | // 15 | // streams - The streams to combine. 16 | // block - An operator that combines two streams and returns a new one. The 17 | // returned stream should contain 2-tuples of the streams' combined 18 | // values. 19 | // 20 | // Returns a combined stream. 21 | + (__kindof RACStream *)join:(id)streams block:(RACStream * (^)(id, id))block; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ReactiveObjC/NSData+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+RACSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 5/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACScheduler; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSData (RACSupport) 17 | 18 | // Read the data at the URL using -[NSData initWithContentsOfURL:options:error:]. 19 | // Sends the data or the error. 20 | // 21 | // scheduler - cannot be nil. 22 | + (RACSignal *)rac_readContentsOfURL:(nullable NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjC/UIButton+RACCommandSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+RACCommandSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Ash Furrow on 2013-06-06. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACCommand<__contravariant InputType, __covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIButton (RACCommandSupport) 16 | 17 | /// Sets the button's command. When the button is clicked, the command is 18 | /// executed with the sender of the event. The button's enabledness is bound 19 | /// to the command's `canExecute`. 20 | @property (nonatomic, strong, nullable) RACCommand<__kindof UIButton *, id> *rac_command; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /ReactiveObjC/NSString+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSequenceAdditions.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSString (RACSequenceAdditions) 16 | 17 | /// Creates and returns a sequence containing strings corresponding to each 18 | /// composed character sequence in the receiver. 19 | /// 20 | /// Mutating the receiver will not affect the sequence after it's been created. 21 | @property (nonatomic, copy, readonly) RACSequence *rac_sequence; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /ReactiveObjCTests/NSEnumeratorRACSequenceAdditionsSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSEnumeratorRACSequenceAdditionsSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 07/01/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACSequenceExamples.h" 13 | 14 | #import "NSEnumerator+RACSequenceAdditions.h" 15 | 16 | QuickSpecBegin(NSEnumeratorRACSequenceAdditionsSpec) 17 | 18 | qck_describe(@"-rac_sequence", ^{ 19 | NSArray *values = @[ @0, @1, @2, @3, @4 ]; 20 | qck_itBehavesLike(RACSequenceExamples, ^{ 21 | return @{ 22 | RACSequenceExampleSequence: values.objectEnumerator.rac_sequence, 23 | RACSequenceExampleExpectedValues: values 24 | }; 25 | }); 26 | }); 27 | 28 | QuickSpecEnd 29 | -------------------------------------------------------------------------------- /ReactiveObjC/UISwitch+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UISwitch+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UISwitch (RACSignalSupport) 16 | 17 | /// Creates a new RACChannel-based binding to the receiver. 18 | /// 19 | /// Returns a RACChannelTerminal that sends whether the receiver is on whenever 20 | /// the UIControlEventValueChanged control event is fired, and sets it on or off 21 | /// when it receives @YES or @NO respectively. 22 | - (RACChannelTerminal *)rac_newOnChannel; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjC/NSControl+RACTextSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACTextSignalSupport.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSControl (RACTextSignalSupport) 16 | 17 | /// Observes a text-based control for changes. 18 | /// 19 | /// Using this method on a control without editable text is considered undefined 20 | /// behavior. 21 | /// 22 | /// Returns a signal which sends the current string value of the receiver, then 23 | /// the new value any time it changes. 24 | - (RACSignal *)rac_textSignal; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDynamicSequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDynamicSequence.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2012-10-29. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RACSequence.h" 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 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACTestUIButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestUIButton.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-06-15. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTestUIButton.h" 10 | 11 | @implementation RACTestUIButton 12 | 13 | + (instancetype)button { 14 | RACTestUIButton *button = [self buttonWithType:UIButtonTypeCustom]; 15 | return button; 16 | } 17 | 18 | // Required for unit testing – controls don't work normally 19 | // outside of normal apps. 20 | -(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { 21 | #pragma clang diagnostic push 22 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 23 | [target performSelector:action withObject:self]; 24 | #pragma clang diagnostic pop 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ReactiveObjC/NSString+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 5/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACScheduler; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSString (RACSupport) 17 | 18 | // Reads in the contents of the file using +[NSString stringWithContentsOfURL:usedEncoding:error:]. 19 | // Note that encoding won't be valid until the signal completes successfully. 20 | // 21 | // scheduler - cannot be nil. 22 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjC/NSControl+RACCommandSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACCommandSupport.h 3 | // ReactiveObjC 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<__contravariant InputType, __covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSControl (RACCommandSupport) 16 | 17 | /// Sets the control's command. When the control is clicked, the command is 18 | /// executed with the sender of the event. The control's enabledness is bound 19 | /// to the command's `canExecute`. 20 | /// 21 | /// Note: this will reset the control's target and action. 22 | @property (nonatomic, strong, nullable) RACCommand<__kindof NSControl *, id> *rac_command; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjC/Deprecations+Removals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Deprecations+Removals.swift 3 | // ReactiveObjC 4 | // 5 | // Created by Adam Sharp on 26/12/18. 6 | // Copyright © 2018 GitHub. All rights reserved. 7 | // 8 | 9 | @available(*, deprecated, renamed: "RACSignalError.notEnabled") 10 | public let RACCommandErrorNotEnabled = RACCommandError.notEnabled.rawValue 11 | 12 | @available(*, deprecated, renamed: "RACSignalError.methodSwizzlingRace") 13 | public let RACSelectorSignalErrorMethodSwizzlingRace = RACSelectorSignalError.methodSwizzlingRace 14 | 15 | @available(*, deprecated, renamed: "RACSignalError.noMatchingCase") 16 | public let RACSignalErrorNoMatchingCase = RACSignalError.noMatchingCase.rawValue 17 | 18 | @available(*, deprecated, renamed: "RACSignalError.timedOut") 19 | public let RACSignalErrorTimedOut = RACSignalError.timedOut.rawValue 20 | -------------------------------------------------------------------------------- /ReactiveObjC/RACReplaySubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACReplaySubject.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 3/14/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSubject.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | extern const NSUInteger RACReplaySubjectUnlimitedCapacity; 14 | 15 | /// A replay subject saves the values it is sent (up to its defined capacity) 16 | /// and resends those to new subscribers. It will also replay an error or 17 | /// completion. 18 | @interface RACReplaySubject : RACSubject 19 | 20 | /// Creates a new replay subject with the given capacity. A capacity of 21 | /// RACReplaySubjectUnlimitedCapacity means values are never trimmed. 22 | + (instancetype)replaySubjectWithCapacity:(NSUInteger)capacity; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjC/UIBarButtonItem+RACCommandSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+RACCommandSupport.h 3 | // ReactiveObjC 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<__contravariant InputType, __covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIBarButtonItem (RACCommandSupport) 16 | 17 | /// Sets the control's command. When the control is clicked, the command is 18 | /// executed with the sender of the event. The control's enabledness is bound 19 | /// to the command's `canExecute`. 20 | /// 21 | /// Note: this will reset the control's target and action. 22 | @property (nonatomic, strong, nullable) RACCommand<__kindof UIBarButtonItem *, id> *rac_command; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjCTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 3.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /script/update-version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -z "$1" ]]; then 4 | echo "Please specify a version tag." 5 | exit 6 | fi 7 | 8 | PRERELEASE_STRIPPED=$(echo "$1" | perl -0777 -ne '/([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?/ and print "$1.$2.$3"') 9 | 10 | if [[ -z "$PRERELEASE_STRIPPED" ]]; then 11 | echo "The version tag is not semver compliant." 12 | exit 13 | fi 14 | 15 | CURRENT_TAG=$(perl -0777 -ne '/s.version([\s]+)=([\s]+)"(.+)"/ and print $3' *.podspec) 16 | echo "Current tag: $CURRENT_TAG" 17 | 18 | perl -0777 -i -pe 's/s.version([\s]+)=([\s]+)"'${CURRENT_TAG}'"/s.version$1=$2"'${1}'"/' *.podspec 19 | perl -0777 -i -pe 's/g>'${CURRENT_TAG}'<\/str/g>'${PRERELEASE_STRIPPED}'<\/str/' */Info.plist 20 | perl -0777 -i -pe 's/g>'${CURRENT_TAG}'<\/str/g>'${PRERELEASE_STRIPPED}'<\/str/' */*/Info.plist 21 | sed -i '' '3i\ 22 | \ 23 | # '${1} CHANGELOG.md 24 | 25 | -------------------------------------------------------------------------------- /ReactiveObjC/UIRefreshControl+RACCommandSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIRefreshControl+RACCommandSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Dave Lee on 2013-10-17. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACCommand<__contravariant InputType, __covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIRefreshControl (RACCommandSupport) 16 | 17 | /// Manipulate the RACCommand property associated with this refresh control. 18 | /// 19 | /// When this refresh control is activated by the user, the command will be 20 | /// executed. Upon completion or error of the execution signal, -endRefreshing 21 | /// will be invoked. 22 | @property (nonatomic, strong, nullable) RACCommand<__kindof UIRefreshControl *, id> *rac_command; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACSubclassObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubclassObject.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 3/18/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACTestObject.h" 13 | 14 | @interface RACSubclassObject : RACTestObject 15 | 16 | // Set whenever -forwardInvocation: is invoked on the receiver. 17 | @property (nonatomic, assign) SEL forwardedSelector; 18 | 19 | // Invokes the superclass implementation with `objectValue` concatenated to 20 | // "SUBCLASS". 21 | - (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue; 22 | 23 | // Asynchronously invokes the superclass implementation on the current scheduler. 24 | - (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ReactiveObjC/UISlider+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UISlider+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UISlider (RACSignalSupport) 16 | 17 | /// Creates a new RACChannel-based binding to the receiver. 18 | /// 19 | /// nilValue - The value to set when the terminal receives `nil`. 20 | /// 21 | /// Returns a RACChannelTerminal that sends the receiver's value whenever the 22 | /// UIControlEventValueChanged control event is fired, and sets the value to the 23 | /// values it receives. 24 | - (RACChannelTerminal *)rac_newValueChannelWithNilValue:(nullable NSNumber *)nilValue; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /ReactiveObjC/UIDatePicker+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIDatePicker+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIDatePicker (RACSignalSupport) 16 | 17 | /// Creates a new RACChannel-based binding to the receiver. 18 | /// 19 | /// nilValue - The date to set when the terminal receives `nil`. 20 | /// 21 | /// Returns a RACChannelTerminal that sends the receiver's date whenever the 22 | /// UIControlEventValueChanged control event is fired, and sets the date to the 23 | /// values it receives. 24 | - (RACChannelTerminal *)rac_newDateChannelWithNilValue:(nullable NSDate *)nilValue; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /ReactiveObjC/UIStepper+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIStepper+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIStepper (RACSignalSupport) 16 | 17 | /// Creates a new RACChannel-based binding to the receiver. 18 | /// 19 | /// nilValue - The value to set when the terminal receives `nil`. 20 | /// 21 | /// Returns a RACChannelTerminal that sends the receiver's value whenever the 22 | /// UIControlEventValueChanged control event is fired, and sets the value to the 23 | /// values it receives. 24 | - (RACChannelTerminal *)rac_newValueChannelWithNilValue:(nullable NSNumber *)nilValue; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSubject.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubject.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 3/9/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACSignal.h" 10 | #import "RACSubscriber.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | /// A subject can be thought of as a signal that you can manually control by 15 | /// sending next, completed, and error. 16 | /// 17 | /// They're most helpful in bridging the non-RAC world to RAC, since they let you 18 | /// manually control the sending of events. 19 | @interface RACSubject : RACSignal 20 | 21 | /// Returns a new subject. 22 | + (instancetype)subject; 23 | 24 | // Redeclaration of the RACSubscriber method. Made in order to specify a generic type. 25 | - (void)sendNext:(nullable ValueType)value; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACSubscriberExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriberExamples.h 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACStreamExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACStreamExamples.h 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACTargetQueueScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTargetQueueScheduler.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 6/6/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACQueueScheduler.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A scheduler that enqueues blocks on a private serial queue, targeting an 14 | /// arbitrary GCD queue. 15 | @interface RACTargetQueueScheduler : RACQueueScheduler 16 | 17 | /// Initializes the receiver with a serial queue that will target the given 18 | /// `targetQueue`. 19 | /// 20 | /// name - The name of the scheduler. If nil, a default name will be used. 21 | /// targetQueue - The queue to target. Cannot be NULL. 22 | /// 23 | /// Returns the initialized object. 24 | - (instancetype)initWithName:(nullable NSString *)name targetQueue:(dispatch_queue_t)targetQueue; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /ReactiveObjC/NSObject+RACDeallocating.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDeallocating.h 3 | // ReactiveObjC 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 RACCompoundDisposable; 12 | @class RACDisposable; 13 | @class RACSignal<__covariant ValueType>; 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface NSObject (RACDeallocating) 18 | 19 | /// The compound disposable which will be disposed of when the receiver is 20 | /// deallocated. 21 | @property (atomic, readonly, strong) RACCompoundDisposable *rac_deallocDisposable; 22 | 23 | /// Returns a signal that will complete immediately before the receiver is fully 24 | /// deallocated. If already deallocated when the signal is subscribed to, 25 | /// a `completed` event will be sent immediately. 26 | - (RACSignal *)rac_willDeallocSignal; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /ReactiveObjC/NSDictionary+RACSequenceAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+RACSequenceAdditions.m 3 | // ReactiveObjC 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 RACTuplePack(key, value); 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACValueTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACValueTransformer.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/UITableViewCell+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-07-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal<__covariant ValueType>; 12 | @class RACUnit; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface UITableViewCell (RACSignalSupport) 17 | 18 | /// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon 19 | /// the receiver. 20 | /// 21 | /// Examples 22 | /// 23 | /// [[[self.cancelButton 24 | /// rac_signalForControlEvents:UIControlEventTouchUpInside] 25 | /// takeUntil:self.rac_prepareForReuseSignal] 26 | /// subscribeNext:^(UIButton *x) { 27 | /// // do other things 28 | /// }]; 29 | @property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /ReactiveObjC/UISegmentedControl+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UISegmentedControl+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 20/07/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UISegmentedControl (RACSignalSupport) 16 | 17 | /// Creates a new RACChannel-based binding to the receiver. 18 | /// 19 | /// nilValue - The segment to select when the terminal receives `nil`. 20 | /// 21 | /// Returns a RACChannelTerminal that sends the receiver's currently selected 22 | /// segment's index whenever the UIControlEventValueChanged control event is 23 | /// fired, and sets the selected segment index to the values it receives. 24 | - (RACChannelTerminal *)rac_newSelectedSegmentIndexChannelWithNilValue:(nullable NSNumber *)nilValue; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /ReactiveObjC/RACTargetQueueScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTargetQueueScheduler.m 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 6/6/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTargetQueueScheduler.h" 10 | #import "RACQueueScheduler+Subclass.h" 11 | 12 | @implementation RACTargetQueueScheduler 13 | 14 | #pragma mark Lifecycle 15 | 16 | - (instancetype)initWithName:(NSString *)name targetQueue:(dispatch_queue_t)targetQueue { 17 | NSCParameterAssert(targetQueue != NULL); 18 | 19 | if (name == nil) { 20 | name = [NSString stringWithFormat:@"org.reactivecocoa.ReactiveObjC.RACTargetQueueScheduler(%s)", dispatch_queue_get_label(targetQueue)]; 21 | } 22 | 23 | dispatch_queue_t queue = dispatch_queue_create(name.UTF8String, DISPATCH_QUEUE_SERIAL); 24 | if (queue == NULL) return nil; 25 | 26 | dispatch_set_target_queue(queue, targetQueue); 27 | 28 | return [super initWithName:name queue:queue]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjC/MKAnnotationView+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // MKAnnotationView+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Zak Remer on 3/31/15. 6 | // Copyright (c) 2015 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class RACSignal<__covariant ValueType>; 13 | @class RACUnit; 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface MKAnnotationView (RACSignalSupport) 18 | 19 | /// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon 20 | /// the receiver. 21 | /// 22 | /// Examples 23 | /// 24 | /// [[[self.cancelButton 25 | /// rac_signalForControlEvents:UIControlEventTouchUpInside] 26 | /// takeUntil:self.rac_prepareForReuseSignal] 27 | /// subscribeNext:^(UIButton *x) { 28 | /// // do other things 29 | /// }]; 30 | @property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /ReactiveObjC/MKAnnotationView+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // MKAnnotationView+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Zak Remer on 3/31/15. 6 | // Copyright (c) 2015 GitHub. All rights reserved. 7 | // 8 | 9 | #import "MKAnnotationView+RACSignalSupport.h" 10 | #import "NSObject+RACDescription.h" 11 | #import "NSObject+RACSelectorSignal.h" 12 | #import "RACSignal+Operations.h" 13 | #import "RACUnit.h" 14 | #import 15 | 16 | @implementation MKAnnotationView (RACSignalSupport) 17 | 18 | - (RACSignal *)rac_prepareForReuseSignal { 19 | RACSignal *signal = objc_getAssociatedObject(self, _cmd); 20 | if (signal != nil) return signal; 21 | 22 | signal = [[[self 23 | rac_signalForSelector:@selector(prepareForReuse)] 24 | mapReplace:RACUnit.defaultUnit] 25 | setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; 26 | 27 | objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | return signal; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjC/UITableViewCell+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-07-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UITableViewCell+RACSignalSupport.h" 10 | #import "NSObject+RACDescription.h" 11 | #import "NSObject+RACSelectorSignal.h" 12 | #import "RACSignal+Operations.h" 13 | #import "RACUnit.h" 14 | #import 15 | 16 | @implementation UITableViewCell (RACSignalSupport) 17 | 18 | - (RACSignal *)rac_prepareForReuseSignal { 19 | RACSignal *signal = objc_getAssociatedObject(self, _cmd); 20 | if (signal != nil) return signal; 21 | 22 | signal = [[[self 23 | rac_signalForSelector:@selector(prepareForReuse)] 24 | mapReplace:RACUnit.defaultUnit] 25 | setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; 26 | 27 | objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | return signal; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjC/NSUserDefaults+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults+RACSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Matt Diephouse on 12/19/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSUserDefaults (RACSupport) 16 | 17 | /// Creates and returns a terminal for binding the user defaults key. 18 | /// 19 | /// **Note:** The value in the user defaults is *asynchronously* updated with 20 | /// values sent to the channel. 21 | /// 22 | /// key - The user defaults key to create the channel terminal for. 23 | /// 24 | /// Returns a channel terminal that sends the value of the user defaults key 25 | /// upon subscription, sends an updated value whenever the default changes, and 26 | /// updates the default asynchronously with values it receives. 27 | - (RACChannelTerminal *)rac_channelTerminalForKey:(NSString *)key; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /ReactiveObjC/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2014 GitHub. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDelegateProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDelegateProxy.h 3 | // ReactiveObjC 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 RACSignal<__covariant ValueType>; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | // A private delegate object suitable for using 16 | // -rac_signalForSelector:fromProtocol: upon. 17 | @interface RACDelegateProxy : NSObject 18 | 19 | // The delegate to which messages should be forwarded if not handled by 20 | // any -signalForSelector: applications. 21 | @property (nonatomic, unsafe_unretained) id rac_proxiedDelegate; 22 | 23 | // Creates a delegate proxy capable of responding to selectors from `protocol`. 24 | - (instancetype)initWithProtocol:(Protocol *)protocol; 25 | 26 | // Calls -rac_signalForSelector:fromProtocol: using the `protocol` specified 27 | // during initialization. 28 | - (RACSignal *)signalForSelector:(SEL)selector; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /ReactiveObjC/UICollectionReusableView+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionReusableView+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Kent Wong on 2013-10-04. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal<__covariant ValueType>; 12 | @class RACUnit; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | // This category is only applicable to iOS >= 6.0. 17 | @interface UICollectionReusableView (RACSignalSupport) 18 | 19 | /// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon 20 | /// the receiver. 21 | /// 22 | /// Examples 23 | /// 24 | /// [[[self.cancelButton 25 | /// rac_signalForControlEvents:UIControlEventTouchUpInside] 26 | /// takeUntil:self.rac_prepareForReuseSignal] 27 | /// subscribeNext:^(UIButton *x) { 28 | /// // do other things 29 | /// }]; 30 | @property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /ReactiveObjC/UICollectionReusableView+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionReusableView+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Kent Wong on 2013-10-04. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UICollectionReusableView+RACSignalSupport.h" 10 | #import "NSObject+RACDescription.h" 11 | #import "NSObject+RACSelectorSignal.h" 12 | #import "RACSignal+Operations.h" 13 | #import "RACUnit.h" 14 | #import 15 | 16 | @implementation UICollectionReusableView (RACSignalSupport) 17 | 18 | - (RACSignal *)rac_prepareForReuseSignal { 19 | RACSignal *signal = objc_getAssociatedObject(self, _cmd); 20 | if (signal != nil) return signal; 21 | 22 | signal = [[[self 23 | rac_signalForSelector:@selector(prepareForReuse)] 24 | mapReplace:RACUnit.defaultUnit] 25 | setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; 26 | 27 | objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | return signal; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewHeaderFooterView+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Syo Ikeda on 12/30/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal<__covariant ValueType>; 12 | @class RACUnit; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | // This category is only applicable to iOS >= 6.0. 17 | @interface UITableViewHeaderFooterView (RACSignalSupport) 18 | 19 | /// A signal which will send a RACUnit whenever -prepareForReuse is invoked upon 20 | /// the receiver. 21 | /// 22 | /// Examples 23 | /// 24 | /// [[[self.cancelButton 25 | /// rac_signalForControlEvents:UIControlEventTouchUpInside] 26 | /// takeUntil:self.rac_prepareForReuseSignal] 27 | /// subscribeNext:^(UIButton *x) { 28 | /// // do other things 29 | /// }]; 30 | @property (nonatomic, strong, readonly) RACSignal *rac_prepareForReuseSignal; 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /ReactiveObjC/NSURLConnection+RACSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLConnection+RACSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACTwoTuple<__covariant First, __covariant Second>; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSURLConnection (RACSupport) 17 | 18 | /// Lazily loads data for the given request in the background. 19 | /// 20 | /// request - The URL request to load. This must not be nil. 21 | /// 22 | /// Returns a signal which will begin loading the request upon each subscription, 23 | /// then send a tuple of the received response and downloaded data, and complete 24 | /// on a background thread. If any errors occur, the returned signal will error 25 | /// out. 26 | + (RACSignal *> *)rac_sendAsynchronousRequest:(NSURLRequest *)request; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /ReactiveObjC/NSString+RACKeyPathUtilities.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACKeyPathUtilities.m 3 | // ReactiveObjC 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 | - (NSString *)rac_keyPathByDeletingFirstKeyPathComponent { 29 | NSUInteger firstDotIndex = [self rangeOfString:@"."].location; 30 | if (firstDotIndex == NSNotFound) { 31 | return nil; 32 | } 33 | return [self substringFromIndex:firstDotIndex + 1]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /ReactiveObjC/UITableViewHeaderFooterView+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewHeaderFooterView+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Syo Ikeda on 12/30/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UITableViewHeaderFooterView+RACSignalSupport.h" 10 | #import "NSObject+RACDescription.h" 11 | #import "NSObject+RACSelectorSignal.h" 12 | #import "RACSignal+Operations.h" 13 | #import "RACUnit.h" 14 | #import 15 | 16 | @implementation UITableViewHeaderFooterView (RACSignalSupport) 17 | 18 | - (RACSignal *)rac_prepareForReuseSignal { 19 | RACSignal *signal = objc_getAssociatedObject(self, _cmd); 20 | if (signal != nil) return signal; 21 | 22 | signal = [[[self 23 | rac_signalForSelector:@selector(prepareForReuse)] 24 | mapReplace:RACUnit.defaultUnit] 25 | setNameWithFormat:@"%@ -rac_prepareForReuseSignal", RACDescription(self)]; 26 | 27 | objc_setAssociatedObject(self, _cmd, signal, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | return signal; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACTestExampleScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestExampleScheduler.m 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 6/7/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACTestExampleScheduler.h" 10 | #import "RACQueueScheduler+Subclass.h" 11 | 12 | @implementation RACTestExampleScheduler 13 | 14 | #pragma mark Lifecycle 15 | 16 | - (instancetype)initWithQueue:(dispatch_queue_t)queue { 17 | return [super initWithName:nil queue:queue]; 18 | } 19 | 20 | #pragma mark RACScheduler 21 | 22 | - (RACDisposable *)schedule:(void (^)(void))block { 23 | dispatch_async(self.queue, ^{ 24 | [self performAsCurrentScheduler:block]; 25 | }); 26 | 27 | return nil; 28 | } 29 | 30 | - (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { 31 | dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)([date timeIntervalSinceNow] * NSEC_PER_SEC)); 32 | dispatch_after(when, self.queue, ^{ 33 | [self performAsCurrentScheduler:block]; 34 | }); 35 | 36 | return nil; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ReactiveObjC/NSData+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+RACSupport.m 3 | // ReactiveObjC 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 | #import "RACReplaySubject.h" 11 | #import "RACScheduler.h" 12 | 13 | @implementation NSData (RACSupport) 14 | 15 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL options:(NSDataReadingOptions)options scheduler:(RACScheduler *)scheduler { 16 | NSCParameterAssert(scheduler != nil); 17 | 18 | RACReplaySubject *subject = [RACReplaySubject subject]; 19 | [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ options: %lu scheduler: %@", URL, (unsigned long)options, scheduler]; 20 | 21 | [scheduler schedule:^{ 22 | NSError *error = nil; 23 | NSData *data = [[NSData alloc] initWithContentsOfURL:URL options:options error:&error]; 24 | if (data == nil) { 25 | [subject sendError:error]; 26 | } else { 27 | [subject sendNext:data]; 28 | [subject sendCompleted]; 29 | } 30 | }]; 31 | 32 | return subject; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /ReactiveObjC/UITextField+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+RACSignalSupport.h 3 | // ReactiveObjC 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 RACChannelTerminal; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface UITextField (RACSignalSupport) 17 | 18 | /// Creates and returns a signal for the text of the field. It always starts with 19 | /// the current text. The signal sends next when the UIControlEventAllEditingEvents 20 | /// control event is fired on the control. 21 | - (RACSignal *)rac_textSignal; 22 | 23 | /// Creates a new RACChannel-based binding to the receiver. 24 | /// 25 | /// Returns a RACChannelTerminal that sends the receiver's text whenever the 26 | /// UIControlEventAllEditingEvents control event is fired, and sets the text 27 | /// to the values it receives. 28 | - (RACChannelTerminal *)rac_newTextChannel; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /ReactiveObjC/NSNotificationCenter+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+RACSupport.m 3 | // ReactiveObjC 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 | #import "RACSignal.h" 12 | #import "RACSubscriber.h" 13 | #import "RACDisposable.h" 14 | 15 | @implementation NSNotificationCenter (RACSupport) 16 | 17 | - (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object { 18 | @unsafeify(object); 19 | return [[RACSignal createSignal:^(id subscriber) { 20 | @strongify(object); 21 | id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) { 22 | [subscriber sendNext:note]; 23 | }]; 24 | 25 | return [RACDisposable disposableWithBlock:^{ 26 | [self removeObserver:observer]; 27 | }]; 28 | }] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjC/RACBlockTrampoline.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACBlockTrampoline.h 3 | // ReactiveObjC 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 | // A private class that 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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACErrorSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACErrorSignal.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACErrorSignal.h" 10 | #import "RACScheduler+Private.h" 11 | #import "RACSubscriber.h" 12 | 13 | @interface RACErrorSignal () 14 | 15 | // The error to send upon subscription. 16 | @property (nonatomic, strong, readonly) NSError *error; 17 | 18 | @end 19 | 20 | @implementation RACErrorSignal 21 | 22 | #pragma mark Lifecycle 23 | 24 | + (RACSignal *)error:(NSError *)error { 25 | RACErrorSignal *signal = [[self alloc] init]; 26 | signal->_error = error; 27 | 28 | #ifdef DEBUG 29 | [signal setNameWithFormat:@"+error: %@", error]; 30 | #else 31 | signal.name = @"+error:"; 32 | #endif 33 | 34 | return signal; 35 | } 36 | 37 | #pragma mark Subscription 38 | 39 | - (RACDisposable *)subscribe:(id)subscriber { 40 | NSCParameterAssert(subscriber != nil); 41 | 42 | return [RACScheduler.subscriptionScheduler schedule:^{ 43 | [subscriber sendError:self.error]; 44 | }]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /ReactiveObjCTests/UIActionSheetRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIActionSheetRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Dave Lee on 2013-06-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACSignal.h" 13 | #import "RACSignal+Operations.h" 14 | #import "UIActionSheet+RACSignalSupport.h" 15 | 16 | QuickSpecBegin(UIActionSheetRACSupportSpec) 17 | 18 | qck_describe(@"-rac_buttonClickedSignal", ^{ 19 | __block UIActionSheet *actionSheet; 20 | 21 | qck_beforeEach(^{ 22 | actionSheet = [[UIActionSheet alloc] init]; 23 | [actionSheet addButtonWithTitle:@"Button 0"]; 24 | [actionSheet addButtonWithTitle:@"Button 1"]; 25 | expect(actionSheet).notTo(beNil()); 26 | }); 27 | 28 | qck_it(@"should send the index of the clicked button", ^{ 29 | __block NSNumber *index = nil; 30 | [actionSheet.rac_buttonClickedSignal subscribeNext:^(NSNumber *i) { 31 | index = i; 32 | }]; 33 | 34 | [actionSheet.delegate actionSheet:actionSheet clickedButtonAtIndex:1]; 35 | expect(index).to(equal(@1)); 36 | }); 37 | }); 38 | 39 | QuickSpecEnd 40 | -------------------------------------------------------------------------------- /ReactiveObjC/RACPassthroughSubscriber.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACPassthroughSubscriber.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-06-13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RACSubscriber.h" 11 | 12 | @class RACCompoundDisposable; 13 | @class RACSignal<__covariant ValueType>; 14 | 15 | // A private subscriber that passes through all events to another subscriber 16 | // while not disposed. 17 | @interface RACPassthroughSubscriber : NSObject 18 | 19 | // Initializes the receiver to pass through events until disposed. 20 | // 21 | // subscriber - The subscriber to forward events to. This must not be nil. 22 | // signal - The signal that will be sending events to the receiver. 23 | // disposable - When this disposable is disposed, no more events will be 24 | // forwarded. This must not be nil. 25 | // 26 | // Returns an initialized passthrough subscriber. 27 | - (instancetype)initWithSubscriber:(id)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ReactiveObjC/RACScheduler+Subclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACScheduler.m 3 | // ReactiveObjC 4 | // 5 | // Created by Miķelis Vindavs on 5/27/14. 6 | // Copyright (c) 2014 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RACScheduler.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | /// An interface for use by subclasses. 15 | /// 16 | /// Subclasses should use `-performAsCurrentScheduler:` to do the actual block 17 | /// invocation so that +[RACScheduler currentScheduler] behaves as expected. 18 | /// 19 | /// **Note that RACSchedulers are expected to be serial**. Subclasses must honor 20 | /// that contract. See `RACTargetQueueScheduler` for a queue-based scheduler 21 | /// which will enforce the serialization guarantee. 22 | @interface RACScheduler () 23 | 24 | /// Performs the given block with the receiver as the current scheduler for 25 | /// its thread. This should only be called by subclasses to perform their 26 | /// scheduled blocks. 27 | /// 28 | /// block - The block to execute. Cannot be NULL. 29 | - (void)performAsCurrentScheduler:(void (^)(void))block; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /ReactiveObjCTests/UIButtonRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIButtonRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Ash Furrow on 2013-06-06. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACControlCommandExamples.h" 13 | #import "RACTestUIButton.h" 14 | 15 | #import "UIButton+RACCommandSupport.h" 16 | #import "RACCommand.h" 17 | #import "RACDisposable.h" 18 | 19 | QuickSpecBegin(UIButtonRACSupportSpec) 20 | 21 | qck_describe(@"UIButton", ^{ 22 | __block UIButton *button; 23 | 24 | qck_beforeEach(^{ 25 | button = [RACTestUIButton button]; 26 | expect(button).notTo(beNil()); 27 | }); 28 | 29 | qck_itBehavesLike(RACControlCommandExamples, ^{ 30 | return @{ 31 | RACControlCommandExampleControl: button, 32 | RACControlCommandExampleActivateBlock: ^(UIButton *button) { 33 | #pragma clang diagnostic push 34 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 35 | [button sendActionsForControlEvents:UIControlEventTouchUpInside]; 36 | #pragma clang diagnostic pop 37 | } 38 | }; 39 | }); 40 | }); 41 | 42 | QuickSpecEnd 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2012 - 2016, 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 | -------------------------------------------------------------------------------- /ReactiveObjC/NSString+RACKeyPathUtilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACKeyPathUtilities.h 3 | // ReactiveObjC 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 | // A private category of methods to extract parts of a key path. 12 | @interface NSString (RACKeyPathUtilities) 13 | 14 | // Returns an array of the components of the receiver. 15 | // 16 | // Calling this method on a string that isn't a key path is considered undefined 17 | // behavior. 18 | - (NSArray *)rac_keyPathComponents; 19 | 20 | // Returns a key path with all the components of the receiver except for the 21 | // last one. 22 | // 23 | // Calling this method on a string that isn't a key path is considered undefined 24 | // behavior. 25 | - (NSString *)rac_keyPathByDeletingLastKeyPathComponent; 26 | 27 | // Returns a key path with all the components of the receiver expect for the 28 | // first one. 29 | // 30 | // Calling this method on a string that isn't a key path is considered undefined 31 | // behavior. 32 | - (NSString *)rac_keyPathByDeletingFirstKeyPathComponent; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ReactiveObjC/NSString+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+RACSupport.m 3 | // ReactiveObjC 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 | #import "RACReplaySubject.h" 11 | #import "RACScheduler.h" 12 | 13 | @implementation NSString (RACSupport) 14 | 15 | + (RACSignal *)rac_readContentsOfURL:(NSURL *)URL usedEncoding:(NSStringEncoding *)encoding scheduler:(RACScheduler *)scheduler { 16 | NSCParameterAssert(URL != nil); 17 | NSCParameterAssert(encoding != nil); 18 | NSCParameterAssert(scheduler != nil); 19 | 20 | RACReplaySubject *subject = [RACReplaySubject subject]; 21 | [subject setNameWithFormat:@"+rac_readContentsOfURL: %@ usedEncoding:scheduler: %@", URL, scheduler]; 22 | 23 | [scheduler schedule:^{ 24 | NSError *error = nil; 25 | NSString *string = [NSString stringWithContentsOfURL:URL usedEncoding:encoding error:&error]; 26 | if (string == nil) { 27 | [subject sendError:error]; 28 | } else { 29 | [subject sendNext:string]; 30 | [subject sendCompleted]; 31 | } 32 | }]; 33 | 34 | return subject; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACDisposable.h 3 | // ReactiveObjC 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 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /// A disposable encapsulates the work necessary to tear down and cleanup a 16 | /// subscription. 17 | @interface RACDisposable : NSObject 18 | 19 | /// Whether the receiver has been disposed. 20 | /// 21 | /// Use of this property is discouraged, since it may be set to `YES` 22 | /// concurrently at any time. 23 | /// 24 | /// This property is not KVO-compliant. 25 | @property (atomic, assign, getter = isDisposed, readonly) BOOL disposed; 26 | 27 | + (instancetype)disposableWithBlock:(void (^)(void))block; 28 | 29 | /// Performs the disposal work. Can be called multiple times, though subsequent 30 | /// calls won't do anything. 31 | - (void)dispose; 32 | 33 | /// Returns a new disposable which will dispose of this disposable when it gets 34 | /// dealloc'd. 35 | - (RACScopedDisposable *)asScopedDisposable; 36 | 37 | @end 38 | 39 | NS_ASSUME_NONNULL_END 40 | -------------------------------------------------------------------------------- /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. We are also happy to help you in our Slack room (ping [@ReactiveCocoa](https://twitter.com/ReactiveCocoa) for an invitation). 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 conventions ([Objective-c](https://github.com/github/objective-c-conventions)). If your particular case is not described in the coding convention, check the ReactiveObjC codebase. 21 | 22 | Thanks for contributing! :boom::camel: 23 | -------------------------------------------------------------------------------- /ReactiveObjCTests/NSControllerRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSControllerRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 26/10/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import 13 | #import "RACKVOChannel.h" 14 | 15 | @interface RACTestController : NSController 16 | 17 | @property (nonatomic, strong) id object; 18 | 19 | @end 20 | 21 | @implementation RACTestController 22 | 23 | @end 24 | 25 | QuickSpecBegin(NSControllerRACSupportSpec) 26 | 27 | qck_it(@"RACKVOChannel should support NSController", ^{ 28 | RACTestController *a = [[RACTestController alloc] init]; 29 | RACTestController *b = [[RACTestController alloc] init]; 30 | RACChannelTo(a, object) = RACChannelTo(b, object); 31 | expect(a.object).to(beNil()); 32 | expect(b.object).to(beNil()); 33 | 34 | a.object = a; 35 | expect(a.object).to(equal(a)); 36 | expect(b.object).to(equal(a)); 37 | 38 | b.object = b; 39 | expect(a.object).to(equal(b)); 40 | expect(b.object).to(equal(b)); 41 | 42 | a.object = nil; 43 | expect(a.object).to(beNil()); 44 | expect(b.object).to(beNil()); 45 | }); 46 | 47 | QuickSpecEnd 48 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACSubscriptingAssignmentTrampolineSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptingAssignmentTrampolineSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 9/24/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACSubscriptingAssignmentTrampoline.h" 13 | #import "RACPropertySignalExamples.h" 14 | #import "RACTestObject.h" 15 | #import "RACSubject.h" 16 | 17 | QuickSpecBegin(RACSubscriptingAssignmentTrampolineSpec) 18 | 19 | id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) { 20 | [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:testObject nilValue:nilValue][keyPath] = signal; 21 | }; 22 | 23 | qck_itBehavesLike(RACPropertySignalExamples, ^{ 24 | return @{ RACPropertySignalExamplesSetupBlock: setupBlock }; 25 | }); 26 | 27 | qck_it(@"should expand the RAC macro properly", ^{ 28 | RACSubject *subject = [RACSubject subject]; 29 | RACTestObject *testObject = [[RACTestObject alloc] init]; 30 | RAC(testObject, objectValue) = subject; 31 | 32 | [subject sendNext:@1]; 33 | expect(testObject.objectValue).to(equal(@1)); 34 | }); 35 | 36 | QuickSpecEnd 37 | -------------------------------------------------------------------------------- /ReactiveObjC/NSText+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSText+RACSignalSupport.m 3 | // ReactiveObjC 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 11 | #import "NSObject+RACDescription.h" 12 | #import "RACDisposable.h" 13 | #import "RACSignal.h" 14 | #import "RACSubscriber.h" 15 | 16 | @implementation NSText (RACSignalSupport) 17 | 18 | - (RACSignal *)rac_textSignal { 19 | @unsafeify(self); 20 | return [[[[RACSignal 21 | createSignal:^(id subscriber) { 22 | @strongify(self); 23 | id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) { 24 | [subscriber sendNext:note.object]; 25 | }]; 26 | 27 | return [RACDisposable disposableWithBlock:^{ 28 | [NSNotificationCenter.defaultCenter removeObserver:observer]; 29 | }]; 30 | }] 31 | map:^(NSText *text) { 32 | return [text.string copy]; 33 | }] 34 | startWith:[self.string copy]] 35 | setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ReactiveObjC/NSObject+RACAppKitBindings.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACAppKitBindings.h 3 | // ReactiveObjC 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 RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSObject (RACAppKitBindings) 16 | 17 | /// Invokes -rac_channelToBinding:options: without any options. 18 | - (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding; 19 | 20 | /// Applies a Cocoa binding to the receiver, then exposes a RACChannel-based 21 | /// interface for manipulating it. 22 | /// 23 | /// Creating two of the same bindings on the same object will result in undefined 24 | /// behavior. 25 | /// 26 | /// binding - The name of the binding. This must not be nil. 27 | /// options - Any options to pass to Cocoa Bindings. This may be nil. 28 | /// 29 | /// Returns a RACChannelTerminal which will send future values from the receiver, 30 | /// and update the receiver when values are sent to the terminal. 31 | - (RACChannelTerminal *)rac_channelToBinding:(NSString *)binding options:(nullable NSDictionary *)options; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /ReactiveObjC/NSObject+RACDescription.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACDescription.m 3 | // ReactiveObjC 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 | #import "RACTuple.h" 11 | 12 | @implementation NSValue (RACDescription) 13 | 14 | - (NSString *)rac_description { 15 | return self.description; 16 | } 17 | 18 | @end 19 | 20 | @implementation NSString (RACDescription) 21 | 22 | - (NSString *)rac_description { 23 | return self.description; 24 | } 25 | 26 | @end 27 | 28 | @implementation RACTuple (RACDescription) 29 | 30 | - (NSString *)rac_description { 31 | if (getenv("RAC_DEBUG_SIGNAL_NAMES") != NULL) { 32 | return self.allObjects.description; 33 | } else { 34 | return @"(description skipped)"; 35 | } 36 | } 37 | 38 | @end 39 | 40 | NSString *RACDescription(id object) { 41 | if (getenv("RAC_DEBUG_SIGNAL_NAMES") != NULL) { 42 | if ([object respondsToSelector:@selector(rac_description)]) { 43 | return [object rac_description]; 44 | } else { 45 | return [[NSString alloc] initWithFormat:@"<%@: %p>", [object class], object]; 46 | } 47 | } else { 48 | return @"(description skipped)"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ReactiveObjC/UIGestureRecognizer+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+RACSignalSupport.m 3 | // ReactiveObjC 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 11 | #import "NSObject+RACDeallocating.h" 12 | #import "NSObject+RACDescription.h" 13 | #import "RACCompoundDisposable.h" 14 | #import "RACDisposable.h" 15 | #import "RACSignal.h" 16 | #import "RACSubscriber.h" 17 | 18 | @implementation UIGestureRecognizer (RACSignalSupport) 19 | 20 | - (RACSignal *)rac_gestureSignal { 21 | @weakify(self); 22 | 23 | return [[RACSignal 24 | createSignal:^(id subscriber) { 25 | @strongify(self); 26 | 27 | [self addTarget:subscriber action:@selector(sendNext:)]; 28 | [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ 29 | [subscriber sendCompleted]; 30 | }]]; 31 | 32 | return [RACDisposable disposableWithBlock:^{ 33 | @strongify(self); 34 | [self removeTarget:subscriber action:@selector(sendNext:)]; 35 | }]; 36 | }] 37 | setNameWithFormat:@"%@ -rac_gestureSignal", RACDescription(self)]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ReactiveObjC/RACScheduler+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACScheduler+Private.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 11/29/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACScheduler.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | // The thread-specific current scheduler key. 14 | extern NSString * const RACSchedulerCurrentSchedulerKey; 15 | 16 | // A private interface for internal RAC use only. 17 | @interface RACScheduler () 18 | 19 | // A dedicated scheduler that fills two requirements: 20 | // 21 | // 1. By the time subscription happens, we need a valid +currentScheduler. 22 | // 2. Subscription should happen as soon as possible. 23 | // 24 | // To fulfill those two, if we already have a valid +currentScheduler, it 25 | // immediately executes scheduled blocks. If we don't, it will execute scheduled 26 | // blocks with a private background scheduler. 27 | + (instancetype)subscriptionScheduler; 28 | 29 | // Initializes the receiver with the given name. 30 | // 31 | // name - The name of the scheduler. If nil, a default name will be used. 32 | // 33 | // Returns the initialized object. 34 | - (instancetype)initWithName:(nullable NSString *)name; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSubscriptingAssignmentTrampoline.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptingAssignmentTrampoline.m 3 | // ReactiveObjC 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 "RACSignal+Operations.h" 11 | 12 | @interface RACSubscriptingAssignmentTrampoline () 13 | 14 | // The object to bind to. 15 | @property (nonatomic, strong, readonly) id target; 16 | 17 | // A value to use when `nil` is sent on the bound signal. 18 | @property (nonatomic, strong, readonly) id nilValue; 19 | 20 | @end 21 | 22 | @implementation RACSubscriptingAssignmentTrampoline 23 | 24 | - (instancetype)initWithTarget:(id)target nilValue:(id)nilValue { 25 | // This is often a programmer error, but this prevents crashes if the target 26 | // object has unexpectedly deallocated. 27 | if (target == nil) return nil; 28 | 29 | self = [super init]; 30 | if (self == nil) return nil; 31 | 32 | _target = target; 33 | _nilValue = nilValue; 34 | 35 | return self; 36 | } 37 | 38 | - (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath { 39 | [signal setKeyPath:keyPath onObject:self.target nilValue:self.nilValue]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ReactiveObjC/UITextField+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+RACSignalSupport.m 3 | // ReactiveObjC 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 11 | #import 12 | #import "NSObject+RACDeallocating.h" 13 | #import "NSObject+RACDescription.h" 14 | #import "RACSignal+Operations.h" 15 | #import "UIControl+RACSignalSupport.h" 16 | #import "UIControl+RACSignalSupportPrivate.h" 17 | 18 | @implementation UITextField (RACSignalSupport) 19 | 20 | - (RACSignal *)rac_textSignal { 21 | @weakify(self); 22 | return [[[[[RACSignal 23 | defer:^{ 24 | @strongify(self); 25 | return [RACSignal return:self]; 26 | }] 27 | concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]] 28 | map:^(UITextField *x) { 29 | return x.text; 30 | }] 31 | takeUntil:self.rac_willDeallocSignal] 32 | setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; 33 | } 34 | 35 | - (RACChannelTerminal *)rac_newTextChannel { 36 | return [self rac_channelForControlEvents:UIControlEventAllEditingEvents key:@keypath(self.text) nilValue:@""]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACSwiftBridgingSpec.swift: -------------------------------------------------------------------------------- 1 | import Quick 2 | import Nimble 3 | import ReactiveObjC 4 | 5 | final class RACSwiftBridgingSpec: QuickSpec { 6 | override func spec() { 7 | describe("RACCommandError") { 8 | it("bridges RACCommandErrorNotEnabled to a Swift error code") { 9 | let error: Error = NSError(domain: RACCommandErrorDomain, code: 1) 10 | expect(RACCommandError.notEnabled ~= error).to(beTrue()) 11 | } 12 | } 13 | 14 | describe("RACSelectorSignalError") { 15 | it("bridges RACSelectorSignalErrorMethodSwizzlingRace to a Swift error code") { 16 | let error: Error = NSError(domain: RACSelectorSignalErrorDomain, code: 1) 17 | expect(RACSelectorSignalError.methodSwizzlingRace ~= error).to(beTrue()) 18 | } 19 | } 20 | 21 | describe("RACSignalError") { 22 | it("bridges RACSignalErrorTimedOut to a Swift error code") { 23 | let error: Error = NSError(domain: RACSignalErrorDomain, code: 1) 24 | expect(RACSignalError.timedOut ~= error).to(beTrue()) 25 | } 26 | 27 | it("bridges RACSignalErrorNoMatchingCase to a Swift error code") { 28 | let error: Error = NSError(domain: RACSignalErrorDomain, code: 2) 29 | expect(RACSignalError.noMatchingCase ~= error).to(beTrue()) 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ReactiveObjC/NSControl+RACTextSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACTextSignalSupport.m 3 | // ReactiveObjC 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 11 | #import "NSObject+RACDescription.h" 12 | #import "RACDisposable.h" 13 | #import "RACSignal.h" 14 | #import "RACSubscriber.h" 15 | 16 | @implementation NSControl (RACTextSignalSupport) 17 | 18 | - (RACSignal *)rac_textSignal { 19 | @weakify(self); 20 | return [[[[RACSignal 21 | createSignal:^(id subscriber) { 22 | @strongify(self); 23 | id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSControlTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) { 24 | [subscriber sendNext:note.object]; 25 | }]; 26 | 27 | return [RACDisposable disposableWithBlock:^{ 28 | [NSNotificationCenter.defaultCenter removeObserver:observer]; 29 | }]; 30 | }] 31 | map:^(NSControl *control) { 32 | return [control.stringValue copy]; 33 | }] 34 | startWith:[self.stringValue copy]] 35 | setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ReactiveObjCTests/UIBarButtonItemRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItemRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Kyle LeNeau on 4/13/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACControlCommandExamples.h" 13 | 14 | #import "UIBarButtonItem+RACCommandSupport.h" 15 | #import "RACCommand.h" 16 | #import "RACDisposable.h" 17 | 18 | QuickSpecBegin(UIBarButtonItemRACSupportSpec) 19 | 20 | qck_describe(@"UIBarButtonItem", ^{ 21 | __block UIBarButtonItem *button; 22 | 23 | qck_beforeEach(^{ 24 | button = [[UIBarButtonItem alloc] init]; 25 | expect(button).notTo(beNil()); 26 | }); 27 | 28 | qck_itBehavesLike(RACControlCommandExamples, ^{ 29 | return @{ 30 | RACControlCommandExampleControl: button, 31 | RACControlCommandExampleActivateBlock: ^(UIBarButtonItem *button) { 32 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[button.target methodSignatureForSelector:button.action]]; 33 | invocation.selector = button.action; 34 | 35 | id target = button.target; 36 | [invocation setArgument:&target atIndex:2]; 37 | [invocation invokeWithTarget:target]; 38 | } 39 | }; 40 | }); 41 | }); 42 | 43 | QuickSpecEnd 44 | -------------------------------------------------------------------------------- /ReactiveObjC/RACKVOTrampoline.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACKVOTrampoline.h 3 | // ReactiveObjC 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 | #import "RACDisposable.h" 12 | 13 | // A private trampoline object that represents a KVO observation. 14 | // 15 | // Disposing of the trampoline will stop observation. 16 | @interface RACKVOTrampoline : RACDisposable 17 | 18 | // Initializes the receiver with the given parameters. 19 | // 20 | // target - The object whose key path should be observed. Cannot be nil. 21 | // observer - The object that gets notified when the value at the key path 22 | // changes. Can be nil. 23 | // keyPath - The key path on `target` to observe. Cannot be nil. 24 | // options - Any key value observing options to use in the observation. 25 | // block - The block to call when the value at the observed key path changes. 26 | // Cannot be nil. 27 | // 28 | // Returns the initialized object. 29 | - (instancetype)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ReactiveObjC/NSFileHandle+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileHandle+RACSupport.m 3 | // ReactiveObjC 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 | #import "NSObject+RACDescription.h" 12 | #import "RACReplaySubject.h" 13 | #import "RACDisposable.h" 14 | 15 | @implementation NSFileHandle (RACSupport) 16 | 17 | - (RACSignal *)rac_readInBackground { 18 | RACReplaySubject *subject = [RACReplaySubject subject]; 19 | [subject setNameWithFormat:@"%@ -rac_readInBackground", RACDescription(self)]; 20 | 21 | RACSignal *dataNotification = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:NSFileHandleReadCompletionNotification object:self] map:^(NSNotification *note) { 22 | return note.userInfo[NSFileHandleNotificationDataItem]; 23 | }]; 24 | 25 | __block RACDisposable *subscription = [dataNotification subscribeNext:^(NSData *data) { 26 | if (data.length > 0) { 27 | [subject sendNext:data]; 28 | [self readInBackgroundAndNotify]; 29 | } else { 30 | [subject sendCompleted]; 31 | [subscription dispose]; 32 | } 33 | }]; 34 | 35 | [self readInBackgroundAndNotify]; 36 | 37 | return subject; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACSubclassObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubclassObject.m 3 | // ReactiveObjC 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 | #import "RACScheduler.h" 11 | 12 | @implementation RACSubclassObject 13 | 14 | - (void)forwardInvocation:(NSInvocation *)invocation { 15 | self.forwardedSelector = invocation.selector; 16 | } 17 | 18 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 19 | NSParameterAssert(selector != NULL); 20 | 21 | NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 22 | if (signature != nil) return signature; 23 | 24 | return [super methodSignatureForSelector:@selector(description)]; 25 | } 26 | 27 | - (NSString *)combineObjectValue:(id)objectValue andIntegerValue:(NSInteger)integerValue { 28 | NSString *appended = [[objectValue description] stringByAppendingString:@"SUBCLASS"]; 29 | return [super combineObjectValue:appended andIntegerValue:integerValue]; 30 | } 31 | 32 | - (void)setObjectValue:(id)objectValue andSecondObjectValue:(id)secondObjectValue { 33 | [RACScheduler.currentScheduler schedule:^{ 34 | [super setObjectValue:objectValue andSecondObjectValue:secondObjectValue]; 35 | }]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ReactiveObjC/UIControl+RACSignalSupportPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+RACSignalSupportPrivate.h 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 06/08/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACChannelTerminal; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UIControl (RACSignalSupportPrivate) 16 | 17 | /// Adds a RACChannel-based interface to the receiver for the given 18 | /// UIControlEvents and exposes it. 19 | /// 20 | /// controlEvents - A mask of UIControlEvents on which to send new values. 21 | /// key - The key whose value should be read and set when a control 22 | /// event fires and when a value is sent to the 23 | /// RACChannelTerminal respectively. 24 | /// nilValue - The value to be assigned to the key when `nil` is sent to the 25 | /// RACChannelTerminal. This value can itself be nil. 26 | /// 27 | /// Returns a RACChannelTerminal which will send future values from the receiver, 28 | /// and update the receiver when values are sent to the terminal. 29 | - (RACChannelTerminal *)rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(nullable id)nilValue; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /ReactiveObjCTests/NSObjectRACAppKitBindingsSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectRACAppKitBindingsSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-07-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACChannelExamples.h" 13 | 14 | #import 15 | #import "NSObject+RACAppKitBindings.h" 16 | 17 | QuickSpecBegin(NSObjectRACAppKitBindingsSpec) 18 | 19 | qck_itBehavesLike(RACViewChannelExamples, ^{ 20 | return @{ 21 | RACViewChannelExampleCreateViewBlock: ^{ 22 | return [[NSSlider alloc] initWithFrame:NSZeroRect]; 23 | }, 24 | RACViewChannelExampleCreateTerminalBlock: ^(NSSlider *view) { 25 | return [view rac_channelToBinding:NSValueBinding]; 26 | }, 27 | RACViewChannelExampleKeyPath: @keypath(NSSlider.new, objectValue), 28 | RACViewChannelExampleSetViewValueBlock: ^(NSSlider *view, NSNumber *value) { 29 | view.objectValue = value; 30 | 31 | // Bindings don't actually trigger from programmatic modification. Do it 32 | // manually. 33 | NSDictionary *bindingInfo = [view infoForBinding:NSValueBinding]; 34 | [bindingInfo[NSObservedObjectKey] setValue:value forKeyPath:bindingInfo[NSObservedKeyPathKey]]; 35 | } 36 | }; 37 | }); 38 | 39 | QuickSpecEnd 40 | -------------------------------------------------------------------------------- /ReactiveObjCTests/NSURLConnectionRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLConnectionRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "NSURLConnection+RACSupport.h" 13 | #import "RACSignal+Operations.h" 14 | #import "RACTuple.h" 15 | 16 | QuickSpecBegin(NSURLConnectionRACSupportSpec) 17 | 18 | qck_it(@"should fetch a JSON file", ^{ 19 | NSURL *fileURL = [[NSBundle bundleForClass:self.class] URLForResource:@"test-data" withExtension:@"json"]; 20 | expect(fileURL).notTo(beNil()); 21 | 22 | NSURLRequest *request = [NSURLRequest requestWithURL:fileURL]; 23 | 24 | BOOL success = NO; 25 | NSError *error = nil; 26 | RACTuple *result = [[NSURLConnection rac_sendAsynchronousRequest:request] firstOrDefault:nil success:&success error:&error]; 27 | expect(@(success)).to(beTruthy()); 28 | expect(error).to(beNil()); 29 | expect(result).to(beAKindOf(RACTuple.class)); 30 | 31 | NSURLResponse *response = result.first; 32 | expect(response).to(beAKindOf(NSURLResponse.class)); 33 | 34 | NSData *data = result.second; 35 | expect(data).to(beAKindOf(NSData.class)); 36 | expect(data).to(equal([NSData dataWithContentsOfURL:fileURL])); 37 | }); 38 | 39 | QuickSpecEnd 40 | -------------------------------------------------------------------------------- /ReactiveObjC/UIActionSheet+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIActionSheet+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Dave Lee on 2013-06-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACDelegateProxy; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface UIActionSheet (RACSignalSupport) 17 | 18 | /// A delegate proxy which will be set as the receiver's delegate when any of the 19 | /// methods in this category are used. 20 | @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; 21 | 22 | /// Creates a signal for button clicks on the receiver. 23 | /// 24 | /// When this method is invoked, the `rac_delegateProxy` will become the 25 | /// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy 26 | /// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't 27 | /// know how to handle. Setting the receiver's `delegate` afterward is 28 | /// considered undefined behavior. 29 | /// 30 | /// Returns a signal which will send the index of the specific button clicked. 31 | /// The signal will complete when the receiver is deallocated. 32 | - (RACSignal *)rac_buttonClickedSignal; 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /ReactiveObjC/NSDictionary+RACSequenceAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+RACSequenceAdditions.h 3 | // ReactiveObjC 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<__covariant ValueType>; 12 | @class RACTwoTuple<__covariant First, __covariant Second>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSDictionary<__covariant KeyType, __covariant ObjectType> (RACSequenceAdditions) 17 | 18 | /// Creates and returns a sequence of key/value tuples. 19 | /// 20 | /// Mutating the receiver will not affect the sequence after it's been created. 21 | @property (nonatomic, copy, readonly) RACSequence *> *rac_sequence; 22 | 23 | /// Creates and returns a sequence corresponding to the keys in the receiver. 24 | /// 25 | /// Mutating the receiver will not affect the sequence after it's been created. 26 | @property (nonatomic, copy, readonly) RACSequence *rac_keySequence; 27 | 28 | /// Creates and returns a sequence corresponding to the values in the receiver. 29 | /// 30 | /// Mutating the receiver will not affect the sequence after it's been created. 31 | @property (nonatomic, copy, readonly) RACSequence *rac_valueSequence; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /ReactiveObjC/UITextView+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+RACSignalSupport.h 3 | // ReactiveObjC 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 RACDelegateProxy; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface UITextView (RACSignalSupport) 17 | 18 | /// A delegate proxy which will be set as the receiver's delegate when any of the 19 | /// methods in this category are used. 20 | @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; 21 | 22 | /// Creates a signal for the text of the receiver. 23 | /// 24 | /// When this method is invoked, the `rac_delegateProxy` will become the 25 | /// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy 26 | /// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't 27 | /// know how to handle. Setting the receiver's `delegate` afterward is 28 | /// considered undefined behavior. 29 | /// 30 | /// Returns a signal which will send the current text upon subscription, then 31 | /// again whenever the receiver's text is changed. The signal will complete when 32 | /// the receiver is deallocated. 33 | - (RACSignal *)rac_textSignal; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /script/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BUILD_DIRECTORY="build" 4 | CONFIGURATION=Release 5 | 6 | if [[ -z $TRAVIS_XCODE_WORKSPACE ]]; then 7 | echo "Error: \$TRAVIS_XCODE_WORKSPACE is not set." 8 | exit 1 9 | fi 10 | 11 | if [[ -z $TRAVIS_XCODE_SCHEME ]]; then 12 | echo "Error: \$TRAVIS_XCODE_SCHEME is not set!" 13 | exit 1 14 | fi 15 | 16 | if [[ -z $XCODE_ACTION ]]; then 17 | echo "Error: \$XCODE_ACTION is not set!" 18 | exit 1 19 | fi 20 | 21 | if [[ -z $XCODE_SDK ]]; then 22 | echo "Error: \$XCODE_SDK is not set!" 23 | exit 1 24 | fi 25 | 26 | if [[ -z $XCODE_DESTINATION ]]; then 27 | echo "Error: \$XCODE_DESTINATION is not set!" 28 | exit 1 29 | fi 30 | 31 | set -o pipefail 32 | xcodebuild $XCODE_ACTION \ 33 | -workspace "$TRAVIS_XCODE_WORKSPACE" \ 34 | -scheme "$TRAVIS_XCODE_SCHEME" \ 35 | -sdk "$XCODE_SDK" \ 36 | -destination "$XCODE_DESTINATION" \ 37 | -derivedDataPath "${BUILD_DIRECTORY}" \ 38 | -configuration $CONFIGURATION \ 39 | ENABLE_TESTABILITY=YES \ 40 | GCC_GENERATE_DEBUGGING_SYMBOLS=NO \ 41 | RUN_CLANG_STATIC_ANALYZER=NO | xcpretty 42 | result=$? 43 | 44 | if [ "$result" -ne 0 ]; then 45 | exit $result 46 | fi 47 | 48 | # Compile code in playgrounds 49 | if [[ "$XCODE_PLAYGROUND_TARGET" ]]; then 50 | echo "Validating playground..." 51 | . script/validate-playground.sh 52 | fi 53 | -------------------------------------------------------------------------------- /ReactiveObjC/RACKVOProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACKVOProxy.h 3 | // ReactiveObjC 4 | // 5 | // Created by Richard Speyer on 4/10/14. 6 | // Copyright (c) 2014 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /// A singleton that can act as a proxy between a KVO observation and a RAC 12 | /// subscriber, in order to protect against KVO lifetime issues. 13 | @interface RACKVOProxy : NSObject 14 | 15 | /// Returns the singleton KVO proxy object. 16 | + (instancetype)sharedProxy; 17 | 18 | /// Registers an observer with the proxy, such that when the proxy receives a 19 | /// KVO change with the given context, it forwards it to the observer. 20 | /// 21 | /// observer - True observer of the KVO change. Must not be nil. 22 | /// context - Arbitrary context object used to differentiate multiple 23 | /// observations of the same keypath. Must be unique, cannot be nil. 24 | - (void)addObserver:(__weak NSObject *)observer forContext:(void *)context; 25 | 26 | /// Removes an observer from the proxy. Parameters must match those passed to 27 | /// addObserver:forContext:. 28 | /// 29 | /// observer - True observer of the KVO change. Must not be nil. 30 | /// context - Arbitrary context object used to differentiate multiple 31 | /// observations of the same keypath. Must be unique, cannot be nil. 32 | - (void)removeObserver:(NSObject *)observer forContext:(void *)context; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ReactiveObjC/UIImagePickerController+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImagePickerController+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Timur Kuchkarov on 28.03.14. 6 | // Copyright (c) 2014 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACDelegateProxy; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface UIImagePickerController (RACSignalSupport) 17 | 18 | /// A delegate proxy which will be set as the receiver's delegate when any of the 19 | /// methods in this category are used. 20 | @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; 21 | 22 | /// Creates a signal for every new selected image. 23 | /// 24 | /// When this method is invoked, the `rac_delegateProxy` will become the 25 | /// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy 26 | /// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't 27 | /// know how to handle. Setting the receiver's `delegate` afterward is considered 28 | /// undefined behavior. 29 | /// 30 | /// Returns a signal which will send the dictionary with info for the selected image. 31 | /// Caller is responsible for picker controller dismissal. The signal will complete 32 | /// itself when the receiver is deallocated or when user cancels selection. 33 | - (RACSignal *)rac_imageSelectedSignal; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /ReactiveObjC/UIControl+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+RACSignalSupport.m 3 | // ReactiveObjC 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 11 | #import "RACCompoundDisposable.h" 12 | #import "RACDisposable.h" 13 | #import "RACSignal.h" 14 | #import "RACSubscriber.h" 15 | #import "NSObject+RACDeallocating.h" 16 | #import "NSObject+RACDescription.h" 17 | 18 | @implementation UIControl (RACSignalSupport) 19 | 20 | - (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents { 21 | @weakify(self); 22 | 23 | return [[RACSignal 24 | createSignal:^(id subscriber) { 25 | @strongify(self); 26 | 27 | [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; 28 | 29 | RACDisposable *disposable = [RACDisposable disposableWithBlock:^{ 30 | [subscriber sendCompleted]; 31 | }]; 32 | [self.rac_deallocDisposable addDisposable:disposable]; 33 | 34 | return [RACDisposable disposableWithBlock:^{ 35 | @strongify(self); 36 | [self.rac_deallocDisposable removeDisposable:disposable]; 37 | [self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; 38 | }]; 39 | }] 40 | setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", RACDescription(self), (unsigned long)controlEvents]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACChannelExamples.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACChannelExamples.h 3 | // ReactiveObjC 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 RACChannel and its subclasses. 10 | extern NSString * const RACChannelExamples; 11 | 12 | // A block of type `RACChannel * (^)(void)`, which should return a new 13 | // RACChannel. 14 | extern NSString * const RACChannelExampleCreateBlock; 15 | 16 | // The name of the shared examples for any RACChannel class that gets and sets 17 | // a property. 18 | extern NSString * const RACViewChannelExamples; 19 | 20 | // A block of type `NSObject * (^)(void)`, which should create a new test view 21 | // and return it. 22 | extern NSString * const RACViewChannelExampleCreateViewBlock; 23 | 24 | // A block of type `RACChannelTerminal * (^)(NSObject *view)`, which should 25 | // create a new RACChannel to the given test view and return an terminal. 26 | extern NSString * const RACViewChannelExampleCreateTerminalBlock; 27 | 28 | // The key path that will be read/written in RACViewChannelExamples. This 29 | // must lead to an NSNumber or numeric primitive property. 30 | extern NSString * const RACViewChannelExampleKeyPath; 31 | 32 | // A block of type `void (^)(NSObject *view, NSNumber *value)`, which should 33 | // change the given test view's value to the given one. 34 | extern NSString * const RACViewChannelExampleSetViewValueBlock; 35 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEmptySignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEmptySignal.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACEmptySignal.h" 10 | #import "RACScheduler+Private.h" 11 | #import "RACSubscriber.h" 12 | 13 | @implementation RACEmptySignal 14 | 15 | #pragma mark Properties 16 | 17 | // Only allow this signal's name to be customized in DEBUG, since it's 18 | // a singleton in release builds (see +empty). 19 | - (void)setName:(NSString *)name { 20 | #ifdef DEBUG 21 | [super setName:name]; 22 | #endif 23 | } 24 | 25 | - (NSString *)name { 26 | #ifdef DEBUG 27 | return super.name; 28 | #else 29 | return @"+empty"; 30 | #endif 31 | } 32 | 33 | #pragma mark Lifecycle 34 | 35 | + (RACSignal *)empty { 36 | #ifdef DEBUG 37 | // Create multiple instances of this class in DEBUG so users can set custom 38 | // names on each. 39 | return [[[self alloc] init] setNameWithFormat:@"+empty"]; 40 | #else 41 | static id singleton; 42 | static dispatch_once_t pred; 43 | 44 | dispatch_once(&pred, ^{ 45 | singleton = [[self alloc] init]; 46 | }); 47 | 48 | return singleton; 49 | #endif 50 | } 51 | 52 | #pragma mark Subscription 53 | 54 | - (RACDisposable *)subscribe:(id)subscriber { 55 | NSCParameterAssert(subscriber != nil); 56 | 57 | return [RACScheduler.subscriptionScheduler schedule:^{ 58 | [subscriber sendCompleted]; 59 | }]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ReactiveObjC/RACBehaviorSubject.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACBehaviorSubject.m 3 | // ReactiveObjC 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) ValueType 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 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACBlockTrampolineSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACBlockTrampolineSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 10/28/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACBlockTrampoline.h" 13 | #import "RACTuple.h" 14 | 15 | QuickSpecBegin(RACBlockTrampolineSpec) 16 | 17 | qck_it(@"should invoke the block with the given arguments", ^{ 18 | __block NSString *stringArg; 19 | __block NSNumber *numberArg; 20 | id (^block)(NSString *, NSNumber *) = ^ id (NSString *string, NSNumber *number) { 21 | stringArg = string; 22 | numberArg = number; 23 | return nil; 24 | }; 25 | 26 | [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi", @1)]; 27 | expect(stringArg).to(equal(@"hi")); 28 | expect(numberArg).to(equal(@1)); 29 | }); 30 | 31 | qck_it(@"should return the result of the block invocation", ^{ 32 | NSString * (^block)(NSString *) = ^(NSString *string) { 33 | return string.uppercaseString; 34 | }; 35 | 36 | NSString *result = [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(@"hi")]; 37 | expect(result).to(equal(@"HI")); 38 | }); 39 | 40 | qck_it(@"should pass RACTupleNils as nil", ^{ 41 | __block id arg; 42 | id (^block)(id) = ^ id (id obj) { 43 | arg = obj; 44 | return nil; 45 | }; 46 | 47 | [RACBlockTrampoline invokeBlock:block withArguments:RACTuplePack(nil)]; 48 | expect(arg).to(beNil()); 49 | }); 50 | 51 | QuickSpecEnd 52 | -------------------------------------------------------------------------------- /ReactiveObjCTests/UIAlertViewRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertViewRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Henrik Hodne on 6/16/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import 13 | #import "RACSignal.h" 14 | #import "UIAlertView+RACSignalSupport.h" 15 | 16 | QuickSpecBegin(UIAlertViewRACSupportSpec) 17 | 18 | qck_describe(@"UIAlertView", ^{ 19 | __block UIAlertView *alertView; 20 | 21 | qck_beforeEach(^{ 22 | alertView = [[UIAlertView alloc] initWithFrame:CGRectZero]; 23 | expect(alertView).notTo(beNil()); 24 | }); 25 | 26 | qck_it(@"sends the index of the clicked button to the buttonClickedSignal when a button is clicked", ^{ 27 | __block NSInteger index = -1; 28 | [alertView.rac_buttonClickedSignal subscribeNext:^(NSNumber *sentIndex) { 29 | index = sentIndex.integerValue; 30 | }]; 31 | 32 | [alertView.delegate alertView:alertView clickedButtonAtIndex:2]; 33 | expect(@(index)).to(equal(@2)); 34 | }); 35 | 36 | qck_it(@"sends the index of the appropriate button to the willDismissSignal when dismissed programatically", ^{ 37 | __block NSInteger index = -1; 38 | [alertView.rac_willDismissSignal subscribeNext:^(NSNumber *sentIndex) { 39 | index = sentIndex.integerValue; 40 | }]; 41 | 42 | [alertView.delegate alertView:alertView willDismissWithButtonIndex:2]; 43 | expect(@(index)).to(equal(@2)); 44 | }); 45 | }); 46 | 47 | QuickSpecEnd 48 | -------------------------------------------------------------------------------- /ReactiveObjC/RACImmediateScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACImmediateScheduler.m 3 | // ReactiveObjC 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 "RACScheduler+Private.h" 11 | 12 | @implementation RACImmediateScheduler 13 | 14 | #pragma mark Lifecycle 15 | 16 | - (instancetype)init { 17 | return [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.immediateScheduler"]; 18 | } 19 | 20 | #pragma mark RACScheduler 21 | 22 | - (RACDisposable *)schedule:(void (^)(void))block { 23 | NSCParameterAssert(block != NULL); 24 | 25 | block(); 26 | return nil; 27 | } 28 | 29 | - (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { 30 | NSCParameterAssert(date != nil); 31 | NSCParameterAssert(block != NULL); 32 | 33 | [NSThread sleepUntilDate:date]; 34 | block(); 35 | 36 | return nil; 37 | } 38 | 39 | - (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { 40 | NSCAssert(NO, @"+[RACScheduler immediateScheduler] does not support %@.", NSStringFromSelector(_cmd)); 41 | return nil; 42 | } 43 | 44 | - (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock { 45 | for (__block NSUInteger remaining = 1; remaining > 0; remaining--) { 46 | recursiveBlock(^{ 47 | remaining++; 48 | }); 49 | } 50 | 51 | return nil; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ReactiveObjC/RACQueueScheduler+Subclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACQueueScheduler+Subclass.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 6/6/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACQueueScheduler.h" 10 | #import "RACScheduler+Subclass.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | /// An interface for use by GCD queue-based subclasses. 15 | /// 16 | /// See RACScheduler+Subclass.h for subclassing notes. 17 | @interface RACQueueScheduler () 18 | 19 | /// The queue on which blocks are enqueued. 20 | #if OS_OBJECT_USE_OBJC 21 | @property (nonatomic, strong, readonly) dispatch_queue_t queue; 22 | #else 23 | // Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :( 24 | @property (nonatomic, assign, readonly) dispatch_queue_t queue; 25 | #endif 26 | 27 | /// Initializes the receiver with the name of the scheduler and the queue which 28 | /// the scheduler should use. 29 | /// 30 | /// name - The name of the scheduler. If nil, a default name will be used. 31 | /// queue - The queue upon which the receiver should enqueue scheduled blocks. 32 | /// This argument must not be NULL. 33 | /// 34 | /// Returns the initialized object. 35 | - (instancetype)initWithName:(nullable NSString *)name queue:(dispatch_queue_t)queue; 36 | 37 | /// Converts a date into a GCD time using dispatch_walltime(). 38 | /// 39 | /// date - The date to convert. This must not be nil. 40 | + (dispatch_time_t)wallTimeWithDate:(NSDate *)date; 41 | 42 | @end 43 | 44 | NS_ASSUME_NONNULL_END 45 | -------------------------------------------------------------------------------- /ReactiveObjC/RACTestScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACTestScheduler.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-07-06. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACScheduler.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A special kind of scheduler that steps through virtualized time. 14 | /// 15 | /// This scheduler class can be used in unit tests to verify asynchronous 16 | /// behaviors without spending significant time waiting. 17 | /// 18 | /// This class can be used from multiple threads, but only one thread can `step` 19 | /// through the enqueued actions at a time. Other threads will wait while the 20 | /// scheduled blocks are being executed. 21 | @interface RACTestScheduler : RACScheduler 22 | 23 | /// Initializes a new test scheduler. 24 | - (instancetype)init; 25 | 26 | /// Executes the next scheduled block, if any. 27 | /// 28 | /// This method will block until the scheduled action has completed. 29 | - (void)step; 30 | 31 | /// Executes up to the next `ticks` scheduled blocks. 32 | /// 33 | /// This method will block until the scheduled actions have completed. 34 | /// 35 | /// ticks - The number of scheduled blocks to execute. If there aren't this many 36 | /// blocks enqueued, all scheduled blocks are executed. 37 | - (void)step:(NSUInteger)ticks; 38 | 39 | /// Executes all of the scheduled blocks on the receiver. 40 | /// 41 | /// This method will block until the scheduled actions have completed. 42 | - (void)stepAll; 43 | 44 | @end 45 | 46 | NS_ASSUME_NONNULL_END 47 | -------------------------------------------------------------------------------- /ReactiveObjC/UIActionSheet+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIActionSheet+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Dave Lee on 2013-06-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIActionSheet+RACSignalSupport.h" 10 | #import "RACDelegateProxy.h" 11 | #import "RACSignal+Operations.h" 12 | #import "NSObject+RACDeallocating.h" 13 | #import "NSObject+RACDescription.h" 14 | #import 15 | 16 | @implementation UIActionSheet (RACSignalSupport) 17 | 18 | static void RACUseDelegateProxy(UIActionSheet *self) { 19 | if (self.delegate == self.rac_delegateProxy) return; 20 | 21 | self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; 22 | self.delegate = (id)self.rac_delegateProxy; 23 | } 24 | 25 | - (RACDelegateProxy *)rac_delegateProxy { 26 | RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); 27 | if (proxy == nil) { 28 | proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIActionSheetDelegate)]; 29 | objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 30 | } 31 | 32 | return proxy; 33 | } 34 | 35 | - (RACSignal *)rac_buttonClickedSignal { 36 | RACSignal *signal = [[[[self.rac_delegateProxy 37 | signalForSelector:@selector(actionSheet:clickedButtonAtIndex:)] 38 | reduceEach:^(UIActionSheet *actionSheet, NSNumber *buttonIndex) { 39 | return buttonIndex; 40 | }] 41 | takeUntil:self.rac_willDeallocSignal] 42 | setNameWithFormat:@"%@ -rac_buttonClickedSignal", RACDescription(self)]; 43 | 44 | RACUseDelegateProxy(self); 45 | 46 | return signal; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ReactiveObjC/UIControl+RACSignalSupportPrivate.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+RACSignalSupportPrivate.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 06/08/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIControl+RACSignalSupportPrivate.h" 10 | #import "NSObject+RACDeallocating.h" 11 | #import "NSObject+RACLifting.h" 12 | #import "RACChannel.h" 13 | #import "RACCompoundDisposable.h" 14 | #import "RACDisposable.h" 15 | #import "RACSignal+Operations.h" 16 | #import "UIControl+RACSignalSupport.h" 17 | 18 | @implementation UIControl (RACSignalSupportPrivate) 19 | 20 | - (RACChannelTerminal *)rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(id)nilValue { 21 | NSCParameterAssert(key.length > 0); 22 | key = [key copy]; 23 | RACChannel *channel = [[RACChannel alloc] init]; 24 | 25 | [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ 26 | [channel.followingTerminal sendCompleted]; 27 | }]]; 28 | 29 | RACSignal *eventSignal = [[[self 30 | rac_signalForControlEvents:controlEvents] 31 | mapReplace:key] 32 | takeUntil:[[channel.followingTerminal 33 | ignoreValues] 34 | catchTo:RACSignal.empty]]; 35 | [[self 36 | rac_liftSelector:@selector(valueForKey:) withSignals:eventSignal, nil] 37 | subscribe:channel.followingTerminal]; 38 | 39 | RACSignal *valuesSignal = [channel.followingTerminal 40 | map:^(id value) { 41 | return value ?: nilValue; 42 | }]; 43 | [self rac_liftSelector:@selector(setValue:forKey:) withSignals:valuesSignal, [RACSignal return:key], nil]; 44 | 45 | return channel.leadingTerminal; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACTargetQueueSchedulerSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTargetQueueSchedulerSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 6/7/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACTargetQueueScheduler.h" 13 | #import 14 | 15 | QuickSpecBegin(RACTargetQueueSchedulerSpec) 16 | 17 | qck_it(@"should have a valid current scheduler", ^{ 18 | dispatch_queue_t queue = dispatch_queue_create("test-queue", DISPATCH_QUEUE_SERIAL); 19 | RACScheduler *scheduler = [[RACTargetQueueScheduler alloc] initWithName:@"test-scheduler" targetQueue:queue]; 20 | __block RACScheduler *currentScheduler; 21 | [scheduler schedule:^{ 22 | currentScheduler = RACScheduler.currentScheduler; 23 | }]; 24 | 25 | expect(currentScheduler).toEventually(equal(scheduler)); 26 | }); 27 | 28 | qck_it(@"should schedule blocks FIFO even when given a concurrent queue", ^{ 29 | dispatch_queue_t queue = dispatch_queue_create("test-queue", DISPATCH_QUEUE_CONCURRENT); 30 | RACScheduler *scheduler = [[RACTargetQueueScheduler alloc] initWithName:@"test-scheduler" targetQueue:queue]; 31 | __block volatile int32_t startedCount = 0; 32 | __block volatile uint32_t waitInFirst = 1; 33 | [scheduler schedule:^{ 34 | OSAtomicIncrement32Barrier(&startedCount); 35 | while (waitInFirst == 1) ; 36 | }]; 37 | 38 | [scheduler schedule:^{ 39 | OSAtomicIncrement32Barrier(&startedCount); 40 | }]; 41 | 42 | expect(@(startedCount)).toEventually(equal(@1)); 43 | 44 | OSAtomicAnd32Barrier(0, &waitInFirst); 45 | 46 | expect(@(startedCount)).toEventually(equal(@2)); 47 | }); 48 | 49 | QuickSpecEnd 50 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEmptySequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEmptySequence.m 3 | // ReactiveObjC 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 | - (instancetype)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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSubscriptionScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriptionScheduler.m 3 | // ReactiveObjC 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 | - (instancetype)init { 25 | self = [super initWithName:@"org.reactivecocoa.ReactiveObjC.RACScheduler.subscriptionScheduler"]; 26 | 27 | _backgroundScheduler = [RACScheduler scheduler]; 28 | 29 | return self; 30 | } 31 | 32 | #pragma mark RACScheduler 33 | 34 | - (RACDisposable *)schedule:(void (^)(void))block { 35 | NSCParameterAssert(block != NULL); 36 | 37 | if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block]; 38 | 39 | block(); 40 | return nil; 41 | } 42 | 43 | - (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block { 44 | RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler; 45 | return [scheduler after:date schedule:block]; 46 | } 47 | 48 | - (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block { 49 | RACScheduler *scheduler = RACScheduler.currentScheduler ?: self.backgroundScheduler; 50 | return [scheduler after:date repeatingEvery:interval withLeeway:leeway schedule:block]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /ReactiveObjC/UITextView+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+RACSignalSupport.m 3 | // ReactiveObjC 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 11 | #import "NSObject+RACDeallocating.h" 12 | #import "NSObject+RACDescription.h" 13 | #import "RACDelegateProxy.h" 14 | #import "RACSignal+Operations.h" 15 | #import "RACTuple.h" 16 | #import 17 | 18 | @implementation UITextView (RACSignalSupport) 19 | 20 | static void RACUseDelegateProxy(UITextView *self) { 21 | if (self.delegate == self.rac_delegateProxy) return; 22 | 23 | self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; 24 | self.delegate = (id)self.rac_delegateProxy; 25 | } 26 | 27 | - (RACDelegateProxy *)rac_delegateProxy { 28 | RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); 29 | if (proxy == nil) { 30 | proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextViewDelegate)]; 31 | objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 32 | } 33 | 34 | return proxy; 35 | } 36 | 37 | - (RACSignal *)rac_textSignal { 38 | @weakify(self); 39 | RACSignal *signal = [[[[[RACSignal 40 | defer:^{ 41 | @strongify(self); 42 | return [RACSignal return:RACTuplePack(self)]; 43 | }] 44 | concat:[self.rac_delegateProxy signalForSelector:@selector(textViewDidChange:)]] 45 | reduceEach:^(UITextView *x) { 46 | return x.text; 47 | }] 48 | takeUntil:self.rac_willDeallocSignal] 49 | setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)]; 50 | 51 | RACUseDelegateProxy(self); 52 | 53 | return signal; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /ReactiveObjC/NSUserDefaults+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults+RACSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Matt Diephouse on 12/19/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSUserDefaults+RACSupport.h" 10 | #import 11 | #import "NSNotificationCenter+RACSupport.h" 12 | #import "NSObject+RACDeallocating.h" 13 | #import "RACChannel.h" 14 | #import "RACScheduler.h" 15 | #import "RACSignal+Operations.h" 16 | 17 | @implementation NSUserDefaults (RACSupport) 18 | 19 | - (RACChannelTerminal *)rac_channelTerminalForKey:(NSString *)key { 20 | NSParameterAssert(key != nil); 21 | 22 | RACChannel *channel = [RACChannel new]; 23 | 24 | RACScheduler *scheduler = [RACScheduler scheduler]; 25 | __block BOOL ignoreNextValue = NO; 26 | 27 | @weakify(self); 28 | [[[[[[[NSNotificationCenter.defaultCenter 29 | rac_addObserverForName:NSUserDefaultsDidChangeNotification object:self] 30 | map:^(id _) { 31 | @strongify(self); 32 | return [self objectForKey:key]; 33 | }] 34 | startWith:[self objectForKey:key]] 35 | // Don't send values that were set on the other side of the terminal. 36 | filter:^ BOOL (id _) { 37 | if (RACScheduler.currentScheduler == scheduler && ignoreNextValue) { 38 | ignoreNextValue = NO; 39 | return NO; 40 | } 41 | return YES; 42 | }] 43 | distinctUntilChanged] 44 | takeUntil:self.rac_willDeallocSignal] 45 | subscribe:channel.leadingTerminal]; 46 | 47 | [[channel.leadingTerminal 48 | deliverOn:scheduler] 49 | subscribeNext:^(id value) { 50 | @strongify(self); 51 | ignoreNextValue = YES; 52 | [self setObject:value forKey:key]; 53 | }]; 54 | 55 | return channel.followingTerminal; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /ReactiveObjCTests/UIImagePickerControllerRACSupportSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImagePickerControllerRACSupportSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Timur Kuchkarov on 17.04.14. 6 | // Copyright (c) 2014 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "UIImagePickerController+RACSignalSupport.h" 13 | #import "RACSignal.h" 14 | 15 | QuickSpecBegin(UIImagePickerControllerRACSupportSpec) 16 | 17 | qck_describe(@"UIImagePickerController", ^{ 18 | __block UIImagePickerController *imagePicker; 19 | 20 | qck_beforeEach(^{ 21 | imagePicker = [[UIImagePickerController alloc] init]; 22 | expect(imagePicker).notTo(beNil()); 23 | }); 24 | 25 | qck_it(@"sends the user info dictionary after confirmation", ^{ 26 | __block NSDictionary *selectedImageUserInfo = nil; 27 | [imagePicker.rac_imageSelectedSignal subscribeNext:^(NSDictionary *userInfo) { 28 | selectedImageUserInfo = userInfo; 29 | }]; 30 | 31 | NSDictionary *info = @{ 32 | UIImagePickerControllerMediaType: @"public.image", 33 | UIImagePickerControllerMediaMetadata: @{} 34 | }; 35 | [imagePicker.delegate imagePickerController:imagePicker didFinishPickingMediaWithInfo:info]; 36 | expect(selectedImageUserInfo).to(equal(info)); 37 | }); 38 | 39 | qck_it(@"cancels image picking process", ^{ 40 | __block BOOL didSend = NO; 41 | __block BOOL didComplete = NO; 42 | [imagePicker.rac_imageSelectedSignal subscribeNext:^(NSDictionary *userInfo) { 43 | didSend = YES; 44 | } completed:^{ 45 | didComplete = YES; 46 | }]; 47 | 48 | [imagePicker.delegate imagePickerControllerDidCancel:imagePicker]; 49 | expect(@(didSend)).to(beFalsy()); 50 | expect(@(didComplete)).to(beTruthy()); 51 | }); 52 | }); 53 | 54 | QuickSpecEnd 55 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDynamicSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACDynamicSignal.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACDynamicSignal.h" 10 | #import 11 | #import "RACCompoundDisposable.h" 12 | #import "RACPassthroughSubscriber.h" 13 | #import "RACScheduler+Private.h" 14 | #import "RACSubscriber.h" 15 | #import 16 | 17 | @interface RACDynamicSignal () 18 | 19 | // The block to invoke for each subscriber. 20 | @property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id subscriber); 21 | 22 | @end 23 | 24 | @implementation RACDynamicSignal 25 | 26 | #pragma mark Lifecycle 27 | 28 | + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe { 29 | RACDynamicSignal *signal = [[self alloc] init]; 30 | signal->_didSubscribe = [didSubscribe copy]; 31 | return [signal setNameWithFormat:@"+createSignal:"]; 32 | } 33 | 34 | #pragma mark Managing Subscribers 35 | 36 | - (RACDisposable *)subscribe:(id)subscriber { 37 | NSCParameterAssert(subscriber != nil); 38 | 39 | RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; 40 | subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable]; 41 | 42 | if (self.didSubscribe != NULL) { 43 | RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ 44 | RACDisposable *innerDisposable = self.didSubscribe(subscriber); 45 | [disposable addDisposable:innerDisposable]; 46 | }]; 47 | 48 | [disposable addDisposable:schedulingDisposable]; 49 | } 50 | 51 | return disposable; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSerialDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSerialDisposable.h 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-07-22. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACDisposable.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A disposable that contains exactly one other disposable and allows it to be 14 | /// swapped out atomically. 15 | @interface RACSerialDisposable : RACDisposable 16 | 17 | /// The inner disposable managed by the serial disposable. 18 | /// 19 | /// This property is thread-safe for reading and writing. However, if you want to 20 | /// read the current value _and_ write a new one atomically, use 21 | /// -swapInDisposable: instead. 22 | /// 23 | /// Disposing of the receiver will also dispose of the current disposable set for 24 | /// this property, then set the property to nil. If any new disposable is set 25 | /// after the receiver is disposed, it will be disposed immediately and this 26 | /// property will remain set to nil. 27 | @property (atomic, strong, nullable) RACDisposable *disposable; 28 | 29 | /// Creates a serial disposable which will wrap the given disposable. 30 | /// 31 | /// disposable - The value to set for `disposable`. This may be nil. 32 | /// 33 | /// Returns a RACSerialDisposable. 34 | + (instancetype)serialDisposableWithDisposable:(nullable RACDisposable *)disposable; 35 | 36 | /// Atomically swaps the receiver's `disposable` for `newDisposable`. 37 | /// 38 | /// newDisposable - The new value for `disposable`. If the receiver has already 39 | /// been disposed, this disposable will be too, and `disposable` 40 | /// will remain set to nil. This argument may be nil. 41 | /// 42 | /// Returns the previous value for the `disposable` property. 43 | - (nullable RACDisposable *)swapInDisposable:(nullable RACDisposable *)newDisposable; 44 | 45 | @end 46 | 47 | NS_ASSUME_NONNULL_END 48 | -------------------------------------------------------------------------------- /ReactiveObjC/UIImagePickerController+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImagePickerController+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Timur Kuchkarov on 28.03.14. 6 | // Copyright (c) 2014 GitHub. All rights reserved. 7 | // 8 | 9 | #import "UIImagePickerController+RACSignalSupport.h" 10 | #import "RACDelegateProxy.h" 11 | #import "RACSignal+Operations.h" 12 | #import "NSObject+RACDeallocating.h" 13 | #import "NSObject+RACDescription.h" 14 | #import 15 | 16 | @implementation UIImagePickerController (RACSignalSupport) 17 | 18 | static void RACUseDelegateProxy(UIImagePickerController *self) { 19 | if (self.delegate == self.rac_delegateProxy) return; 20 | 21 | self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; 22 | self.delegate = (id)self.rac_delegateProxy; 23 | } 24 | 25 | - (RACDelegateProxy *)rac_delegateProxy { 26 | RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); 27 | if (proxy == nil) { 28 | proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIImagePickerControllerDelegate)]; 29 | objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 30 | } 31 | 32 | return proxy; 33 | } 34 | 35 | - (RACSignal *)rac_imageSelectedSignal { 36 | RACSignal *pickerCancelledSignal = [[self.rac_delegateProxy 37 | signalForSelector:@selector(imagePickerControllerDidCancel:)] 38 | merge:self.rac_willDeallocSignal]; 39 | 40 | RACSignal *imagePickerSignal = [[[[self.rac_delegateProxy 41 | signalForSelector:@selector(imagePickerController:didFinishPickingMediaWithInfo:)] 42 | reduceEach:^(UIImagePickerController *pickerController, NSDictionary *userInfo) { 43 | return userInfo; 44 | }] 45 | takeUntil:pickerCancelledSignal] 46 | setNameWithFormat:@"%@ -rac_imageSelectedSignal", RACDescription(self)]; 47 | 48 | RACUseDelegateProxy(self); 49 | 50 | return imagePickerSignal; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /ReactiveObjCTests/NSStringRACKeyPathUtilitiesSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSStringRACKeyPathUtilitiesSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 05/05/2013. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "NSString+RACKeyPathUtilities.h" 13 | 14 | QuickSpecBegin(NSStringRACKeyPathUtilitiesSpec) 15 | 16 | qck_describe(@"-keyPathComponents", ^{ 17 | qck_it(@"should return components in the key path", ^{ 18 | expect(@"self.test.key.path".rac_keyPathComponents).to(equal((@[@"self", @"test", @"key", @"path"]))); 19 | }); 20 | 21 | qck_it(@"should return nil if given an empty string", ^{ 22 | expect(@"".rac_keyPathComponents).to(beNil()); 23 | }); 24 | }); 25 | 26 | qck_describe(@"-keyPathByDeletingLastKeyPathComponent", ^{ 27 | qck_it(@"should return the parent key path", ^{ 28 | expect(@"grandparent.parent.child".rac_keyPathByDeletingLastKeyPathComponent).to(equal(@"grandparent.parent")); 29 | }); 30 | 31 | qck_it(@"should return nil if given an empty string", ^{ 32 | expect(@"".rac_keyPathByDeletingLastKeyPathComponent).to(beNil()); 33 | }); 34 | 35 | qck_it(@"should return nil if given a key path with only one component", ^{ 36 | expect(@"self".rac_keyPathByDeletingLastKeyPathComponent).to(beNil()); 37 | }); 38 | }); 39 | 40 | qck_describe(@"-keyPathByDeletingFirstKeyPathComponent", ^{ 41 | qck_it(@"should return the remaining key path", ^{ 42 | expect(@"first.second.third".rac_keyPathByDeletingFirstKeyPathComponent).to(equal(@"second.third")); 43 | }); 44 | 45 | qck_it(@"should return nil if given an empty string", ^{ 46 | expect(@"".rac_keyPathByDeletingFirstKeyPathComponent).to(beNil()); 47 | }); 48 | 49 | qck_it(@"should return nil if given a key path with only one component", ^{ 50 | expect(@"self".rac_keyPathByDeletingFirstKeyPathComponent).to(beNil()); 51 | }); 52 | }); 53 | 54 | QuickSpecEnd 55 | -------------------------------------------------------------------------------- /ReactiveObjC/UIBarButtonItem+RACCommandSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+RACCommandSupport.m 3 | // ReactiveObjC 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 "RACCommand.h" 12 | #import "RACDisposable.h" 13 | #import "RACSignal+Operations.h" 14 | #import 15 | 16 | static void *UIControlRACCommandKey = &UIControlRACCommandKey; 17 | static void *UIControlEnabledDisposableKey = &UIControlEnabledDisposableKey; 18 | 19 | @implementation UIBarButtonItem (RACCommandSupport) 20 | 21 | - (RACCommand *)rac_command { 22 | return objc_getAssociatedObject(self, UIControlRACCommandKey); 23 | } 24 | 25 | - (void)setRac_command:(RACCommand *)command { 26 | objc_setAssociatedObject(self, UIControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | 28 | // Check for stored signal in order to remove it and add a new one 29 | RACDisposable *disposable = objc_getAssociatedObject(self, UIControlEnabledDisposableKey); 30 | [disposable dispose]; 31 | 32 | if (command == nil) return; 33 | 34 | disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self]; 35 | objc_setAssociatedObject(self, UIControlEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 36 | 37 | [self rac_hijackActionAndTargetIfNeeded]; 38 | } 39 | 40 | - (void)rac_hijackActionAndTargetIfNeeded { 41 | SEL hijackSelector = @selector(rac_commandPerformAction:); 42 | if (self.target == self && self.action == hijackSelector) return; 43 | 44 | if (self.target != nil) NSLog(@"WARNING: UIBarButtonItem.rac_command hijacks the control's existing target and action."); 45 | 46 | self.target = self; 47 | self.action = hijackSelector; 48 | } 49 | 50 | - (void)rac_commandPerformAction:(id)sender { 51 | [self.rac_command execute:sender]; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ReactiveObjC/UIButton+RACCommandSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+RACCommandSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Ash Furrow on 2013-06-06. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIButton+RACCommandSupport.h" 10 | #import 11 | #import "RACCommand.h" 12 | #import "RACDisposable.h" 13 | #import "RACSignal+Operations.h" 14 | #import 15 | 16 | static void *UIButtonRACCommandKey = &UIButtonRACCommandKey; 17 | static void *UIButtonEnabledDisposableKey = &UIButtonEnabledDisposableKey; 18 | 19 | @implementation UIButton (RACCommandSupport) 20 | 21 | - (RACCommand *)rac_command { 22 | return objc_getAssociatedObject(self, UIButtonRACCommandKey); 23 | } 24 | 25 | - (void)setRac_command:(RACCommand *)command { 26 | objc_setAssociatedObject(self, UIButtonRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 27 | 28 | // Check for stored signal in order to remove it and add a new one 29 | RACDisposable *disposable = objc_getAssociatedObject(self, UIButtonEnabledDisposableKey); 30 | [disposable dispose]; 31 | 32 | if (command == nil) return; 33 | 34 | disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self]; 35 | objc_setAssociatedObject(self, UIButtonEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 36 | 37 | [self rac_hijackActionAndTargetIfNeeded]; 38 | } 39 | 40 | - (void)rac_hijackActionAndTargetIfNeeded { 41 | SEL hijackSelector = @selector(rac_commandPerformAction:); 42 | 43 | for (NSString *selector in [self actionsForTarget:self forControlEvent:UIControlEventTouchUpInside]) { 44 | if (hijackSelector == NSSelectorFromString(selector)) { 45 | return; 46 | } 47 | } 48 | 49 | [self addTarget:self action:hijackSelector forControlEvents:UIControlEventTouchUpInside]; 50 | } 51 | 52 | - (void)rac_commandPerformAction:(id)sender { 53 | [self.rac_command execute:sender]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSubscriber.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACSubscriber.h 3 | // ReactiveObjC 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 RACCompoundDisposable; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /// Represents any object which can directly receive values from a RACSignal. 16 | /// 17 | /// You generally shouldn't need to implement this protocol. +[RACSignal 18 | /// createSignal:], RACSignal's subscription methods, or RACSubject should work 19 | /// for most uses. 20 | /// 21 | /// Implementors of this protocol may receive messages and values from multiple 22 | /// threads simultaneously, and so should be thread-safe. Subscribers will also 23 | /// be weakly referenced so implementations must allow that. 24 | @protocol RACSubscriber 25 | @required 26 | 27 | /// Sends the next value to subscribers. 28 | /// 29 | /// value - The value to send. This can be `nil`. 30 | - (void)sendNext:(nullable id)value; 31 | 32 | /// Sends the error to subscribers. 33 | /// 34 | /// error - The error to send. This can be `nil`. 35 | /// 36 | /// This terminates the subscription, and invalidates the subscriber (such that 37 | /// it cannot subscribe to anything else in the future). 38 | - (void)sendError:(nullable NSError *)error; 39 | 40 | /// Sends completed to subscribers. 41 | /// 42 | /// This terminates the subscription, and invalidates the subscriber (such that 43 | /// it cannot subscribe to anything else in the future). 44 | - (void)sendCompleted; 45 | 46 | /// Sends the subscriber a disposable that represents one of its subscriptions. 47 | /// 48 | /// A subscriber may receive multiple disposables if it gets subscribed to 49 | /// multiple signals; however, any error or completed events must terminate _all_ 50 | /// subscriptions. 51 | - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable; 52 | 53 | @end 54 | 55 | NS_ASSUME_NONNULL_END 56 | -------------------------------------------------------------------------------- /ReactiveObjC/RACKVOProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACKVOProxy.m 3 | // ReactiveObjC 4 | // 5 | // Created by Richard Speyer on 4/10/14. 6 | // Copyright (c) 2014 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACKVOProxy.h" 10 | 11 | @interface RACKVOProxy() 12 | 13 | @property (strong, nonatomic, readonly) NSMapTable *trampolines; 14 | @property (strong, nonatomic, readonly) dispatch_queue_t queue; 15 | 16 | @end 17 | 18 | @implementation RACKVOProxy 19 | 20 | + (instancetype)sharedProxy { 21 | static RACKVOProxy *proxy; 22 | static dispatch_once_t onceToken; 23 | 24 | dispatch_once(&onceToken, ^{ 25 | proxy = [[self alloc] init]; 26 | }); 27 | 28 | return proxy; 29 | } 30 | 31 | - (instancetype)init { 32 | self = [super init]; 33 | 34 | _queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxy", DISPATCH_QUEUE_SERIAL); 35 | _trampolines = [NSMapTable strongToWeakObjectsMapTable]; 36 | 37 | return self; 38 | } 39 | 40 | - (void)addObserver:(__weak NSObject *)observer forContext:(void *)context { 41 | NSValue *valueContext = [NSValue valueWithPointer:context]; 42 | 43 | dispatch_sync(self.queue, ^{ 44 | [self.trampolines setObject:observer forKey:valueContext]; 45 | }); 46 | } 47 | 48 | - (void)removeObserver:(NSObject *)observer forContext:(void *)context { 49 | NSValue *valueContext = [NSValue valueWithPointer:context]; 50 | 51 | dispatch_sync(self.queue, ^{ 52 | [self.trampolines removeObjectForKey:valueContext]; 53 | }); 54 | } 55 | 56 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 57 | NSValue *valueContext = [NSValue valueWithPointer:context]; 58 | __block NSObject *trueObserver; 59 | 60 | dispatch_sync(self.queue, ^{ 61 | trueObserver = [self.trampolines objectForKey:valueContext]; 62 | }); 63 | 64 | if (trueObserver != nil) { 65 | [trueObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 66 | } 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACEvent.h 3 | // ReactiveObjC 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 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// Describes the type of a RACEvent. 14 | /// 15 | /// RACEventTypeCompleted - A `completed` event. 16 | /// RACEventTypeError - An `error` event. 17 | /// RACEventTypeNext - A `next` event. 18 | typedef NS_ENUM(NSUInteger, RACEventType) { 19 | RACEventTypeCompleted, 20 | RACEventTypeError, 21 | RACEventTypeNext 22 | }; 23 | 24 | /// Represents an event sent by a RACSignal. 25 | /// 26 | /// This corresponds to the `Notification` class in Rx. 27 | @interface RACEvent<__covariant ValueType> : NSObject 28 | 29 | /// Returns a singleton RACEvent representing the `completed` event. 30 | + (RACEvent *)completedEvent; 31 | 32 | /// Returns a new event of type RACEventTypeError, containing the given error. 33 | + (RACEvent *)eventWithError:(nullable NSError *)error; 34 | 35 | /// Returns a new event of type RACEventTypeNext, containing the given value. 36 | + (RACEvent *)eventWithValue:(nullable ValueType)value; 37 | 38 | /// The type of event represented by the receiver. 39 | @property (nonatomic, assign, readonly) RACEventType eventType; 40 | 41 | /// Returns whether the receiver is of type RACEventTypeCompleted or 42 | /// RACEventTypeError. 43 | @property (nonatomic, getter = isFinished, assign, readonly) BOOL finished; 44 | 45 | /// The error associated with an event of type RACEventTypeError. This will be 46 | /// nil for all other event types. 47 | @property (nonatomic, strong, readonly, nullable) NSError *error; 48 | 49 | /// The value associated with an event of type RACEventTypeNext. This will be 50 | /// nil for all other event types. 51 | @property (nonatomic, strong, readonly, nullable) ValueType value; 52 | 53 | @end 54 | 55 | NS_ASSUME_NONNULL_END 56 | -------------------------------------------------------------------------------- /ReactiveObjC/RACCompoundDisposable.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACCompoundDisposable.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 11/30/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACDisposable.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// A disposable of disposables. When it is disposed, it disposes of all its 14 | /// contained disposables. 15 | /// 16 | /// If -addDisposable: is called after the compound disposable has been disposed 17 | /// of, the given disposable is immediately disposed. This allows a compound 18 | /// disposable to act as a stand-in for a disposable that will be delivered 19 | /// asynchronously. 20 | @interface RACCompoundDisposable : RACDisposable 21 | 22 | /// Creates and returns a new compound disposable. 23 | + (instancetype)compoundDisposable; 24 | 25 | /// Creates and returns a new compound disposable containing the given 26 | /// disposables. 27 | + (instancetype)compoundDisposableWithDisposables:(nullable NSArray *)disposables; 28 | 29 | /// Adds the given disposable. If the receiving disposable has already been 30 | /// disposed of, the given disposable is disposed immediately. 31 | /// 32 | /// This method is thread-safe. 33 | /// 34 | /// disposable - The disposable to add. This may be nil, in which case nothing 35 | /// happens. 36 | - (void)addDisposable:(nullable RACDisposable *)disposable; 37 | 38 | /// Removes the specified disposable from the compound disposable (regardless of 39 | /// its disposed status), or does nothing if it's not in the compound disposable. 40 | /// 41 | /// This is mainly useful for limiting the memory usage of the compound 42 | /// disposable for long-running operations. 43 | /// 44 | /// This method is thread-safe. 45 | /// 46 | /// disposable - The disposable to remove. This argument may be nil (to make the 47 | /// use of weak references easier). 48 | - (void)removeDisposable:(nullable RACDisposable *)disposable; 49 | 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /ReactiveObjC/RACStringSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACStringSequence.m 3 | // ReactiveObjC 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 | 11 | @interface RACStringSequence () 12 | 13 | // The string being sequenced. 14 | @property (nonatomic, copy, readonly) NSString *string; 15 | 16 | // The index in the string from which the sequence starts. 17 | @property (nonatomic, assign, readonly) NSUInteger offset; 18 | 19 | @end 20 | 21 | @implementation RACStringSequence 22 | 23 | #pragma mark Lifecycle 24 | 25 | + (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset { 26 | NSCParameterAssert(offset <= string.length); 27 | 28 | if (offset == string.length) return self.empty; 29 | 30 | RACStringSequence *seq = [[self alloc] init]; 31 | seq->_string = [string copy]; 32 | seq->_offset = offset; 33 | return seq; 34 | } 35 | 36 | #pragma mark RACSequence 37 | 38 | - (id)head { 39 | return [self.string substringWithRange:NSMakeRange(self.offset, 1)]; 40 | } 41 | 42 | - (RACSequence *)tail { 43 | RACSequence *sequence = [self.class sequenceWithString:self.string offset:self.offset + 1]; 44 | sequence.name = self.name; 45 | return sequence; 46 | } 47 | 48 | - (NSArray *)array { 49 | NSUInteger substringLength = self.string.length - self.offset; 50 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:substringLength]; 51 | 52 | [self.string enumerateSubstringsInRange:NSMakeRange(self.offset, substringLength) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { 53 | [array addObject:substring]; 54 | }]; 55 | 56 | return [array copy]; 57 | } 58 | 59 | #pragma mark NSObject 60 | 61 | - (NSString *)description { 62 | return [NSString stringWithFormat:@"<%@: %p>{ name = %@, string = %@ }", self.class, self, self.name, [self.string substringFromIndex:self.offset]]; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /ReactiveObjC/UIAlertView+RACSignalSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertView+RACSignalSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Henrik Hodne on 6/16/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIAlertView+RACSignalSupport.h" 10 | #import "RACDelegateProxy.h" 11 | #import "RACSignal+Operations.h" 12 | #import "NSObject+RACDeallocating.h" 13 | #import "NSObject+RACDescription.h" 14 | #import 15 | 16 | @implementation UIAlertView (RACSignalSupport) 17 | 18 | static void RACUseDelegateProxy(UIAlertView *self) { 19 | if (self.delegate == self.rac_delegateProxy) return; 20 | 21 | self.rac_delegateProxy.rac_proxiedDelegate = self.delegate; 22 | self.delegate = (id)self.rac_delegateProxy; 23 | } 24 | 25 | - (RACDelegateProxy *)rac_delegateProxy { 26 | RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd); 27 | if (proxy == nil) { 28 | proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UIAlertViewDelegate)]; 29 | objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 30 | } 31 | 32 | return proxy; 33 | } 34 | 35 | - (RACSignal *)rac_buttonClickedSignal { 36 | RACSignal *signal = [[[[self.rac_delegateProxy 37 | signalForSelector:@selector(alertView:clickedButtonAtIndex:)] 38 | reduceEach:^(UIAlertView *alertView, NSNumber *buttonIndex) { 39 | return buttonIndex; 40 | }] 41 | takeUntil:self.rac_willDeallocSignal] 42 | setNameWithFormat:@"%@ -rac_buttonClickedSignal", RACDescription(self)]; 43 | 44 | RACUseDelegateProxy(self); 45 | 46 | return signal; 47 | } 48 | 49 | - (RACSignal *)rac_willDismissSignal { 50 | RACSignal *signal = [[[[self.rac_delegateProxy 51 | signalForSelector:@selector(alertView:willDismissWithButtonIndex:)] 52 | reduceEach:^(UIAlertView *alertView, NSNumber *buttonIndex) { 53 | return buttonIndex; 54 | }] 55 | takeUntil:self.rac_willDeallocSignal] 56 | setNameWithFormat:@"%@ -rac_willDismissSignal", RACDescription(self)]; 57 | 58 | RACUseDelegateProxy(self); 59 | 60 | return signal; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /ReactiveObjC/NSControl+RACCommandSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSControl+RACCommandSupport.m 3 | // ReactiveObjC 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 | #import "RACScopedDisposable.h" 12 | #import "RACSignal+Operations.h" 13 | #import 14 | 15 | static void *NSControlRACCommandKey = &NSControlRACCommandKey; 16 | static void *NSControlEnabledDisposableKey = &NSControlEnabledDisposableKey; 17 | 18 | @implementation NSControl (RACCommandSupport) 19 | 20 | - (RACCommand *)rac_command { 21 | return objc_getAssociatedObject(self, NSControlRACCommandKey); 22 | } 23 | 24 | - (void)setRac_command:(RACCommand *)command { 25 | objc_setAssociatedObject(self, NSControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 26 | 27 | // Tear down any previous binding before setting up our new one, or else we 28 | // might get assertion failures. 29 | [objc_getAssociatedObject(self, NSControlEnabledDisposableKey) dispose]; 30 | objc_setAssociatedObject(self, NSControlEnabledDisposableKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 31 | 32 | if (command == nil) { 33 | self.enabled = YES; 34 | return; 35 | } 36 | 37 | [self rac_hijackActionAndTargetIfNeeded]; 38 | 39 | RACScopedDisposable *disposable = [[command.enabled setKeyPath:@"enabled" onObject:self] asScopedDisposable]; 40 | objc_setAssociatedObject(self, NSControlEnabledDisposableKey, disposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 41 | } 42 | 43 | - (void)rac_hijackActionAndTargetIfNeeded { 44 | SEL hijackSelector = @selector(rac_commandPerformAction:); 45 | if (self.target == self && self.action == hijackSelector) return; 46 | 47 | if (self.target != nil) NSLog(@"WARNING: NSControl.rac_command hijacks the control's existing target and action."); 48 | 49 | self.target = self; 50 | self.action = hijackSelector; 51 | } 52 | 53 | - (void)rac_commandPerformAction:(id)sender { 54 | [self.rac_command execute:sender]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ReactiveObjC/NSInvocation+RACTypeParsing.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSInvocation+RACTypeParsing.h 3 | // ReactiveObjC 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 | @class RACTuple; 12 | 13 | // A private category of methods to handle wrapping and unwrapping of values. 14 | @interface NSInvocation (RACTypeParsing) 15 | 16 | // Sets the argument for the invocation at the given index by unboxing the given 17 | // object based on the type signature of the argument. 18 | // 19 | // This does not support C arrays or unions. 20 | // 21 | // Note that calling this on a char * or const char * argument can cause all 22 | // arguments to be retained. 23 | // 24 | // object - The object to unbox and set as the argument. 25 | // index - The index of the argument to set. 26 | - (void)rac_setArgument:(id)object atIndex:(NSUInteger)index; 27 | 28 | // Gets the argument for the invocation at the given index based on the 29 | // invocation's method signature. The value is then wrapped in the appropriate 30 | // object type. 31 | // 32 | // This does not support C arrays or unions. 33 | // 34 | // index - The index of the argument to get. 35 | // 36 | // Returns the argument of the invocation, wrapped in an object. 37 | - (id)rac_argumentAtIndex:(NSUInteger)index; 38 | 39 | // Arguments tuple for the invocation. 40 | // 41 | // The arguments tuple excludes implicit variables `self` and `_cmd`. 42 | // 43 | // See -rac_argumentAtIndex: and -rac_setArgumentAtIndex: for further 44 | // description of the underlying behavior. 45 | @property (nonatomic, copy) RACTuple *rac_argumentsTuple; 46 | 47 | // Gets the return value from the invocation based on the invocation's method 48 | // signature. The value is then wrapped in the appropriate object type. 49 | // 50 | // This does not support C arrays or unions. 51 | // 52 | // Returns the return value of the invocation, wrapped in an object. Voids are 53 | // returned as `RACUnit.defaultUnit`. 54 | - (id)rac_returnValue; 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /ReactiveObjC/RACSignalSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACSignalSequence.m 3 | // ReactiveObjC 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 | -------------------------------------------------------------------------------- /ReactiveObjC/NSURLConnection+RACSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLConnection+RACSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-01. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "NSURLConnection+RACSupport.h" 10 | #import "RACDisposable.h" 11 | #import "RACSignal.h" 12 | #import "RACSubscriber.h" 13 | #import "RACTuple.h" 14 | 15 | @implementation NSURLConnection (RACSupport) 16 | 17 | + (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request { 18 | NSCParameterAssert(request != nil); 19 | 20 | return [[RACSignal 21 | createSignal:^(id subscriber) { 22 | NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 23 | queue.name = @"org.reactivecocoa.ReactiveObjC.NSURLConnectionRACSupport"; 24 | #pragma clang diagnostic push 25 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 26 | [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { 27 | // The docs say that `nil` data means an error occurred, but 28 | // `nil` responses can also occur in practice (circumstances 29 | // unknown). Consider either to be an error. 30 | // 31 | // Note that _empty_ data is not necessarily erroneous, as there 32 | // may be headers but no HTTP body. 33 | if (response == nil || data == nil) { 34 | [subscriber sendError:error]; 35 | } else { 36 | [subscriber sendNext:RACTuplePack(response, data)]; 37 | [subscriber sendCompleted]; 38 | } 39 | }]; 40 | #pragma clang diagnostic pop 41 | 42 | return [RACDisposable disposableWithBlock:^{ 43 | // It's not clear if this will actually cancel the connection, 44 | // but we can at least prevent _some_ unnecessary work -- 45 | // without writing all the code for a proper delegate, which 46 | // doesn't really belong in RAC. 47 | queue.suspended = YES; 48 | [queue cancelAllOperations]; 49 | }]; 50 | }] 51 | setNameWithFormat:@"+rac_sendAsynchronousRequest: %@", request]; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ReactiveObjC/RACEagerSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACEagerSequence.m 3 | // ReactiveObjC 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 | + (RACSequence *)return:(id)value { 18 | return [[self sequenceWithArray:@[ value ] offset:0] setNameWithFormat:@"+return: %@", RACDescription(value)]; 19 | } 20 | 21 | - (RACSequence *)bind:(RACSequenceBindBlock (^)(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 | - (RACSequence *)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 reduce:(id (^)(id, RACSequence *rest))reduce { 61 | return [super foldRightWithStart:start reduce:^(id first, RACSequence *rest) { 62 | return reduce(first, rest.eagerSequence); 63 | }]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /ReactiveObjC/UIAlertView+RACSignalSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertView+RACSignalSupport.h 3 | // ReactiveObjC 4 | // 5 | // Created by Henrik Hodne on 6/16/13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACDelegateProxy; 12 | @class RACSignal<__covariant ValueType>; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface UIAlertView (RACSignalSupport) 17 | 18 | /// A delegate proxy which will be set as the receiver's delegate when any of the 19 | /// methods in this category are used. 20 | @property (nonatomic, strong, readonly) RACDelegateProxy *rac_delegateProxy; 21 | 22 | /// Creates a signal for button clicks on the receiver. 23 | /// 24 | /// When this method is invoked, the `rac_delegateProxy` will become the 25 | /// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy 26 | /// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't 27 | /// know how to handle. Setting the receiver's `delegate` afterward is considered 28 | /// undefined behavior. 29 | /// 30 | /// Note that this signal will not send a value when the alert is dismissed 31 | /// programatically. 32 | /// 33 | /// Returns a signal which will send the index of the specific button clicked. 34 | /// The signal will complete itself when the receiver is deallocated. 35 | - (RACSignal *)rac_buttonClickedSignal; 36 | 37 | /// Creates a signal for dismissal of the receiver. 38 | /// 39 | /// When this method is invoked, the `rac_delegateProxy` will become the 40 | /// receiver's delegate. Any previous delegate will become the -[RACDelegateProxy 41 | /// rac_proxiedDelegate], so that it receives any messages that the proxy doesn't 42 | /// know how to handle. Setting the receiver's `delegate` afterward is considered 43 | /// undefined behavior. 44 | /// 45 | /// Returns a signal which will send the index of the button associated with the 46 | /// dismissal. The signal will complete itself when the receiver is deallocated. 47 | - (RACSignal *)rac_willDismissSignal; 48 | 49 | @end 50 | 51 | NS_ASSUME_NONNULL_END 52 | -------------------------------------------------------------------------------- /ReactiveObjC/NSObject+RACKVOWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACKVOWrapper.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 10/11/11. 6 | // Copyright (c) 2011 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACDisposable; 12 | @class RACKVOTrampoline; 13 | 14 | // A private category providing a block based interface to KVO. 15 | @interface NSObject (RACKVOWrapper) 16 | 17 | // Adds the given block as the callbacks for when the key path changes. 18 | // 19 | // Unlike direct KVO observation, this handles deallocation of `weak` properties 20 | // by generating an appropriate notification. This will only occur if there is 21 | // an `@property` declaration visible in the observed class, with the `weak` 22 | // memory management attribute. 23 | // 24 | // The observation does not need to be explicitly removed. It will be removed 25 | // when the observer or the receiver deallocate. 26 | // 27 | // keyPath - The key path to observe. Must not be nil. 28 | // options - The KVO observation options. 29 | // observer - The object that requested the observation. May be nil. 30 | // block - The block called when the value at the key path changes. It is 31 | // passed the current value of the key path and the extended KVO 32 | // change dictionary including RAC-specific keys and values. Must not 33 | // be nil. 34 | // 35 | // Returns a disposable that can be used to stop the observation. 36 | - (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)observer block:(void (^)(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent))block; 37 | 38 | @end 39 | 40 | typedef void (^RACKVOBlock)(id target, id observer, NSDictionary *change); 41 | 42 | @interface NSObject (RACUnavailableKVOWrapper) 43 | 44 | - (RACKVOTrampoline *)rac_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block __attribute((unavailable("Use rac_observeKeyPath:options:observer:block: instead."))); 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /ReactiveObjC/NSObject+RACLifting.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+RACLifting.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 10/13/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal<__covariant ValueType>; 12 | @class RACTuple; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface NSObject (RACLifting) 17 | 18 | /// Lifts the selector on the receiver into the reactive world. The selector will 19 | /// be invoked whenever any signal argument sends a value, but only after each 20 | /// signal has sent an initial value. 21 | /// 22 | /// It will replay the most recently sent value to new subscribers. 23 | /// 24 | /// This does not support C arrays or unions. 25 | /// 26 | /// selector - The selector on self to invoke. 27 | /// firstSignal - The signal corresponding to the first method argument. This 28 | /// must not be nil. 29 | /// ... - A list of RACSignals corresponding to the remaining arguments. 30 | /// There must be a non-nil signal for each method argument. 31 | /// 32 | /// Examples 33 | /// 34 | /// [button rac_liftSelector:@selector(setTitleColor:forState:) withSignals:textColorSignal, [RACSignal return:@(UIControlStateNormal)], nil]; 35 | /// 36 | /// Returns a signal which sends the return value from each invocation of the 37 | /// selector. If the selector returns void, it instead sends RACUnit.defaultUnit. 38 | /// It completes only after all the signal arguments complete. 39 | - (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... NS_REQUIRES_NIL_TERMINATION; 40 | 41 | /// Like -rac_liftSelector:withSignals:, but accepts an array instead of 42 | /// a variadic list of arguments. 43 | - (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals; 44 | 45 | /// Like -rac_liftSelector:withSignals:, but accepts a signal sending tuples of 46 | /// arguments instead of a variadic list of arguments. 47 | - (RACSignal *)rac_liftSelector:(SEL)selector withSignalOfArguments:(RACSignal *)arguments; 48 | 49 | @end 50 | 51 | NS_ASSUME_NONNULL_END 52 | -------------------------------------------------------------------------------- /ReactiveObjC/RACMulticastConnection.h: -------------------------------------------------------------------------------- 1 | // 2 | // RACMulticastConnection.h 3 | // ReactiveObjC 4 | // 5 | // Created by Josh Abernathy on 4/11/12. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RACAnnotations.h" 11 | 12 | @class RACDisposable; 13 | @class RACSignal<__covariant ValueType>; 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /// A multicast connection encapsulates the idea of sharing one subscription to a 18 | /// signal to many subscribers. This is most often needed if the subscription to 19 | /// the underlying signal involves side-effects or shouldn't be called more than 20 | /// once. 21 | /// 22 | /// The multicasted signal is only subscribed to when 23 | /// -[RACMulticastConnection connect] is called. Until that happens, no values 24 | /// will be sent on `signal`. See -[RACMulticastConnection autoconnect] for how 25 | /// -[RACMulticastConnection connect] can be called automatically. 26 | /// 27 | /// Note that you shouldn't create RACMulticastConnection manually. Instead use 28 | /// -[RACSignal publish] or -[RACSignal multicast:]. 29 | @interface RACMulticastConnection<__covariant ValueType> : NSObject 30 | 31 | /// The multicasted signal. 32 | @property (nonatomic, strong, readonly) RACSignal *signal; 33 | 34 | /// Connect to the underlying signal by subscribing to it. Calling this multiple 35 | /// times does nothing but return the existing connection's disposable. 36 | /// 37 | /// Returns the disposable for the subscription to the multicasted signal. 38 | - (RACDisposable *)connect; 39 | 40 | /// Connects to the underlying signal when the returned signal is first 41 | /// subscribed to, and disposes of the subscription to the multicasted signal 42 | /// when the returned signal has no subscribers. 43 | /// 44 | /// If new subscribers show up after being disposed, they'll subscribe and then 45 | /// be immediately disposed of. The returned signal will never re-connect to the 46 | /// multicasted signal. 47 | /// 48 | /// Returns the autoconnecting signal. 49 | - (RACSignal *)autoconnect RAC_WARN_UNUSED_RESULT; 50 | 51 | @end 52 | 53 | NS_ASSUME_NONNULL_END 54 | -------------------------------------------------------------------------------- /ReactiveObjC/UIRefreshControl+RACCommandSupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIRefreshControl+RACCommandSupport.m 3 | // ReactiveObjC 4 | // 5 | // Created by Dave Lee on 2013-10-17. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIRefreshControl+RACCommandSupport.h" 10 | #import 11 | #import "RACCommand.h" 12 | #import "RACCompoundDisposable.h" 13 | #import "RACDisposable.h" 14 | #import "RACSignal.h" 15 | #import "RACSignal+Operations.h" 16 | #import "UIControl+RACSignalSupport.h" 17 | #import 18 | 19 | static void *UIRefreshControlRACCommandKey = &UIRefreshControlRACCommandKey; 20 | static void *UIRefreshControlDisposableKey = &UIRefreshControlDisposableKey; 21 | 22 | @implementation UIRefreshControl (RACCommandSupport) 23 | 24 | - (RACCommand *)rac_command { 25 | return objc_getAssociatedObject(self, UIRefreshControlRACCommandKey); 26 | } 27 | 28 | - (void)setRac_command:(RACCommand *)command { 29 | objc_setAssociatedObject(self, UIRefreshControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 30 | 31 | // Dispose of any active command associations. 32 | [objc_getAssociatedObject(self, UIRefreshControlDisposableKey) dispose]; 33 | 34 | if (command == nil) return; 35 | 36 | // Like RAC(self, enabled) = command.enabled; but with access to disposable. 37 | RACDisposable *enabledDisposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self]; 38 | 39 | RACDisposable *executionDisposable = [[[[[self 40 | rac_signalForControlEvents:UIControlEventValueChanged] 41 | map:^(UIRefreshControl *x) { 42 | return [[[command 43 | execute:x] 44 | catchTo:[RACSignal empty]] 45 | then:^{ 46 | return [RACSignal return:x]; 47 | }]; 48 | }] 49 | concat] 50 | deliverOnMainThread] 51 | subscribeNext:^(UIRefreshControl *x) { 52 | [x endRefreshing]; 53 | }]; 54 | 55 | RACDisposable *commandDisposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ enabledDisposable, executionDisposable ]]; 56 | objc_setAssociatedObject(self, UIRefreshControlDisposableKey, commandDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACDisposableSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACDisposableSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-06-13. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACDisposable.h" 13 | #import "RACScopedDisposable.h" 14 | 15 | QuickSpecBegin(RACDisposableSpec) 16 | 17 | qck_it(@"should initialize without a block", ^{ 18 | RACDisposable *disposable = [[RACDisposable alloc] init]; 19 | expect(disposable).notTo(beNil()); 20 | expect(@(disposable.disposed)).to(beFalsy()); 21 | 22 | [disposable dispose]; 23 | expect(@(disposable.disposed)).to(beTruthy()); 24 | }); 25 | 26 | qck_it(@"should execute a block upon disposal", ^{ 27 | __block BOOL disposed = NO; 28 | RACDisposable *disposable = [RACDisposable disposableWithBlock:^{ 29 | disposed = YES; 30 | }]; 31 | 32 | expect(disposable).notTo(beNil()); 33 | expect(@(disposed)).to(beFalsy()); 34 | expect(@(disposable.disposed)).to(beFalsy()); 35 | 36 | [disposable dispose]; 37 | expect(@(disposed)).to(beTruthy()); 38 | expect(@(disposable.disposed)).to(beTruthy()); 39 | }); 40 | 41 | qck_it(@"should not dispose upon deallocation", ^{ 42 | __block BOOL disposed = NO; 43 | __weak RACDisposable *weakDisposable = nil; 44 | 45 | @autoreleasepool { 46 | RACDisposable *disposable = [RACDisposable disposableWithBlock:^{ 47 | disposed = YES; 48 | }]; 49 | 50 | weakDisposable = disposable; 51 | expect(weakDisposable).notTo(beNil()); 52 | } 53 | 54 | expect(weakDisposable).to(beNil()); 55 | expect(@(disposed)).to(beFalsy()); 56 | }); 57 | 58 | qck_it(@"should create a scoped disposable", ^{ 59 | __block BOOL disposed = NO; 60 | __weak RACScopedDisposable *weakDisposable = nil; 61 | 62 | @autoreleasepool { 63 | RACScopedDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACScopedDisposable disposableWithBlock:^{ 64 | disposed = YES; 65 | }]; 66 | 67 | weakDisposable = disposable; 68 | expect(weakDisposable).notTo(beNil()); 69 | expect(@(disposed)).to(beFalsy()); 70 | } 71 | 72 | expect(weakDisposable).to(beNil()); 73 | expect(@(disposed)).to(beTruthy()); 74 | }); 75 | 76 | QuickSpecEnd 77 | -------------------------------------------------------------------------------- /ReactiveObjC/RACTupleSequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACTupleSequence.m 3 | // ReactiveObjC 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 | + (RACSequence *)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[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 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDisposable.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACDisposable.m 3 | // ReactiveObjC 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 | // a pointer to `self` if no logic should be performed upon disposal, or 16 | // NULL if the receiver is already disposed. 17 | // 18 | // This should only be used atomically. 19 | void * volatile _disposeBlock; 20 | } 21 | 22 | @end 23 | 24 | @implementation RACDisposable 25 | 26 | #pragma mark Properties 27 | 28 | - (BOOL)isDisposed { 29 | return _disposeBlock == NULL; 30 | } 31 | 32 | #pragma mark Lifecycle 33 | 34 | - (instancetype)init { 35 | self = [super init]; 36 | 37 | _disposeBlock = (__bridge void *)self; 38 | OSMemoryBarrier(); 39 | 40 | return self; 41 | } 42 | 43 | - (instancetype)initWithBlock:(void (^)(void))block { 44 | NSCParameterAssert(block != nil); 45 | 46 | self = [super init]; 47 | 48 | _disposeBlock = (void *)CFBridgingRetain([block copy]); 49 | OSMemoryBarrier(); 50 | 51 | return self; 52 | } 53 | 54 | + (instancetype)disposableWithBlock:(void (^)(void))block { 55 | return [(RACDisposable *)[self alloc] initWithBlock:block]; 56 | } 57 | 58 | - (void)dealloc { 59 | if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return; 60 | 61 | CFRelease(_disposeBlock); 62 | _disposeBlock = NULL; 63 | } 64 | 65 | #pragma mark Disposal 66 | 67 | - (void)dispose { 68 | void (^disposeBlock)(void) = NULL; 69 | 70 | while (YES) { 71 | void *blockPtr = _disposeBlock; 72 | if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) { 73 | if (blockPtr != (__bridge void *)self) { 74 | disposeBlock = CFBridgingRelease(blockPtr); 75 | } 76 | 77 | break; 78 | } 79 | } 80 | 81 | if (disposeBlock != nil) disposeBlock(); 82 | } 83 | 84 | #pragma mark Scoped Disposables 85 | 86 | - (RACScopedDisposable *)asScopedDisposable { 87 | return [RACScopedDisposable scopedDisposableWithDisposable:self]; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /ReactiveObjC/RACDelegateProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACDelegateProxy.m 3 | // ReactiveObjC 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 "NSObject+RACSelectorSignal.h" 11 | #import 12 | 13 | @interface RACDelegateProxy () { 14 | // Declared as an ivar to avoid method naming conflicts. 15 | Protocol *_protocol; 16 | } 17 | 18 | @end 19 | 20 | @implementation RACDelegateProxy 21 | 22 | #pragma mark Lifecycle 23 | 24 | - (instancetype)initWithProtocol:(Protocol *)protocol { 25 | NSCParameterAssert(protocol != NULL); 26 | 27 | self = [super init]; 28 | 29 | class_addProtocol(self.class, protocol); 30 | 31 | _protocol = protocol; 32 | 33 | return self; 34 | } 35 | 36 | #pragma mark API 37 | 38 | - (RACSignal *)signalForSelector:(SEL)selector { 39 | return [self rac_signalForSelector:selector fromProtocol:_protocol]; 40 | } 41 | 42 | #pragma mark NSObject 43 | 44 | - (BOOL)isProxy { 45 | return YES; 46 | } 47 | 48 | - (void)forwardInvocation:(NSInvocation *)invocation { 49 | [invocation invokeWithTarget:self.rac_proxiedDelegate]; 50 | } 51 | 52 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 53 | // Look for the selector as an optional instance method. 54 | struct objc_method_description methodDescription = protocol_getMethodDescription(_protocol, selector, NO, YES); 55 | 56 | if (methodDescription.name == NULL) { 57 | // Then fall back to looking for a required instance 58 | // method. 59 | methodDescription = protocol_getMethodDescription(_protocol, selector, YES, YES); 60 | if (methodDescription.name == NULL) return [super methodSignatureForSelector:selector]; 61 | } 62 | 63 | return [NSMethodSignature signatureWithObjCTypes:methodDescription.types]; 64 | } 65 | 66 | - (BOOL)respondsToSelector:(SEL)selector { 67 | // Add the delegate to the autorelease pool, so it doesn't get deallocated 68 | // between this method call and -forwardInvocation:. 69 | __autoreleasing id delegate = self.rac_proxiedDelegate; 70 | if ([delegate respondsToSelector:selector]) return YES; 71 | 72 | return [super respondsToSelector:selector]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /ReactiveObjC/RACReturnSignal.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACReturnSignal.m 3 | // ReactiveObjC 4 | // 5 | // Created by Justin Spahr-Summers on 2013-10-10. 6 | // Copyright (c) 2013 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | #import "RACReturnSignal.h" 10 | #import "RACScheduler+Private.h" 11 | #import "RACSubscriber.h" 12 | #import "RACUnit.h" 13 | 14 | @interface RACReturnSignal () 15 | 16 | // The value to send upon subscription. 17 | @property (nonatomic, strong, readonly) id value; 18 | 19 | @end 20 | 21 | @implementation RACReturnSignal 22 | 23 | #pragma mark Properties 24 | 25 | // Only allow this signal's name to be customized in DEBUG, since it's 26 | // potentially a singleton in release builds (see +return:). 27 | - (void)setName:(NSString *)name { 28 | #ifdef DEBUG 29 | [super setName:name]; 30 | #endif 31 | } 32 | 33 | - (NSString *)name { 34 | #ifdef DEBUG 35 | return super.name; 36 | #else 37 | return @"+return:"; 38 | #endif 39 | } 40 | 41 | #pragma mark Lifecycle 42 | 43 | + (RACSignal *)return:(id)value { 44 | #ifndef DEBUG 45 | // In release builds, use singletons for two very common cases. 46 | if (value == RACUnit.defaultUnit) { 47 | static RACReturnSignal *unitSingleton; 48 | static dispatch_once_t unitPred; 49 | 50 | dispatch_once(&unitPred, ^{ 51 | unitSingleton = [[self alloc] init]; 52 | unitSingleton->_value = RACUnit.defaultUnit; 53 | }); 54 | 55 | return unitSingleton; 56 | } else if (value == nil) { 57 | static RACReturnSignal *nilSingleton; 58 | static dispatch_once_t nilPred; 59 | 60 | dispatch_once(&nilPred, ^{ 61 | nilSingleton = [[self alloc] init]; 62 | nilSingleton->_value = nil; 63 | }); 64 | 65 | return nilSingleton; 66 | } 67 | #endif 68 | 69 | RACReturnSignal *signal = [[self alloc] init]; 70 | signal->_value = value; 71 | 72 | #ifdef DEBUG 73 | [signal setNameWithFormat:@"+return: %@", value]; 74 | #endif 75 | 76 | return signal; 77 | } 78 | 79 | #pragma mark Subscription 80 | 81 | - (RACDisposable *)subscribe:(id)subscriber { 82 | NSCParameterAssert(subscriber != nil); 83 | 84 | return [RACScheduler.subscriptionScheduler schedule:^{ 85 | [subscriber sendNext:self.value]; 86 | [subscriber sendCompleted]; 87 | }]; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /ReactiveObjC/RACUnarySequence.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACUnarySequence.m 3 | // ReactiveObjC 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 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 | + (RACUnarySequence *)return:(id)value { 29 | RACUnarySequence *sequence = [[self alloc] init]; 30 | sequence.head = value; 31 | return [sequence setNameWithFormat:@"+return: %@", RACDescription(value)]; 32 | } 33 | 34 | #pragma mark RACSequence 35 | 36 | - (RACSequence *)tail { 37 | return nil; 38 | } 39 | 40 | - (RACSequence *)bind:(RACSequenceBindBlock (^)(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 | - (instancetype)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]; 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 | -------------------------------------------------------------------------------- /ReactiveObjCTests/RACChannelSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RACChannelSpec.m 3 | // ReactiveObjC 4 | // 5 | // Created by Uri Baghin on 30/12/2012. 6 | // Copyright (c) 2012 GitHub, Inc. All rights reserved. 7 | // 8 | 9 | @import Quick; 10 | @import Nimble; 11 | 12 | #import "RACChannelExamples.h" 13 | 14 | #import "NSObject+RACDeallocating.h" 15 | #import "RACChannel.h" 16 | #import "RACCompoundDisposable.h" 17 | #import "RACDisposable.h" 18 | #import "RACSignal.h" 19 | 20 | QuickSpecBegin(RACChannelSpec) 21 | 22 | qck_describe(@"RACChannel", ^{ 23 | qck_itBehavesLike(RACChannelExamples, ^{ 24 | return @{ 25 | RACChannelExampleCreateBlock: [^{ 26 | return [[RACChannel alloc] init]; 27 | } copy] 28 | }; 29 | }); 30 | 31 | qck_describe(@"memory management", ^{ 32 | qck_it(@"should dealloc when its subscribers are disposed", ^{ 33 | RACDisposable *leadingDisposable = nil; 34 | RACDisposable *followingDisposable = nil; 35 | 36 | __block BOOL deallocated = NO; 37 | 38 | @autoreleasepool { 39 | RACChannel *channel __attribute__((objc_precise_lifetime)) = [[RACChannel alloc] init]; 40 | [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ 41 | deallocated = YES; 42 | }]]; 43 | 44 | leadingDisposable = [channel.leadingTerminal subscribeCompleted:^{}]; 45 | followingDisposable = [channel.followingTerminal subscribeCompleted:^{}]; 46 | } 47 | 48 | [leadingDisposable dispose]; 49 | [followingDisposable dispose]; 50 | expect(@(deallocated)).toEventually(beTruthy()); 51 | }); 52 | 53 | qck_it(@"should dealloc when its subscriptions are disposed", ^{ 54 | RACDisposable *leadingDisposable = nil; 55 | RACDisposable *followingDisposable = nil; 56 | 57 | __block BOOL deallocated = NO; 58 | 59 | @autoreleasepool { 60 | RACChannel *channel __attribute__((objc_precise_lifetime)) = [[RACChannel alloc] init]; 61 | [channel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ 62 | deallocated = YES; 63 | }]]; 64 | 65 | leadingDisposable = [[RACSignal never] subscribe:channel.leadingTerminal]; 66 | followingDisposable = [[RACSignal never] subscribe:channel.followingTerminal]; 67 | } 68 | 69 | [leadingDisposable dispose]; 70 | [followingDisposable dispose]; 71 | expect(@(deallocated)).toEventually(beTruthy()); 72 | }); 73 | }); 74 | }); 75 | 76 | QuickSpecEnd 77 | --------------------------------------------------------------------------------