├── .gitignore ├── Examples ├── Chain Examples.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Examples │ ├── Controls │ │ ├── CAEControlsExample.h │ │ ├── CAEControlsExample.m │ │ └── CAEControlsExample.xib │ ├── Scroll View │ │ ├── CAEScrollViewExample.h │ │ └── CAEScrollViewExample.m │ ├── Stopwatch │ │ ├── CAEStopwatchExample.h │ │ └── CAEStopwatchExample.m │ └── Throttled │ │ ├── CAEThrottledExample.h │ │ └── CAEThrottledExample.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist └── Sources │ ├── CAEAppDelegate.h │ ├── CAEAppDelegate.m │ ├── CAEExampleViewController.h │ ├── CAEExampleViewController.m │ ├── CAEListViewController.h │ ├── CAEListViewController.m │ ├── Prefix.pch │ └── main.m ├── LICENSE ├── Objective-Chain.podspec ├── Objective-Chain.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── OS X Library.xcscheme │ └── iOS Library.xcscheme ├── README.md ├── Sources ├── Foundation │ ├── Consumers │ │ ├── OCAConsumer.h │ │ ├── OCAInvoker.h │ │ ├── OCAInvoker.m │ │ ├── OCASubscriber.h │ │ ├── OCASubscriber.m │ │ ├── OCASwitch.h │ │ └── OCASwitch.m │ ├── Mediators │ │ ├── OCABridge.h │ │ ├── OCABridge.m │ │ ├── OCAContext.h │ │ ├── OCAContext.m │ │ ├── OCAFilter.h │ │ ├── OCAFilter.m │ │ ├── OCALowPassFilter.h │ │ ├── OCALowPassFilter.m │ │ ├── OCAMediator.h │ │ ├── OCAMediator.m │ │ ├── OCAThrottle.h │ │ └── OCAThrottle.m │ ├── OCAFoundation.h │ ├── OCAObject.h │ ├── OCAObject.m │ ├── Predicates │ │ ├── OCAPredicate.h │ │ └── OCAPredicate.m │ ├── Producers │ │ ├── OCACommand.h │ │ ├── OCACommand.m │ │ ├── OCAHub.h │ │ ├── OCAHub.m │ │ ├── OCAInterpolator.h │ │ ├── OCAInterpolator.m │ │ ├── OCANotificator.h │ │ ├── OCANotificator.m │ │ ├── OCAProducer+Subclass.h │ │ ├── OCAProducer.h │ │ ├── OCAProducer.m │ │ ├── OCAProperty.h │ │ ├── OCAProperty.m │ │ ├── OCATimer.h │ │ └── OCATimer.m │ ├── Transformers │ │ ├── OCAMath.h │ │ ├── OCAMath.m │ │ ├── OCATransformer+Base.h │ │ ├── OCATransformer+Base.m │ │ ├── OCATransformer+Collections.h │ │ ├── OCATransformer+Collections.m │ │ ├── OCATransformer+Core.h │ │ ├── OCATransformer+Core.m │ │ ├── OCATransformer+NSData.h │ │ ├── OCATransformer+NSData.m │ │ ├── OCATransformer+NSDate.h │ │ ├── OCATransformer+NSDate.m │ │ ├── OCATransformer+NSString.h │ │ ├── OCATransformer+NSString.m │ │ └── OCATransformer.h │ └── Utilities │ │ ├── Accessors │ │ ├── OCAAccessor.h │ │ ├── OCAAccessor.m │ │ ├── OCAIndexAccessor.h │ │ ├── OCAIndexAccessor.m │ │ ├── OCAKeyPathAccessor.h │ │ ├── OCAKeyPathAccessor.m │ │ ├── OCAStructureAccessor.h │ │ └── OCAStructureAccessor.m │ │ ├── NSArray+Ordinals.h │ │ ├── NSArray+Ordinals.m │ │ ├── NSValue+Boxing.h │ │ ├── NSValue+Boxing.m │ │ ├── OCADebug.h │ │ ├── OCADebug.m │ │ ├── OCADecomposer.h │ │ ├── OCADecomposer.m │ │ ├── OCAInvocationCatcher.h │ │ ├── OCAInvocationCatcher.m │ │ ├── OCAKeyValueChange.h │ │ ├── OCAKeyValueChange.m │ │ ├── OCAMutableArrayProxy.h │ │ ├── OCAMutableArrayProxy.m │ │ ├── OCAPlaceholderObject.h │ │ ├── OCAPlaceholderObject.m │ │ ├── OCAQueue.h │ │ ├── OCAQueue.m │ │ ├── OCASemaphore.h │ │ ├── OCASemaphore.m │ │ ├── OCASwizzling.h │ │ ├── OCASwizzling.m │ │ ├── OCAVariadic.h │ │ └── OCAVariadic.m ├── Geometry │ ├── OCAGeometry+Functions.h │ ├── OCAGeometry+Functions.m │ ├── OCAGeometry.h │ ├── Predicates │ │ ├── OCAPredicate+CATransform3D.h │ │ ├── OCAPredicate+CATransform3D.m │ │ ├── OCAPredicate+CGAffineTransform.h │ │ ├── OCAPredicate+CGAffineTransform.m │ │ ├── OCAPredicate+CGPoint.h │ │ ├── OCAPredicate+CGPoint.m │ │ ├── OCAPredicate+CGRect.h │ │ ├── OCAPredicate+CGRect.m │ │ ├── OCAPredicate+CGSize.h │ │ ├── OCAPredicate+CGSize.m │ │ ├── OCAPredicate+UIEdgeInsets.h │ │ └── OCAPredicate+UIEdgeInsets.m │ └── Transformers │ │ ├── OCATransformer+CATransform3D.h │ │ ├── OCATransformer+CATransform3D.m │ │ ├── OCATransformer+CGAffineTransform.h │ │ ├── OCATransformer+CGAffineTransform.m │ │ ├── OCATransformer+CGPoint.h │ │ ├── OCATransformer+CGPoint.m │ │ ├── OCATransformer+CGRect.h │ │ ├── OCATransformer+CGRect.m │ │ ├── OCATransformer+CGSize.h │ │ ├── OCATransformer+CGSize.m │ │ ├── OCATransformer+UIEdgeInsets.h │ │ └── OCATransformer+UIEdgeInsets.m ├── ObjectiveChain.h ├── README.md └── UIKit │ ├── Consumers │ ├── OCAUIKit+UIButton.h │ └── OCAUIKit+UIButton.m │ ├── Mediators │ ├── OCAContext+UIKit.h │ ├── OCAContext+UIKit.m │ ├── OCAFader.h │ └── OCAFader.m │ ├── OCAUIKit.h │ ├── Producers │ ├── OCATargetter.h │ ├── OCATargetter.m │ ├── UITextField+editedText.h │ ├── UITextField+editedText.m │ ├── UIView+Gestures.h │ ├── UIView+Gestures.m │ ├── UIView+tintColor.h │ └── UIView+tintColor.m │ ├── Transformers │ ├── OCATransformer+UIColor.h │ ├── OCATransformer+UIColor.m │ ├── OCATransformer+UIImage.h │ └── OCATransformer+UIImage.m │ └── Utilities │ ├── OCAErrorRecoveryAttempter.h │ └── OCAErrorRecoveryAttempter.m └── Tests ├── Info.plist ├── OCAAccessorTest.m ├── OCAConnectionTest.m ├── OCAFilterTest.m ├── OCAFoundationTest.m ├── OCAHubTest.m ├── OCAInterpolatorTest.m ├── OCAInvokerTest.m ├── OCAMathTest.m ├── OCAObjectTest.m ├── OCAPropertyTest.m ├── OCAQueueTest.m ├── OCASwitchTest.m ├── OCAThrottleTest.m ├── OCATransformerTest.m ├── OCAVariadicTest.m └── Prefix.pch /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /Examples/Chain Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Examples/Controls/CAEControlsExample.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEControlsExample.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEExampleViewController.h" 10 | 11 | @interface CAEControlsExample : CAEExampleViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/Examples/Scroll View/CAEScrollViewExample.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEScrollViewExample.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEExampleViewController.h" 10 | 11 | @interface CAEScrollViewExample : CAEExampleViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/Examples/Stopwatch/CAEStopwatchExample.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEStopwatchExample.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 17.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEExampleViewController.h" 10 | 11 | @interface CAEStopwatchExample : CAEExampleViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/Examples/Throttled/CAEThrottledExample.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEThrottledExample.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 10.3.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEExampleViewController.h" 10 | 11 | @interface CAEThrottledExample : CAEExampleViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/Examples/Throttled/CAEThrottledExample.m: -------------------------------------------------------------------------------- 1 | // 2 | // CAEThrottledExample.m 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 10.3.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEThrottledExample.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface CAEThrottledExample () 16 | 17 | 18 | 19 | @property (nonatomic, readwrite, assign) NSUInteger integer; 20 | @property (nonatomic, readwrite, strong) UILabel *label; 21 | @property (nonatomic, readwrite, strong) UISlider *slider; 22 | 23 | 24 | 25 | @end 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | @implementation CAEThrottledExample 37 | 38 | 39 | 40 | 41 | 42 | #pragma mark Example Info & Registration 43 | 44 | 45 | + (void)load { 46 | [self registerExample]; 47 | } 48 | 49 | 50 | + (NSString *)exampleTitle { 51 | return @"Throttled"; 52 | } 53 | 54 | 55 | + (NSString *)exampleSubtitle { 56 | return @"Limiting frequency of events"; 57 | } 58 | 59 | 60 | + (NSString *)exampleDescription { 61 | return @"This example uses Throttling mechanism to limit how often is value from Slider applied. It then animates the changed value by interpolating from previous to the current. Total of 2 chains"; 62 | } 63 | 64 | 65 | + (NSString *)exampleAuthor { 66 | return @"iMartin Kiss"; 67 | } 68 | 69 | 70 | + (NSDate *)exampleDate { 71 | return [self day:10 month:3 year:2014]; 72 | } 73 | 74 | 75 | 76 | 77 | 78 | #pragma mark Setup 79 | 80 | 81 | - (void)setupViews { 82 | [super setupViews]; 83 | 84 | self.label = ({ 85 | UILabel *label = [[UILabel alloc] init]; 86 | label.font = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:90]; 87 | label.backgroundColor = [UIColor clearColor]; 88 | label.frame = CGRectMake(20, 64+40, 280, label.font.pointSize); 89 | label.text = @"1000"; 90 | label.textAlignment = NSTextAlignmentCenter; 91 | 92 | [self.view addSubview:label]; 93 | label; 94 | }); 95 | 96 | self.slider = ({ 97 | UISlider *slider = [[UISlider alloc] init]; 98 | slider.frame = CGRectMake(20, CGRectGetMaxY(self.label.frame) + 60, 280, 44); 99 | slider.minimumValue = 0; 100 | slider.maximumValue = 100; 101 | 102 | [self.view addSubview:slider]; 103 | slider; 104 | }); 105 | 106 | } 107 | 108 | 109 | - (void)setupChains { 110 | [super setupChains]; 111 | OCAWeakify(self); 112 | 113 | __block __weak OCAInterpolator *interpolator = nil; // Weak, because it will deallocate automatically once finished. 114 | [[OCAProperty(self, integer, NSUInteger) produceChanges] 115 | subscribeForClass:[OCAKeyValueChangeSetting class] handler:^(OCAKeyValueChangeSetting *change) { 116 | OCAStrongify(self); 117 | 118 | [interpolator finishWithLastValue:YES]; // Will stop and display final value. 119 | 120 | NSUInteger previous = [change.previousValue unsignedIntegerValue]; 121 | NSUInteger latest = [change.latestValue unsignedIntegerValue]; 122 | interpolator = [OCAInterpolator interpolatorWithDuration:0.5 123 | frequency:30 124 | fromValue:previous 125 | toValue:latest]; 126 | 127 | [[interpolator transformValues: 128 | [OCATransformer stringWithNumberStyle:NSNumberFormatterDecimalStyle fractionDigits:0], 129 | nil] connectTo:OCAProperty(self, label.text, NSString)]; 130 | }]; 131 | 132 | [[[self.slider producerForValue] 133 | throttleContinuous:1] // Sends latest value every 1 second 134 | connectTo:OCAProperty(self, integer, NSUInteger)]; 135 | } 136 | 137 | 138 | 139 | 140 | 141 | @end 142 | 143 | 144 | -------------------------------------------------------------------------------- /Examples/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Obj-Chain 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.iMartinKiss.Objective-Chain.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Examples/Sources/CAEAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEAppDelegate.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | 11 | 12 | 13 | @interface CAEAppDelegate : UIResponder 14 | 15 | 16 | @property (strong, nonatomic) UIWindow *window; 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Examples/Sources/CAEAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CAEAppDelegate.m 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEAppDelegate.h" 10 | #import "CAEListViewController.h" 11 | 12 | 13 | 14 | 15 | 16 | @implementation CAEAppDelegate 17 | 18 | 19 | 20 | 21 | 22 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 23 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 24 | self.window.backgroundColor = [UIColor blackColor]; 25 | 26 | CAEListViewController *listViewController = [[CAEListViewController alloc] init]; 27 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:listViewController]; 28 | 29 | [self.window makeKeyAndVisible]; 30 | return YES; 31 | } 32 | 33 | 34 | 35 | 36 | 37 | @end 38 | 39 | 40 | -------------------------------------------------------------------------------- /Examples/Sources/CAEExampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEExampleViewController.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | #import "ObjectiveChain.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface CAEExampleViewController : UIViewController 17 | 18 | 19 | 20 | #pragma mark Registering Subclasses 21 | 22 | + (void)registerExample; 23 | 24 | + (NSArray *)allSubclasses; 25 | 26 | 27 | #pragma mark Getting Info about Examples 28 | 29 | + (NSString *)exampleTitle; 30 | + (NSString *)exampleSubtitle; 31 | + (NSString *)exampleDescription; 32 | + (NSString *)exampleAuthor; 33 | + (NSDate *)exampleDate; 34 | 35 | + (NSDate *)day:(NSUInteger)day month:(NSUInteger)month year:(NSUInteger)year; 36 | 37 | 38 | #pragma mark Setup the Example 39 | 40 | - (void)setupViews; 41 | - (void)setupChains; 42 | 43 | 44 | #pragma mark State Properties 45 | 46 | @property (nonatomic, readonly, assign) BOOL partiallyVisible; 47 | @property (nonatomic, readonly, assign) BOOL fullyVisible; 48 | 49 | 50 | 51 | @end 52 | 53 | 54 | -------------------------------------------------------------------------------- /Examples/Sources/CAEListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CAEListViewController.h 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | 11 | 12 | 13 | @interface CAEListViewController : UITableViewController 14 | 15 | @end 16 | 17 | 18 | -------------------------------------------------------------------------------- /Examples/Sources/CAEListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CAEListViewController.m 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEListViewController.h" 10 | #import "CAEExampleViewController.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface CAEListViewController () 17 | 18 | 19 | @property (nonatomic, readonly, strong) NSArray *exampleClasses; 20 | 21 | 22 | @end 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @implementation CAEListViewController 34 | 35 | 36 | 37 | 38 | 39 | #pragma mark Creating & Loading 40 | 41 | 42 | - (instancetype)initWithStyle:(UITableViewStyle)style { 43 | self = [super initWithStyle:UITableViewStyleGrouped]; 44 | if (self) { 45 | self.title = @"Objective-Chain Examples"; 46 | self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] init]; 47 | self.navigationItem.backBarButtonItem.title = @"Examples"; 48 | 49 | NSSortDescriptor *sortByDate = [NSSortDescriptor sortDescriptorWithKey:OCAKPUnsafe(exampleDate) ascending:YES]; 50 | self->_exampleClasses = [[CAEExampleViewController allSubclasses] sortedArrayUsingDescriptors:@[ sortByDate ]]; 51 | } 52 | return self; 53 | } 54 | 55 | 56 | - (void)viewDidLoad { 57 | [super viewDidLoad]; 58 | 59 | UILabel *footnote = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 100)]; 60 | footnote.textColor = [UIColor darkGrayColor]; 61 | footnote.numberOfLines = 0; 62 | footnote.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:[UIFont smallSystemFontSize]]; 63 | footnote.textAlignment = NSTextAlignmentCenter; 64 | footnote.text = 65 | @"github.com/iMartinKiss/Objective-Chain" @"\n" 66 | @"\n" 67 | @"Copyright © 2014 Martin Kiss" @"\n" 68 | @"Licensed under MIT License"; 69 | self.tableView.tableFooterView = footnote; 70 | } 71 | 72 | 73 | 74 | 75 | 76 | #pragma mark tTable View 77 | 78 | 79 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 80 | return self.exampleClasses.count; 81 | } 82 | 83 | 84 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 85 | static NSString *CAEListCellIdentifier = @"CAEListCellIdentifier"; 86 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CAEListCellIdentifier]; 87 | if ( ! cell) { 88 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CAEListCellIdentifier]; 89 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 90 | } 91 | 92 | Class exampleClass = [self.exampleClasses objectAtIndex:indexPath.row]; 93 | cell.textLabel.text = [exampleClass exampleTitle]; 94 | cell.detailTextLabel.text = [exampleClass exampleSubtitle]; 95 | 96 | return cell; 97 | } 98 | 99 | 100 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 101 | Class exampleClass = [self.exampleClasses objectAtIndex:indexPath.row]; 102 | CAEExampleViewController *example = [[exampleClass alloc] init]; 103 | [self.navigationController pushViewController:example animated:YES]; 104 | } 105 | 106 | 107 | 108 | 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /Examples/Sources/Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix.pch 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | -------------------------------------------------------------------------------- /Examples/Sources/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Chain Examples 4 | // 5 | // Created by Martin Kiss on 14.1.14. 6 | // Copyright (c) 2014 iMartin Kiss. All rights reserved. 7 | // 8 | 9 | #import "CAEAppDelegate.h" 10 | 11 | 12 | 13 | int main(int argc, char * argv[]) { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([CAEAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Martin Kiss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Objective-Chain.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "Objective-Chain" 4 | s.version = "0.2.0" 5 | s.summary = "Object-oriented reactive framework, inspired by ReactiveCocoa" 6 | s.homepage = "https://github.com/iMartinKiss/Objective-Chain" 7 | s.license = { :type => "MIT", :file => "LICENSE" } 8 | 9 | s.author = { "Martin Kiss" => "martin.kiss@me.com" } 10 | 11 | s.source = { :git => "https://github.com/iMartinKiss/Objective-Chain.git", :tag => "#{s.version}" } 12 | 13 | s.framework = "Foundation" 14 | s.framework = "UIKit" 15 | 16 | s.requires_arc = true 17 | 18 | s.source_files = "Sources/**/*.{h,m}" 19 | 20 | end 21 | -------------------------------------------------------------------------------- /Objective-Chain.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Objective-Chain.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 17 | 19 | 20 | 21 | 22 | 23 | 37 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Objective-Chain.xcodeproj/xcshareddata/xcschemes/OS X Library.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Objective-Chain.xcodeproj/xcshareddata/xcschemes/iOS Library.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Sources/Foundation/Consumers/OCAConsumer.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAConsumer.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | 14 | 15 | /*! Consumer is abstract receiver of values that are send from Producers. 16 | * Consumers are retained by all Producers from whose they receive values . 17 | */ 18 | @protocol OCAConsumer 19 | 20 | 21 | 22 | @required 23 | 24 | //! Return a class of values that your instance consumes. Retunr nil or NSObject for any values. 25 | - (Class)consumedValueClass; 26 | 27 | //! Called after Producer sends new value. Do whatever your implementation requires. Your Consumer can receive from multiple Producers and you don't have a reference to them unless you create it. 28 | - (void)consumeValue:(id)value; 29 | 30 | //! Called after Producer finishes sending values. Your Consumer can receive from multiple Producers, so this MAY get called multiple times, one for each Producer. 31 | - (void)finishConsumingWithError:(NSError *)error; 32 | 33 | 34 | 35 | @optional 36 | 37 | //! Return an array of classes of values that your instance consumes. Implementing this method is optional and takes precedence over -consumedValueClass. 38 | - (NSArray *)consumedValueClasses; 39 | 40 | 41 | 42 | @end 43 | 44 | 45 | -------------------------------------------------------------------------------- /Sources/Foundation/Consumers/OCAInvoker.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInvoker.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 27.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCAConsumer.h" 11 | #import "OCAProducer.h" 12 | #import "OCAPlaceholderObject.h" 13 | #import "OCAInvocationCatcher.h" 14 | 15 | 16 | 17 | 18 | 19 | @interface OCAInvoker : OCAObject < OCAConsumer > 20 | 21 | 22 | 23 | #define OCAInvocation(TARGET, METHOD) \ 24 | (OCAInvoker *)({ \ 25 | OCAInvoker *invoker = nil;\ 26 | if (TARGET) {\ 27 | OCAInvocationCatcher *catcher = [[OCAInvocationCatcher alloc] initWithTarget:(TARGET)]; \ 28 | [(typeof(TARGET))catcher METHOD]; \ 29 | invoker = [[OCAInvoker alloc] initWithInvocationCatcher:catcher]; \ 30 | }\ 31 | invoker;\ 32 | }) \ 33 | 34 | //TODO: OCAInvocation subclass to handle flaws of NSInvocation. 35 | 36 | - (instancetype)initWithInvocationCatcher:(OCAInvocationCatcher *)catcher; 37 | 38 | @property (atomic, readonly, weak) id target; 39 | @property (atomic, readonly, strong) NSInvocation *invocation; 40 | 41 | 42 | 43 | @end 44 | 45 | 46 | -------------------------------------------------------------------------------- /Sources/Foundation/Consumers/OCASubscriber.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCASubscriber.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCAConsumer.h" 11 | 12 | 13 | 14 | typedef void(^OCASubscriberValueHandler)(id value); 15 | typedef void(^OCASubscriberFinishHandler)(NSError *error); 16 | 17 | 18 | 19 | 20 | 21 | /*! Subscriber is concrete universal Consumer, that can be customized using blocks. It is best alternative to implementing your own Consumer class. 22 | * NOTE: Since Subscriber uses blocks and is retained by Producers, you have to be careful about retain cycles. Best way to avoid them is not to use Subscriber at all. There may be better alternatives to achieve your goal, for example Invoker or Property can handle many cases without creating retain cycles. 23 | */ 24 | @interface OCASubscriber : OCAObject < OCAConsumer > 25 | 26 | 27 | 28 | #pragma mark Creating Subscriber 29 | 30 | //! Designated initializer. Use specific value class to opt-in to class-checking. All arguments may be nil. 31 | - (instancetype)initWithValueClass:(Class)valueClass 32 | valueHandler:(OCASubscriberValueHandler)valueHandler 33 | finishHandler:(OCASubscriberFinishHandler)finishHandler; 34 | 35 | //! Returns new Subscriber, that invokes given block once a value is received, but the value itself is ignored. 36 | + (instancetype)subscribe:(void(^)(void))handler; 37 | 38 | //! Returns new Subscriber with given value class and value handler. 39 | + (instancetype)subscribeForClass:(Class)valueClass handler:(OCASubscriberValueHandler)valueHandler; 40 | 41 | //! Returns new Subscriber with given value class, value handler and finish handler. 42 | + (instancetype)subscribeForClass:(Class)valueClass handler:(OCASubscriberValueHandler)valueHandler finish:(OCASubscriberFinishHandler)finishHandler; 43 | 44 | 45 | @property (atomic, readonly, strong) Class consumedValueClass; 46 | 47 | 48 | @end 49 | 50 | 51 | -------------------------------------------------------------------------------- /Sources/Foundation/Consumers/OCASubscriber.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCASubscriber.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCASubscriber.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCASubscriber () 16 | 17 | 18 | @property (atomic, readonly, copy) OCASubscriberValueHandler valueHandler; 19 | @property (atomic, readonly, copy) OCASubscriberFinishHandler finishHandler; 20 | 21 | 22 | @end 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @implementation OCASubscriber 34 | 35 | 36 | 37 | 38 | 39 | #pragma mark Creating Subscriber 40 | 41 | 42 | - (instancetype)init { 43 | return [self initWithValueClass:nil valueHandler:nil finishHandler:nil]; 44 | } 45 | 46 | 47 | - (instancetype)initWithValueClass:(Class)valueClass 48 | valueHandler:(OCASubscriberValueHandler)valueHandler 49 | finishHandler:(OCASubscriberFinishHandler)finishHandler { 50 | self = [super init]; 51 | if (self) { 52 | self->_consumedValueClass = valueClass; 53 | self->_valueHandler = valueHandler; 54 | self->_finishHandler = finishHandler; 55 | } 56 | return self; 57 | } 58 | 59 | 60 | + (instancetype)subscribe:(void (^)(void))handler { 61 | return [[self alloc] initWithValueClass:nil 62 | valueHandler:^(id value){ 63 | handler(); 64 | } 65 | finishHandler:nil]; 66 | } 67 | 68 | 69 | + (instancetype)subscribeForClass:(Class)valueClass handler:(OCASubscriberValueHandler)valueHandler { 70 | return [[self alloc] initWithValueClass:valueClass valueHandler:valueHandler finishHandler:nil]; 71 | } 72 | 73 | 74 | + (instancetype)subscribeForClass:(Class)valueClass handler:(OCASubscriberValueHandler)valueHandler finish:(OCASubscriberFinishHandler)finishHandler { 75 | return [[self alloc] initWithValueClass:valueClass valueHandler:valueHandler finishHandler:finishHandler]; 76 | } 77 | 78 | 79 | 80 | 81 | 82 | #pragma mark Lifetime of Subscriber 83 | 84 | 85 | @synthesize consumedValueClass = _consumedValueClass; 86 | 87 | 88 | - (void)consumeValue:(id)value { 89 | // Values are validated on Producer's side. 90 | OCASubscriberValueHandler handler = self.valueHandler; 91 | if (handler) handler(value); 92 | } 93 | 94 | 95 | - (void)finishConsumingWithError:(NSError *)error { 96 | OCASubscriberFinishHandler handler = self.finishHandler; 97 | if (handler) handler(error); 98 | } 99 | 100 | 101 | 102 | 103 | 104 | @end 105 | 106 | 107 | -------------------------------------------------------------------------------- /Sources/Foundation/Consumers/OCASwitch.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCASwitch.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 4.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCAConsumer.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCASwitch : OCAObject 17 | 18 | 19 | - (instancetype)initWithDictionary:(NSDictionary *)consumersByPredicates; 20 | 21 | + (OCASwitch *)switchYes:(id)consumer no:(id)consumer; 22 | + (OCASwitch *)switchIf:(NSPredicate *)predicate then:(id)consumer else:(id)consumer; 23 | 24 | @property (atomic, readonly, strong) NSDictionary *consumersByPredicates; 25 | @property (atomic, readonly, strong) Class consumedValueClass; 26 | 27 | 28 | @end 29 | 30 | 31 | -------------------------------------------------------------------------------- /Sources/Foundation/Consumers/OCASwitch.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCASwitch.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 4.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCASwitch.h" 10 | #import "OCAPredicate.h" 11 | 12 | 13 | 14 | 15 | 16 | @implementation OCASwitch 17 | 18 | 19 | 20 | - (instancetype)initWithDictionary:(NSDictionary *)consumersByPredicates { 21 | self = [super init]; 22 | if (self) { 23 | self->_consumersByPredicates = consumersByPredicates; 24 | 25 | NSArray *classes = [consumersByPredicates.allValues valueForKeyPath:OCAKP(OCASwitch, consumedValueClass)]; 26 | self->_consumedValueClass = [self valueClassForClasses:classes]; 27 | } 28 | return self; 29 | } 30 | 31 | 32 | + (OCASwitch *)switchYes:(id)trueConsumer no:(id)falseConsumer { 33 | return [[self alloc] initWithDictionary:@{ 34 | [OCAPredicate isTrue]: trueConsumer ?: (id)[NSNull null], 35 | [OCAPredicate isFalse]: falseConsumer ?: (id)[NSNull null], 36 | }]; 37 | } 38 | 39 | 40 | + (OCASwitch *)switchIf:(NSPredicate *)predicate then:(id)thenConsumer else:(id)elseConsumer { 41 | return [[self alloc] initWithDictionary:@{ 42 | predicate: thenConsumer ?: (id)[NSNull null], 43 | [predicate negate]: elseConsumer ?: (id)[NSNull null], 44 | }]; 45 | } 46 | 47 | 48 | 49 | 50 | 51 | - (void)consumeValue:(id)value { 52 | [self.consumersByPredicates enumerateKeysAndObjectsUsingBlock:^(NSPredicate *predicate, id consumer, BOOL *stop) { 53 | if (consumer != (id)[NSNull null]) { 54 | BOOL passed = [predicate evaluateWithObject:value]; 55 | if ( ! passed) return; 56 | 57 | id consumedValue = value; // Always new variable. 58 | BOOL consumedValid = [self validateObject:&consumedValue ofClass:[consumer consumedValueClass]]; 59 | if (consumedValid) { 60 | [consumer consumeValue:consumedValue]; 61 | } 62 | } 63 | }]; 64 | } 65 | 66 | 67 | - (void)finishConsumingWithError:(NSError *)error { 68 | self->_consumersByPredicates = nil; 69 | } 70 | 71 | 72 | 73 | 74 | 75 | @end 76 | 77 | 78 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCABridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCABridge.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 5.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMediator.h" 10 | 11 | 12 | 13 | 14 | 15 | //! Bridge is a Mediator (Producer and Consumer), that simply passes values further with optional transformation. 16 | @interface OCABridge : OCAMediator 17 | 18 | 19 | 20 | #pragma mark Creating Bridge 21 | 22 | /*! Designated initializer. Initializes new Bridge that uses given transformer to transform values. Pass nil for no transformation on values. 23 | * The receiver will use transformer's +valueClass and +transformedValueClass methods to declare its own -valueClass and -consumedValueClass. 24 | */ 25 | - (instancetype)initWithTransformer:(NSValueTransformer *)transformer; 26 | 27 | //! Creates new Bridge, that passes values of given class. If class is nil, it passes all values to its Consumers. 28 | + (OCABridge *)bridgeForClass:(Class)theClass; 29 | 30 | //! Create new Bridge, that uses sequence of transformers to transform values passed then to Consumers. 31 | + (OCABridge *)bridgeWithTransformers:(NSValueTransformer *)transformer, ... NS_REQUIRES_NIL_TERMINATION; 32 | 33 | 34 | 35 | #pragma mark Transformer 36 | 37 | //! Property that contains the transformer passed to initializer, if any. 38 | @property (atomic, readonly, strong) NSValueTransformer *transformer; 39 | 40 | 41 | 42 | @end 43 | 44 | 45 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCABridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCABridge.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 5.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCABridge.h" 10 | #import "OCAProducer+Subclass.h" 11 | #import "OCATransformer+Core.h" 12 | #import "OCAVariadic.h" 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | @implementation OCABridge 24 | 25 | 26 | 27 | 28 | 29 | #pragma mark Creating Bridge 30 | 31 | 32 | - (instancetype)initWithValueClass:(Class)valueClass { 33 | NSValueTransformer *transformer = [[OCATransformer pass] specializeFromClass:valueClass toClass:valueClass]; 34 | return [self initWithTransformer:transformer]; 35 | } 36 | 37 | 38 | - (instancetype)initWithTransformer:(NSValueTransformer *)transformer { 39 | self = [super initWithValueClass:[transformer.class transformedValueClass]]; 40 | if (self) { 41 | self->_transformer = transformer ?: [OCATransformer pass]; 42 | } 43 | return self; 44 | } 45 | 46 | 47 | + (OCABridge *)bridgeForClass:(Class)class { 48 | NSValueTransformer *transformer = [[OCATransformer pass] specializeFromClass:class toClass:class]; 49 | return [[self alloc] initWithTransformer:transformer]; 50 | } 51 | 52 | 53 | + (OCABridge *)bridgeWithTransformers:(NSValueTransformer *)firstTransformer, ... NS_REQUIRES_NIL_TERMINATION { 54 | NSArray *transformersArray = OCAArrayFromVariadicArguments(firstTransformer); 55 | NSValueTransformer *transformer = (transformersArray.count <= 1 56 | ? transformersArray.firstObject 57 | : [OCATransformer sequence:transformersArray]); 58 | return [[self alloc] initWithTransformer:transformer]; 59 | } 60 | 61 | 62 | 63 | 64 | 65 | #pragma mark Lifetime of Bridge 66 | 67 | 68 | - (Class)consumedValueClass { 69 | return [self.transformer.class valueClass]; 70 | } 71 | 72 | 73 | - (void)consumeValue:(id)value { 74 | id transformedValue = [self.transformer transformedValue:value]; 75 | [self produceValue:transformedValue]; 76 | } 77 | 78 | 79 | - (void)finishConsumingWithError:(NSError *)error { 80 | [self finishProducingWithError:error]; 81 | } 82 | 83 | 84 | 85 | 86 | 87 | @end 88 | 89 | 90 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAContext.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMediator.h" 10 | #import "OCAProperty.h" 11 | #import "OCAQueue.h" 12 | 13 | 14 | 15 | typedef void(^OCAContextExecutionBlock)(void); 16 | typedef void(^OCAContextDefinitionBlock)(OCAContextExecutionBlock executionBlock); 17 | 18 | extern OCAContextDefinitionBlock const OCAContextDefaultDefinitionBlock; 19 | 20 | 21 | 22 | 23 | 24 | /*! This class represents “a context”, that acts as a Consumer and a Producer. Received values are passed to Consumers in a known context. 25 | * “Context” for the purpose of this class means, that some code is executed before and after the main task. 26 | * Good example is use of NSLock (via +lock: constructor). When the Context object receives a value, it locks the Lock, passes the value to Consumers and then unlocks the Lock. This means, that the Consumers are invoked in a known “context”. 27 | * You can use one of the provided constructors or define your own using simple block. 28 | * 29 | * NOTE: Since this class is quite abstract, it allows you to do anything in the definition block. For thing like filtering, transformations or delaying the production of values, use appropriate Mediator subclasses. Don't abuse this Context. 30 | */ 31 | @interface OCAContext : OCAMediator 32 | 33 | 34 | 35 | #pragma mark Creating Context 36 | 37 | //! Designated initializer. Definition block receives execution block that must be invoked. Execute desired code before and after the execution block as you wish. You may even nest the execution block into other block, for example for animations or queues. 38 | - (instancetype)initWithDefinitionBlock:(OCAContextDefinitionBlock)definitionBlock; 39 | 40 | //! Returns a Context that does nothing extra. 41 | + (OCAContext *)empty; 42 | 43 | //! Shorthand for designated initializer. 44 | + (OCAContext *)custom:(OCAContextDefinitionBlock)block; 45 | 46 | //! Returns a Context, that sets given value to the property, then produces consumed value and reverts the proeprty to previous value. For example, you may have a flag to supress observations of a notification, so you may want to set the flag to YES while sending it. 47 | + (OCAContext *)property:(OCAProperty *)property value:(id)value; 48 | 49 | //! Returns a Context, that produces consumed values on provided queue. 50 | + (OCAContext *)onQueue:(OCAQueue *)queue synchronous:(BOOL)synchronous; 51 | 52 | //! Returns a Context, that lock and unlocks provided NSLocking object. This means all chained Consumers are executed inside of locked context. 53 | + (OCAContext *)lock:(id)lock; 54 | 55 | //! Returns a Context, that uses @synchronized directive to produce values. 56 | + (OCAContext *)synchronized:(id)object; 57 | 58 | //! Returns a Context, that produces consumed values in auto-release pool. 59 | + (OCAContext *)autoreleasepool; 60 | 61 | 62 | 63 | #pragma mark Using Context 64 | 65 | //! Definition block as passed to the designated initializer. 66 | @property (atomic, readonly, strong) OCAContextDefinitionBlock definitionBlock; 67 | 68 | //! You can use this method to execute arbitrary code inside of the context represented by the receiver. 69 | - (void)execute:(OCAContextExecutionBlock)executionBlock; 70 | 71 | 72 | 73 | @end 74 | 75 | 76 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAContext.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAContext.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAContext.h" 10 | #import "OCAProducer+Subclass.h" 11 | 12 | 13 | 14 | 15 | 16 | OCAContextDefinitionBlock const OCAContextDefaultDefinitionBlock = ^(OCAContextExecutionBlock executionBlock){ 17 | executionBlock(); 18 | }; 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | @implementation OCAContext 30 | 31 | 32 | 33 | 34 | 35 | #pragma mark Creating Context 36 | 37 | 38 | - (instancetype)initWithValueClass:(Class)valueClass { 39 | return [self initWithDefinitionBlock:nil]; 40 | } 41 | 42 | 43 | - (instancetype)initWithDefinitionBlock:(OCAContextDefinitionBlock)definitionBlock { 44 | self = [super initWithValueClass:nil]; 45 | if (self) { 46 | self->_definitionBlock = definitionBlock ?: OCAContextDefaultDefinitionBlock; 47 | } 48 | return self; 49 | } 50 | 51 | 52 | + (OCAContext *)empty { 53 | return [[self alloc] initWithDefinitionBlock:nil]; 54 | } 55 | 56 | 57 | + (OCAContext *)custom:(OCAContextDefinitionBlock)block { 58 | return [[self alloc] initWithDefinitionBlock:block]; 59 | } 60 | 61 | 62 | + (OCAContext *)property:(OCAProperty *)property value:(id)value { 63 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 64 | id originalValue = [property value]; 65 | [property setValue:value]; 66 | 67 | executionBlock(); // Block is executed when the property has desired value. 68 | 69 | [property setValue:originalValue]; 70 | }]; 71 | } 72 | 73 | 74 | + (OCAContext *)onQueue:(OCAQueue *)queue synchronous:(BOOL)synchronous { 75 | if (synchronous) { 76 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 77 | [queue performBlockAndWait:executionBlock]; 78 | }]; 79 | } 80 | else { 81 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 82 | [queue performBlockAndTryWait:executionBlock]; 83 | }]; 84 | } 85 | } 86 | 87 | 88 | + (OCAContext *)lock:(id)lock { 89 | OCAAssert(lock != nil, @"Lock is missing.") return nil; 90 | return [[OCAContext alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 91 | [lock lock]; 92 | 93 | executionBlock(); 94 | 95 | [lock unlock]; 96 | }]; 97 | } 98 | 99 | 100 | + (OCAContext *)synchronized:(id)object { 101 | OCAAssert(object != nil, @"Object is missing.") return nil; 102 | return [[OCAContext alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 103 | @synchronized(object) { 104 | executionBlock(); 105 | } 106 | }]; 107 | } 108 | 109 | 110 | + (OCAContext *)autoreleasepool { 111 | return [[OCAContext alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 112 | @autoreleasepool { 113 | executionBlock(); 114 | } 115 | }]; 116 | } 117 | 118 | 119 | 120 | 121 | 122 | #pragma mark Using Context 123 | 124 | 125 | - (void)execute:(OCAContextExecutionBlock)executionBlock { 126 | self.definitionBlock(executionBlock); 127 | } 128 | 129 | 130 | 131 | 132 | 133 | #pragma mark Context as a Consumer 134 | 135 | 136 | - (Class)consumedValueClass { 137 | return nil; 138 | } 139 | 140 | 141 | - (void)consumeValue:(id)value { 142 | [self execute:^{ 143 | [self produceValue:value]; 144 | }]; 145 | } 146 | 147 | 148 | - (void)finishConsumingWithError:(NSError *)error { 149 | [self execute:^{ 150 | [self finishProducingWithError:error]; 151 | }]; 152 | } 153 | 154 | 155 | 156 | 157 | 158 | @end 159 | 160 | 161 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAFilter.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAFilter.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 16.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMediator.h" 10 | 11 | 12 | 13 | 14 | 15 | //! Filter is a Mediator, that evaluates predicates on the values. If the object evaluates to Yes, it is passed further, otherwise no action is made. 16 | @interface OCAFilter : OCAMediator 17 | 18 | 19 | 20 | #pragma mark Creating Filter 21 | 22 | //! Designated initializer. Pass a predicate object you wish to evaluate on the values. 23 | - (instancetype)initWithPredicate:(NSPredicate *)predicate; 24 | 25 | //! Returns new Filer with given predicate. 26 | + (OCAFilter *)filterWithPredicate:(NSPredicate *)predicate; 27 | 28 | //! Returns new Filter, that remembers number of consumed objects and passes all of them once it receives more than \c count. 29 | + (OCAFilter *)filterThatSkipsFirst:(NSUInteger)count; 30 | 31 | //! Returns new Filter, that stores previously evaluated object and passes new objects only is they are not equal (as defined by isEqual: method). 32 | + (OCAFilter *)filterThatSkipsEqual; 33 | 34 | 35 | 36 | #pragma mark Using Filter 37 | 38 | //! Predicate as passed to the initializer. 39 | @property (atomic, readonly, strong) NSPredicate *predicate; 40 | 41 | //! This allows you to use Filter out of chain. 42 | - (BOOL)validateObject:(id)object; 43 | 44 | 45 | 46 | @end 47 | 48 | 49 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAFilter.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAFilter.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 16.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAFilter.h" 10 | #import "OCAProducer+Subclass.h" 11 | #import "OCAPredicate.h" 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @implementation OCAFilter 23 | 24 | 25 | 26 | 27 | 28 | #pragma mark Creating Filter 29 | 30 | 31 | - (instancetype)initWithValueClass:(Class)valueClass { 32 | NSPredicate *predicate = [OCAPredicate predicateForClass:valueClass block:^BOOL(id object) { 33 | return YES; // Pass all of that class. 34 | }]; 35 | return [self initWithPredicate:predicate]; 36 | } 37 | 38 | 39 | - (instancetype)initWithPredicate:(NSPredicate *)predicate { 40 | self = [super initWithValueClass:nil]; 41 | if (self) { 42 | self->_predicate = predicate ?: [NSPredicate predicateWithValue:YES]; 43 | } 44 | return self; 45 | } 46 | 47 | 48 | + (OCAFilter *)filterWithPredicate:(NSPredicate *)predicate { 49 | return [[self alloc] initWithPredicate:predicate]; 50 | } 51 | 52 | 53 | + (OCAFilter *)filterThatSkipsFirst:(NSUInteger)countToSkip { 54 | // This instance uses __block variable that outlives the block scope. 55 | __block NSUInteger countOfValues = 0; 56 | NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 57 | countOfValues ++; 58 | return (countOfValues > countToSkip); 59 | }]; 60 | return [[self alloc] initWithPredicate:predicate]; 61 | } 62 | 63 | 64 | + (OCAFilter *)filterThatSkipsEqual { 65 | // This instance uses __block variable that outlives the block scope. 66 | __block id previouslyEvaluatedObject = nil; //TODO: Don't retain the previous forever. Maybe class and hash comparision would be enough. 67 | NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 68 | BOOL isEqual = OCAEqual(previouslyEvaluatedObject, evaluatedObject); 69 | previouslyEvaluatedObject = evaluatedObject; 70 | return ! isEqual; 71 | }]; 72 | return [[self alloc] initWithPredicate:predicate]; 73 | } 74 | 75 | 76 | 77 | 78 | 79 | #pragma mark Using Filter 80 | 81 | 82 | - (BOOL)validateObject:(id)object { 83 | BOOL compatible = [self isClass:[object class] compatibleWithClass:self.valueClass]; 84 | return (compatible && [self.predicate evaluateWithObject:object]); 85 | } 86 | 87 | 88 | 89 | 90 | 91 | #pragma mark Filter as a Consumer 92 | 93 | 94 | - (Class)consumedValueClass { 95 | return nil; 96 | } 97 | 98 | 99 | - (void)consumeValue:(id)value { 100 | if ([self validateObject:value]) { 101 | [self produceValue:value]; 102 | } 103 | } 104 | 105 | 106 | - (void)finishConsumingWithError:(NSError *)error { 107 | [self finishProducingWithError:error]; 108 | } 109 | 110 | 111 | 112 | 113 | 114 | @end 115 | 116 | 117 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCALowPassFilter.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCALowPassFilter.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 23.10.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMediator.h" 10 | #import "OCAMath.h" 11 | #import "OCAAccessor.h" 12 | 13 | 14 | 15 | /*! Low-pass filter is used for processing of real-time data with constant frequency. It produces smoother values than it receives. 16 | * Low-pass filters are good for animating data that changes very quickly, for example input from accelerometer or similar sensors. 17 | * You feed the filter by connecting it to a chain or setting input property. Input may come at any frequency, even irregularly. 18 | * You obtain the values by connecting the filter to other Consumer or by observing output property. Output is updated with operating frequency. 19 | * 20 | * Low-pass filter typically processes scalar values, but you may also provide class and its accessors to proces structured values. 21 | * Example: To process CGPoint values wrapped in NSValue, initialize the instance with NSValue class and accessors for X and Y components. 22 | * Processing non-scalar types is more computationally expensive and processed types must conform to NSCopying protocol. 23 | * When no accessors are specified, the filter uses -doubleValue method to convert the processed values to scalars. 24 | */ 25 | @interface OCALowPassFilter : OCAMediator 26 | 27 | 28 | //! Creates new low-pass filter at 60Hz, with cut-off frequency of fraction * 60Hz. 29 | + (instancetype)filterWithTolerance:(OCAReal)fraction; 30 | 31 | //! Creates new low-pass filter that operates on given frequency and processes numbers. Cut-off frequency is used to soften the input. 32 | + (instancetype)filterWithFrequency:(OCAInteger)hertz cutoff:(OCAInteger)hertz; 33 | 34 | //! Creates new low-pass filter that operates on given frequency. You may specify custom class and its accessors that must return NSNumber objects. 35 | - (instancetype)initWithFrequency:(OCAInteger)hertz cutoff:(OCAInteger)hertz fadeNils:(BOOL)fadeNils inputClass:(Class)inputClass accessors:(OCAAccessor *)accessors, ... NS_REQUIRES_NIL_TERMINATION; 36 | 37 | 38 | //! Frequency on which this filter operates. Use 60Hz when you display the output values. 39 | @property (nonatomic, readonly) OCAInteger frequency; 40 | //! Cut-off frequency used to calculate intersteps of output values. 41 | @property (nonatomic, readonly) OCAInteger cutoff; 42 | //! Whether the filter treats nils as zero values (YES), or it immediately produces nils (NO). 43 | @property (nonatomic, readonly) BOOL fadeNils; 44 | //! Accessors used on input values to obtain numeric components and then used on copy of the input to set them. 45 | @property (nonatomic, readonly) NSArray *accessors; 46 | 47 | 48 | //! Last value that has passes to the filter. Set this to feed the filter, or use it as a Consumer. 49 | @property (atomic, strong) id input; 50 | //! Lats value produced by this filter. Read this property, observe it or connecting the filter to other Consumer. 51 | @property (atomic, readonly) id output; 52 | 53 | 54 | @end 55 | 56 | 57 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAMediator.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAMediator.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 27.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAProducer.h" 10 | 11 | 12 | 13 | 14 | 15 | /*! Mediator is abstract class, that inherits from Producer and conforms to Consumer protocol. 16 | * Subclasses of this class have two things in common: 17 | * 1. They don't really consume the values in meaningful manner. 18 | * 2. They don't really produce any genuine values. 19 | * Mediator serves as an intermediate step between original Producer and final Consumer and allows you to do powerful manipulations to the values that flow between them. 20 | * 21 | * Concrete Mediators allow you to transform the values, filter them, forward them in a known context, limit them, or temporarily stop them. 22 | */ 23 | @interface OCAMediator : OCAProducer 24 | 25 | @end 26 | 27 | 28 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAMediator.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAMediator.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 27.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMediator.h" 10 | #import "OCAProducer+Subclass.h" 11 | 12 | 13 | 14 | 15 | 16 | @implementation OCAMediator 17 | 18 | 19 | 20 | 21 | 22 | - (Class)consumedValueClass { 23 | return nil; 24 | } 25 | 26 | 27 | - (void)consumeValue:(id)value { 28 | OCAAssert(NO, @"You can't use this abstract class!") return; 29 | } 30 | 31 | 32 | - (void)finishConsumingWithError:(NSError *)error { 33 | OCAAssert(NO, @"You can't use this abstract class!") return; 34 | } 35 | 36 | 37 | 38 | 39 | 40 | - (void)finishProducingWithError:(NSError *)error { 41 | if (self.isFinished) return; 42 | 43 | if (error) { 44 | // Errors are fatal, so we finish. 45 | [super finishProducingWithError:error]; 46 | } 47 | else { 48 | // When Producer finished successfully, do nothing there should be more of them. And if not... 49 | } 50 | } 51 | 52 | 53 | - (void)dealloc { 54 | // ... we will finish once deallocated. 55 | [super finishProducingWithError:nil]; 56 | } 57 | 58 | 59 | 60 | 61 | 62 | @end 63 | 64 | 65 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAThrottle.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAThrottle.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 2.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMediator.h" 10 | 11 | 12 | 13 | 14 | 15 | /*! Throttle is a Mediator, that reduces frequency of production. It takes a time interval and can operate in two modes: 16 | * 1. Non-continuous: Will forward latest value after no new value came in given time interval. 17 | * 2. Continuous: Will forward one latest value repeatedly after the given interval. 18 | */ 19 | @interface OCAThrottle : OCAMediator 20 | 21 | 22 | 23 | #pragma mark Creating Throttle 24 | 25 | //! Designated initializer. Pass desired interval and choose the continuation mode. 26 | - (instancetype)initWithInterval:(NSTimeInterval)interval continuous:(BOOL)continuous; 27 | 28 | //! Returns new Throttle with given interval and continuity. 29 | + (OCAThrottle *)throttleWithInterval:(NSTimeInterval)interval continuous:(BOOL)continuous; 30 | 31 | 32 | 33 | #pragma mark State of the Throttle 34 | 35 | //! Interval as passed to designated initializer. 36 | @property (atomic, readonly, assign) NSTimeInterval interval; 37 | 38 | //! Continuity flag as passed to designated initializer. 39 | @property (atomic, readonly, assign) BOOL isContinuous; 40 | 41 | //! Flag, that indicates whether the receiver is actually throttling a value. 42 | @property (atomic, readonly, assign) BOOL isThrottled; 43 | 44 | //! Last consumed value, that has not yet been forwarded. 45 | @property (atomic, readonly, strong) id lastThrottledValue; 46 | 47 | 48 | 49 | @end 50 | 51 | 52 | -------------------------------------------------------------------------------- /Sources/Foundation/Mediators/OCAThrottle.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAThrottle.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 2.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAThrottle.h" 10 | #import "OCAProducer+Subclass.h" 11 | #import "OCATimer.h" 12 | #import "OCASubscriber.h" 13 | 14 | 15 | 16 | 17 | 18 | @interface OCAThrottle () 19 | 20 | 21 | @property (atomic, readwrite, strong) OCATimer *timer; 22 | @property (atomic, readwrite, assign) BOOL isThrottled; 23 | @property (atomic, readwrite, strong) id lastThrottledValue; 24 | @property (atomic, readwrite, strong) OCASubscriber *subscriber; 25 | 26 | 27 | @end 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | @implementation OCAThrottle 39 | 40 | 41 | 42 | 43 | 44 | #pragma mark Creating Throttle 45 | 46 | 47 | - (instancetype)initWithValueClass:(Class)valueClass { 48 | return [self initWithInterval:0 continuous:NO]; 49 | } 50 | 51 | 52 | - (instancetype)initWithInterval:(NSTimeInterval)interval continuous:(BOOL)continuous { 53 | self = [super initWithValueClass:nil]; 54 | if (self) { 55 | OCAAssert(interval >= 0, @"I am not a time traveller.") interval = 0; 56 | 57 | self->_interval = interval; 58 | self->_isContinuous = continuous; 59 | 60 | OCAWeakify(self); 61 | self.subscriber = [[OCASubscriber alloc] initWithValueClass:nil 62 | valueHandler:^(id value) { 63 | OCAStrongify(self); 64 | 65 | self.timer = nil; 66 | self.isThrottled = NO; 67 | [self produceValue:self.lastThrottledValue]; 68 | self.lastThrottledValue = nil; 69 | 70 | } finishHandler:nil]; 71 | } 72 | return self; 73 | } 74 | 75 | 76 | + (OCAThrottle *)throttleWithInterval:(NSTimeInterval)delay { 77 | return [[self alloc] initWithInterval:delay continuous:NO]; 78 | } 79 | 80 | 81 | + (OCAThrottle *)throttleWithInterval:(NSTimeInterval)delay continuous:(BOOL)continuous { 82 | return [[self alloc] initWithInterval:delay continuous:continuous]; 83 | } 84 | 85 | 86 | 87 | 88 | 89 | #pragma mark Lifetime of Throttle 90 | 91 | 92 | - (void)consumeValue:(id)value { 93 | if (self.interval == 0) { 94 | [self produceValue:value]; 95 | return; 96 | } 97 | 98 | self.lastThrottledValue = value; 99 | if (self.isContinuous && self.timer.isRunning) return; 100 | 101 | [self.timer stop]; 102 | NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:self.interval]; 103 | self.timer = [[OCATimer alloc] initWithOwner:self queue:nil startDate:fireDate interval:0 leeway:0 endDate:nil]; 104 | self.isThrottled = YES; 105 | [self.timer addConsumer:self.subscriber]; 106 | } 107 | 108 | 109 | - (void)finishConsumingWithError:(NSError *)error { 110 | [self.timer stop]; 111 | self.isThrottled = NO; 112 | self.lastThrottledValue = nil; 113 | [self finishProducingWithError:error]; 114 | } 115 | 116 | 117 | 118 | 119 | 120 | @end 121 | 122 | 123 | -------------------------------------------------------------------------------- /Sources/Foundation/OCAFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectiveChain.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | 11 | /// Producers 12 | 13 | #import "OCAProducer.h" 14 | #import "OCACommand.h" 15 | #import "OCAHub.h" 16 | #import "OCAProperty.h" 17 | #import "OCATimer.h" 18 | #import "OCANotificator.h" 19 | #import "OCAInterpolator.h" 20 | 21 | 22 | /// Mediators 23 | 24 | #import "OCAMediator.h" 25 | #import "OCABridge.h" 26 | #import "OCAContext.h" 27 | #import "OCAFilter.h" 28 | #import "OCAThrottle.h" 29 | #import "OCALowPassFilter.h" 30 | 31 | 32 | /// Consumers 33 | 34 | #import "OCAConsumer.h" 35 | #import "OCASubscriber.h" 36 | #import "OCAInvoker.h" 37 | #import "OCASwitch.h" 38 | 39 | 40 | /// Transformers 41 | 42 | #import "OCATransformer.h" 43 | 44 | 45 | /// Predicates 46 | 47 | #import "OCAPredicate.h" 48 | 49 | 50 | /// Utilities 51 | 52 | #import "OCAKeyPathAccessor.h" 53 | #import "OCAStructureAccessor.h" 54 | #import "OCAIndexAccessor.h" 55 | 56 | #import "OCAQueue.h" 57 | #import "OCASemaphore.h" 58 | 59 | #import "NSValue+Boxing.h" 60 | #import "NSArray+Ordinals.h" 61 | #import "OCADecomposer.h" 62 | #import "OCAInvocationCatcher.h" 63 | #import "OCAKeyValueChange.h" 64 | #import "OCAVariadic.h" 65 | #import "OCAPlaceholderObject.h" 66 | #import "OCAMutableArrayProxy.h" 67 | 68 | #import "OCADebug.h" 69 | 70 | 71 | -------------------------------------------------------------------------------- /Sources/Foundation/OCAObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAObject.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | #define OCAKPUnsafe(KEYPATH) NSStringFromSelector(@selector(KEYPATH)) 14 | #define OCAKP(CLASS, KEYPATH) OCAKPObject([CLASS new], KEYPATH) 15 | #define OCAKPObject(OBJECT, KEYPATH) \ 16 | _Pragma("clang diagnostic push") \ 17 | _Pragma("clang diagnostic ignored \"-Warc-repeated-use-of-weak\"") \ 18 | ((NSString *)@(((void)(NO && ((void)OBJECT.KEYPATH, NO)), # KEYPATH))) \ 19 | _Pragma("clang diagnostic pop") \ 20 | 21 | #define OCAWeakify(VARIABLE) __weak typeof(VARIABLE) VARIABLE##_weak = VARIABLE 22 | #define OCAStrongify(VARIABLE) __strong typeof(VARIABLE##_weak) VARIABLE = VARIABLE##_weak 23 | 24 | #define OCAKeyPathsAffecting(AFFECTED, KEYPATHS...)\ 25 | + (NSSet *)keyPathsForValuesAffecting##AFFECTED { return [NSSet setWithObjects: KEYPATHS, nil]; } 26 | 27 | #define OCAEqual(A, B) OCAEqualCustom(A, isEqual:, B) 28 | #define OCAEqualString(A, B) OCAEqualCustom(A, isEqualToString:, B) 29 | #define OCAEqualCustom(A, SELECTOR, B) \ 30 | (BOOL)({ \ 31 | typeof(A) a = (A); \ 32 | typeof(B) b = (B); \ 33 | ((a == b) || (b && [a SELECTOR b])); \ 34 | }) \ 35 | 36 | #define OCA_iOS TARGET_OS_IPHONE 37 | 38 | #define CONVENIENCE 39 | 40 | #define OCAT(TYPE) @(@encode(TYPE)) 41 | #define OCATypes(...) ( [@[ __VA_ARGS__ ] componentsJoinedByString:@""] ) 42 | 43 | 44 | 45 | 46 | 47 | @interface OCAObject : NSObject 48 | 49 | 50 | 51 | #pragma mark Class Checking 52 | 53 | + (Class)valueClassForClasses:(NSArray *)classes; 54 | - (Class)valueClassForClasses:(NSArray *)classes; 55 | 56 | //! These methods take reference to the variable, because they may modify the value to make it valid. So far only one modification is made: NSNull is replaced with nil (and YES is returned). 57 | + (BOOL)validateObject:(id *)object ofClass:(Class)theClass; 58 | - (BOOL)validateObject:(id *)object ofClass:(Class)theClass; 59 | + (BOOL)validateObject:(id *)object ofClasses:(NSArray *)classes; 60 | - (BOOL)validateObject:(id *)object ofClasses:(NSArray *)classes; 61 | 62 | + (BOOL)isClass:(Class)class1 compatibleWithClass:(Class)class2; 63 | + (BOOL)isClass:(Class)classToCheck compatibleWithClasses:(NSArray *)classes; 64 | - (BOOL)isClass:(Class)class1 compatibleWithClass:(Class)class2; 65 | 66 | 67 | #pragma mark Describing Objects 68 | 69 | - (NSString *)descriptionName; 70 | - (NSString *)description; 71 | - (NSString *)shortDescription; 72 | - (NSDictionary *)debugDescriptionValues; 73 | - (NSString *)debugDescription; 74 | - (NSString *)debugShortDescription; 75 | 76 | 77 | 78 | @end 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | #if !defined(NS_BLOCK_ASSERTIONS) 89 | 90 | #define OCAAssert(CONDITION, MESSAGE, ...) \ 91 | if ( ! (CONDITION) && (( [[NSAssertionHandler currentHandler] \ 92 | handleFailureInFunction: [NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ 93 | file: [NSString stringWithUTF8String:__FILE__] \ 94 | lineNumber: __LINE__ \ 95 | description: (MESSAGE), ##__VA_ARGS__], YES)) ) // Will NOT execute appended code, if exception is thrown. 96 | 97 | #else 98 | 99 | #define OCAAssert(CONDITION, MESSAGE, ...)\ 100 | if ( ! (CONDITION) && (( NSLog(@"*** Assertion failure in %s, %s:%d, Condition not satisfied: %s, reason: '" MESSAGE "'", __PRETTY_FUNCTION__, __FILE__, __LINE__, #CONDITION, ##__VA_ARGS__), YES)) ) // Will execute appended code. 101 | 102 | #endif 103 | 104 | 105 | -------------------------------------------------------------------------------- /Sources/Foundation/Predicates/OCAPredicate.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 11.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCAAccessor.h" 11 | #import "OCAProperty.h" 12 | 13 | 14 | 15 | 16 | 17 | @interface NSPredicate (OCAPredicateCompatibility) 18 | 19 | 20 | - (instancetype)negate; 21 | 22 | 23 | @end 24 | 25 | 26 | 27 | 28 | 29 | @interface OCAPredicate : OCAObject 30 | 31 | 32 | + (NSPredicate *)isNil; 33 | + (NSPredicate *)isNotNil; 34 | + (NSPredicate *)predicateForClass:(Class)theClass block:(BOOL(^)(id object))block; 35 | 36 | 37 | @end 38 | 39 | 40 | 41 | 42 | 43 | @interface OCAPredicate (Predefined) 44 | 45 | 46 | 47 | + (NSPredicate *)isTrue; 48 | + (NSPredicate *)isFalse; 49 | + (NSPredicate *)isEmpty; 50 | + (NSPredicate *)isNotEmpty; 51 | 52 | + (NSPredicate *)pass; 53 | + (NSPredicate *)discard; 54 | 55 | + (NSPredicate *)isKindOf:(Class)theClass; 56 | 57 | + (NSPredicate *)not:(NSPredicate *)predicate; 58 | + (NSPredicate *)and:(NSArray *)subpredicates; 59 | + (NSPredicate *)or:(NSArray *)subpredicates; 60 | + (NSPredicate *)all:(NSPredicate *)predicate; 61 | + (NSPredicate *)any:(NSPredicate *)predicate; 62 | 63 | + (NSPredicate *)predicateForClass:(Class)theClass predicate:(NSPredicate *)predicate; 64 | + (NSPredicate *)predicateFor:(Class)theClass format:(NSString *)format, ...; 65 | 66 | + (NSPredicate *)isProperty:(OCAProperty *)property; 67 | + (NSPredicate *)compareProperty:(OCAProperty *)property using:(NSPredicate *)predicate; 68 | 69 | + (NSPredicate *)compare:(OCAAccessor *)accessor using:(NSPredicate *)predicate; 70 | + (NSPredicate *)compare:(OCAAccessor *)accessor format:(NSString *)operatorFormat value:(id)value; 71 | 72 | + (NSPredicate *)operator:(NSPredicateOperatorType)theOperator options:(NSComparisonPredicateOptions)options value:(id)value; 73 | + (NSPredicate *)isLessThan:(id)value; 74 | + (NSPredicate *)isLessThanOrEqual:(id)value; 75 | + (NSPredicate *)isGreaterThan:(id)value; 76 | + (NSPredicate *)isGreaterThanOrEqual:(id)value; 77 | + (NSPredicate *)isEqualTo:(id)value; 78 | + (NSPredicate *)isNotEqualTo:(id)value; 79 | + (NSPredicate *)matches:(NSString *)regex; 80 | + (NSPredicate *)isLike:(NSString *)string; 81 | + (NSPredicate *)beginsWith:(id)value; 82 | + (NSPredicate *)endsWith:(id)value; 83 | + (NSPredicate *)isIn:(id)value; 84 | + (NSPredicate *)contains:(id)value; 85 | + (NSPredicate *)isBetween:(id)lower and:(id)upper; 86 | 87 | 88 | 89 | @end 90 | 91 | 92 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCACommand.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCACommand.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAProducer.h" 10 | 11 | 12 | 13 | 14 | 15 | //! Command is a Producer that allows manually sending values. Using Command is an alternative to subclassing the Producer. 16 | @interface OCACommand : OCAProducer 17 | 18 | 19 | 20 | #pragma mark Creating Command 21 | 22 | //! Initializes Command that produces values of given class. 23 | - (instancetype)initWithValueClass:(Class)valueClass; 24 | 25 | //! Creates Command that produces values of given class. 26 | + (instancetype)commandForClass:(Class)valueClass; 27 | 28 | //! Shortcut to manually send value to any consumer. Creates temporary Command producer. 29 | + (void)send:(id)value to:(id)consumer; 30 | 31 | 32 | 33 | #pragma mark Using Command 34 | 35 | //! Produces value to all Consumers. 36 | - (void)sendValue:(id)value; 37 | 38 | //! Produces values in array to all Consumers. 39 | - (void)sendValues:(NSArray *)values; 40 | 41 | //! Marks the Command as finished and sends the errro to Consumers. 42 | - (void)finishWithError:(NSError *)error; 43 | 44 | 45 | 46 | @end 47 | 48 | 49 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCACommand.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCACommand.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCACommand.h" 10 | #import "OCAProducer+Subclass.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCACommand 22 | 23 | 24 | 25 | 26 | 27 | #pragma mark Creating Command 28 | 29 | 30 | - (instancetype)initWithValueClass:(Class)valueClass { 31 | return [super initWithValueClass:valueClass]; 32 | } 33 | 34 | 35 | + (instancetype)commandForClass:(Class)valueClass { 36 | return [[self alloc] initWithValueClass:valueClass]; 37 | } 38 | 39 | 40 | + (void)send:(id)value to:(id)consumer { 41 | if ( ! consumer) return; 42 | 43 | OCACommand *command = [OCACommand commandForClass:[value classForCoder]]; 44 | [command connectTo:consumer]; 45 | [command sendValue:value]; 46 | } 47 | 48 | 49 | 50 | 51 | 52 | #pragma mark Using Command 53 | 54 | 55 | - (void)sendValue:(id)value { 56 | [self produceValue:value]; 57 | } 58 | 59 | 60 | - (void)sendValues:(NSArray *)values { 61 | for (id object in values) { 62 | [self produceValue:object]; 63 | } 64 | } 65 | 66 | 67 | - (void)finishWithError:(NSError *)error { 68 | [self finishProducingWithError:error]; 69 | } 70 | 71 | 72 | 73 | 74 | 75 | @end 76 | 77 | 78 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCAHub.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAHub.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 5.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAProducer.h" 10 | 11 | 12 | 13 | typedef enum : NSInteger { 14 | OCAHubTypeMerge, 15 | OCAHubTypeCombine, 16 | OCAHubTypeDependency, 17 | } OCAHubType; 18 | 19 | 20 | 21 | 22 | 23 | @interface OCAHub : OCAProducer 24 | 25 | 26 | 27 | #pragma mark Creating Hub 28 | 29 | - (instancetype)initWithType:(OCAHubType)type producers:(NSArray *)producers; 30 | + (instancetype)merge:(OCAProducer *)producers, ... NS_REQUIRES_NIL_TERMINATION; 31 | + (instancetype)combine:(OCAProducer *)producers, ... NS_REQUIRES_NIL_TERMINATION; 32 | + (OCAProducer *)allTrue:(OCAProducer *)producers, ... NS_REQUIRES_NIL_TERMINATION; 33 | + (OCAProducer *)anyTrue:(OCAProducer *)producers, ... NS_REQUIRES_NIL_TERMINATION; 34 | 35 | #pragma mark Attributes of Hub 36 | 37 | @property (atomic, readonly, assign) OCAHubType type; 38 | @property (atomic, readonly, copy) NSArray *producers; //TODO: Make mutable 39 | 40 | 41 | 42 | @end 43 | 44 | 45 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCAInterpolator.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInterpolator.h 3 | // Objective-Chain 4 | // 5 | // Created by Juraj Homola on 24.2.2014. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAProducer.h" 10 | #import "OCAMath.h" 11 | 12 | 13 | 14 | @interface OCAInterpolator : OCAProducer 15 | 16 | 17 | /// Designated initializer 18 | - (instancetype)initWithDuration:(NSTimeInterval)duration 19 | frequency:(NSInteger)frequency 20 | fromValue:(OCAReal)fromValue 21 | toValue:(OCAReal)toValue; 22 | 23 | 24 | /// Interpolates between 0 and 1. 25 | + (instancetype)interpolatorWithDuration:(NSTimeInterval)duration frequency:(NSInteger)frequency; 26 | 27 | 28 | /// Interpolates between specific values. 29 | + (instancetype)interpolatorWithDuration:(NSTimeInterval)duration 30 | frequency:(NSInteger)frequency 31 | fromValue:(OCAReal)fromValue 32 | toValue:(OCAReal)toValue; 33 | 34 | 35 | /// Duration for which the interpolator is producing it's respective values. Must be non-negative. 36 | @property (nonatomic, readonly, assign) NSTimeInterval duration; 37 | 38 | /// Indicates how many values are produced per one second. Must be non-negative. 39 | @property (nonatomic, readonly, assign) NSInteger frequency; 40 | 41 | /// Value that is interpolated from. 42 | @property (nonatomic, readonly, assign) OCAReal fromValue; 43 | 44 | /// Value that is interpolated to. 45 | @property (nonatomic, readonly, assign) OCAReal toValue; 46 | 47 | 48 | /// Stops the interpolator and if the BOOL is YES - produces the last value. 49 | - (void)finishWithLastValue:(BOOL)withLastValue; 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCAInterpolator.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInterpolator.m 3 | // Objective-Chain 4 | // 5 | // Created by Juraj Homola on 24.2.2014. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAInterpolator.h" 10 | #import "OCAProducer+Subclass.h" 11 | #import "OCATimer.h" 12 | 13 | 14 | @interface OCAInterpolator () 15 | 16 | 17 | @property (nonatomic, readwrite, strong) OCATimer *timer; 18 | @property (nonatomic, readwrite, strong) NSDate *initialDate; 19 | 20 | 21 | @end 22 | 23 | 24 | 25 | 26 | 27 | @implementation OCAInterpolator 28 | 29 | 30 | - (instancetype)init { 31 | return [self initWithDuration:0 frequency:1 fromValue:0 toValue:1]; 32 | } 33 | 34 | - (instancetype)initWithDuration:(NSTimeInterval)duration 35 | frequency:(NSInteger)frequency 36 | fromValue:(OCAReal)fromValue 37 | toValue:(OCAReal)toValue { 38 | self = [super initWithValueClass:[NSNumber class]]; 39 | if (self) { 40 | 41 | OCAAssert(frequency >= 0, @"Interpolator's frequency must be a non-negative value") return nil; 42 | OCAAssert(duration >= 0, @"Interpolator's duration must be a non-negative value") return nil; 43 | 44 | self->_duration = duration; 45 | self->_frequency = frequency; 46 | self->_fromValue = fromValue; 47 | self->_toValue = toValue; 48 | 49 | self.initialDate = [NSDate date]; 50 | 51 | [self start]; 52 | } 53 | return self; 54 | } 55 | 56 | 57 | + (instancetype)interpolatorWithDuration:(NSTimeInterval)duration frequency:(NSInteger)frequency { 58 | return [[self alloc] initWithDuration:duration frequency:frequency fromValue:0.0 toValue:1.0]; 59 | } 60 | 61 | + (instancetype)interpolatorWithDuration:(NSTimeInterval)duration 62 | frequency:(NSInteger)frequency 63 | fromValue:(OCAReal)fromValue 64 | toValue:(OCAReal)toValue { 65 | return [[self alloc] initWithDuration:duration frequency:frequency fromValue:fromValue toValue:toValue]; 66 | } 67 | 68 | 69 | 70 | - (void)start { 71 | 72 | if (self.duration <= 0 || (self.fromValue == self.toValue)) { 73 | [self finishWithLastValue:YES]; 74 | return; 75 | } 76 | 77 | 78 | BOOL isFrequencyZero = (self.frequency == 0); 79 | 80 | [self produceValue:@(self.fromValue)]; 81 | 82 | NSDate *untilDate = [self.initialDate dateByAddingTimeInterval:self.duration]; 83 | if (isFrequencyZero) { 84 | self.timer = [OCATimer timerForDate:untilDate]; 85 | } else { 86 | self.timer = [OCATimer timerWithInterval:MIN(1.0/self.frequency, self.duration) untilDate:untilDate]; 87 | } 88 | 89 | OCAWeakify(self); 90 | [self.timer subscribeForClass:[NSNumber class] handler:^(NSNumber *lastFireInterval) { 91 | OCAStrongify(self); 92 | 93 | if ( ! isFrequencyZero) { 94 | 95 | OCAReal progress = -self.initialDate.timeIntervalSinceNow / self.duration; 96 | 97 | OCAReal producedValue = self.fromValue + (self.toValue - self.fromValue) * progress; 98 | 99 | [self produceValue:@(producedValue)]; 100 | } 101 | 102 | } finish:^(NSError *error) { 103 | [self finishWithLastValue:YES]; 104 | }]; 105 | 106 | } 107 | 108 | - (void)finishWithLastValue:(BOOL)withLastValue { 109 | [self.timer stop]; 110 | self.timer = nil; 111 | if (withLastValue) { 112 | [self produceValue:@(self.toValue)]; 113 | } 114 | [self finishProducingWithError:nil]; 115 | } 116 | 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCANotificator.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCANotificator.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 8.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAProducer.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCANotificator : OCAProducer 16 | 17 | 18 | 19 | #pragma mark Creating Notifier 20 | 21 | - (instancetype)initWithCenter:(NSNotificationCenter *)center name:(NSString *)name sender:(id)sender; 22 | 23 | @property (atomic, readonly, weak) NSNotificationCenter *notificationCenter; 24 | @property (atomic, readonly, copy) NSString *notificationName; 25 | @property (atomic, readonly, weak) id notificationSender; 26 | 27 | 28 | #pragma mark Receiving Notifications 29 | 30 | + (instancetype)notify:(NSString *)name; 31 | + (instancetype)notify:(NSString *)name from:(id)sender; 32 | + (OCAProducer *)notify:(NSString *)name from:(id)sender transform:(NSValueTransformer *)transformer; 33 | + (OCAProducer *)mergedNotify:(NSString *)name from:(NSArray *)senders; 34 | 35 | 36 | #pragma mark Notification Transformations 37 | 38 | - (OCAProducer *)produceName CONVENIENCE; 39 | - (OCAProducer *)produceSender CONVENIENCE; 40 | - (OCAProducer *)produceUserInfo CONVENIENCE; 41 | - (OCAProducer *)produceUserInfoForKey:(NSString *)key CONVENIENCE; 42 | 43 | 44 | #pragma mark Posting Notifications 45 | 46 | //TODO: Move to OCAConsumer class 47 | + (id)postNotification:(NSString *)name; 48 | + (id)postNotification:(NSString *)name sender:(NSObject *)object; 49 | + (id)postNotification:(NSString *)name sender:(NSObject *)object userInfoKey:(id)key; 50 | 51 | 52 | 53 | @end 54 | 55 | 56 | -------------------------------------------------------------------------------- /Sources/Foundation/Producers/OCAProducer+Subclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAProducer+Subclass.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAProducer.h" 10 | 11 | 12 | 13 | 14 | 15 | /*! Interface to be used by subclasses. Before you decide to subclass Producer, you may consider to use Command subclass. 16 | * Every Producer must manage its own lifetime, so it should live and produce values until it makes sense. For example, a Timer produces values until the end date. Property produces until its observed object deallocates. 17 | * To hook onto another object (a.k.a. owner), use Decomposer class. 18 | */ 19 | @interface OCAProducer () 20 | 21 | 22 | 23 | #pragma mark Creating Producer 24 | 25 | //! Designated initializer. Your custom public initializer can provide a class that you are planning to produce. All produced values are validated to be of this class. May be nil, which means any values pass the validation. 26 | - (instancetype)initWithValueClass:(Class)valueClass; 27 | 28 | 29 | #pragma mark Managing Connections 30 | 31 | //! Your subclass can provide a replacement for added consumer. Pass the original consumer or create Mediator, for example, to transform produced values. 32 | - (id)replacementConsumerForConsumer:(id)consumer; 33 | 34 | //! Empty. Called before the consumer is added, but after -replacementConsumerForConsumer: is called. 35 | - (void)willAddConsumer:(id )consumer; 36 | 37 | //! If the receiver already finished, it calls -finishConsumingWithError: on the Consumer, otherwise it re-sends the latest produced value. 38 | - (void)didAddConsumer:(id )consumer; 39 | 40 | //! Empty. 41 | - (void)willRemoveConsumer:(id )consumer; 42 | 43 | //! Empty. 44 | - (void)didRemoveConsumer:(id )consumer; 45 | 46 | 47 | 48 | #pragma mark Lifetime of Producer 49 | 50 | //! Core method. Call this from your subclass with values you want to produce. Values are validated and sent to all Consumers. If the Producer already finished, nothing is sent. 51 | - (void)produceValue:(id)value NS_REQUIRES_SUPER; 52 | 53 | //! Core method. Call this from your subclass once you plan to send no more values. Marks the Producer as finished, sends the error to consumers and removes them. Usually after finishing, the Producer is no longer needed and may be deallocated. This method is automatically called before deallocation. 54 | - (void)finishProducingWithError:(NSError *)error NS_REQUIRES_SUPER; 55 | 56 | //! Readwrite acces to the laste produced value. You usually don't need to do any changes to it. 57 | @property (atomic, readwrite, strong) id lastValue; 58 | 59 | //! Called from -prodcuceValue:. Default implementation validated class of the value to the valueClass property. Override if you need other validation. 60 | - (BOOL)validateProducedValue:(id)value; 61 | 62 | 63 | 64 | #pragma mark Describing Producer 65 | 66 | - (NSString *)descriptionName; 67 | - (NSDictionary *)debugDescriptionValues; 68 | 69 | 70 | 71 | @end 72 | 73 | 74 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCAMath.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAMath.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 2.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCATransformer.h" 11 | #import 12 | 13 | 14 | 15 | typedef long OCAInteger; 16 | typedef double OCAReal; 17 | typedef OCAInteger(^OCAIntegerTransformBlock)(OCAInteger x); 18 | typedef OCAReal(^OCARealTransformBlock)(OCAReal x); 19 | 20 | 21 | 22 | 23 | 24 | @interface OCAMath : OCAObject 25 | 26 | 27 | 28 | #pragma mark Generic 29 | 30 | + (OCATransformer *)transform:(OCARealTransformBlock)transform; 31 | + (OCATransformer *)transform:(OCARealTransformBlock)transform reverse:(OCARealTransformBlock)reverse; 32 | 33 | + (OCATransformer *)function:(OCAReal(*)(OCAReal))function; 34 | + (OCATransformer *)function:(OCAReal(*)(OCAReal))function reverse:(OCAReal(*)(OCAReal))reverse; 35 | 36 | 37 | #pragma mark Basic 38 | 39 | + (OCATransformer *)add:(OCAReal)add; 40 | + (OCATransformer *)subtract:(OCAReal)substract; 41 | + (OCATransformer *)subtractFrom:(OCAReal)value; 42 | + (OCATransformer *)multiplyBy:(OCAReal)multiplier; 43 | + (OCATransformer *)divideBy:(OCAReal)divisor; 44 | + (OCATransformer *)modulus:(OCAInteger)value; 45 | + (OCATransformer *)absoluteValue; 46 | + (OCATransformer *)inversedValue; 47 | 48 | + (OCATransformer *)relativeBetween:(OCAReal)lower and:(OCAReal)upper; 49 | + (OCATransformer *)absoluteBetween:(OCAReal)lower and:(OCAReal)upper; 50 | 51 | 52 | #pragma mark Advanced 53 | 54 | + (OCATransformer *)powerBy:(OCAReal)value; 55 | + (OCATransformer *)rootOf:(OCAReal)value; 56 | + (OCATransformer *)exponentOf:(OCAReal)value; 57 | + (OCATransformer *)logarithmWithBase:(OCAReal)base; 58 | 59 | 60 | #pragma mark Rounding 61 | 62 | + (OCATransformer *)roundTo:(OCAReal)scale; 63 | + (OCATransformer *)floorTo:(OCAReal)scale; 64 | + (OCATransformer *)ceilTo:(OCAReal)scale; 65 | 66 | 67 | #pragma mark Limits 68 | 69 | + (OCATransformer *)maximumOf:(OCAReal)value; 70 | + (OCATransformer *)minimumOf:(OCAReal)value; 71 | + (OCATransformer *)clampFrom:(OCAReal)minimum to:(OCAReal)maximum; 72 | 73 | 74 | #pragma mark Trigonometry 75 | 76 | + (OCATransformer *)sine; 77 | + (OCATransformer *)cosine; 78 | + (OCATransformer *)tangent; 79 | + (OCATransformer *)arcSine; 80 | + (OCATransformer *)arcCosine; 81 | + (OCATransformer *)arcTangent; 82 | 83 | + (OCATransformer *)toDegrees; 84 | + (OCATransformer *)toRadians; 85 | 86 | 87 | #pragma mark Array 88 | 89 | + (OCATransformer *)sum; 90 | + (OCATransformer *)average; 91 | + (OCATransformer *)minimum; 92 | + (OCATransformer *)maximum; 93 | 94 | 95 | #pragma mark Random 96 | 97 | + (OCATransformer *)randomUpTo:(NSUInteger)bound; 98 | 99 | 100 | #pragma mark Logic 101 | 102 | + (OCATransformer *)allTrue; 103 | + (OCATransformer *)anyTrue; 104 | 105 | 106 | 107 | @end 108 | 109 | 110 | 111 | 112 | 113 | #pragma mark Functions 114 | 115 | extern OCAReal OCALogarithm(OCAReal x, OCAReal base); 116 | extern OCAReal OCARound(OCAReal x, OCAReal scale); 117 | extern OCAReal OCACeil(OCAReal x, OCAReal scale); 118 | extern OCAReal OCAFloor(OCAReal x, OCAReal scale); 119 | extern OCAReal OCAClamp(OCAReal lower, OCAReal value, OCAReal upper); 120 | 121 | 122 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer+Base.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+Base.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAObject.h" 11 | 12 | 13 | 14 | typedef id (^OCATransformerBlock)(id input); 15 | 16 | 17 | 18 | 19 | 20 | @interface NSValueTransformer (OCATransformerCompatibility) 21 | 22 | + (Class)valueClass; 23 | 24 | - (NSValueTransformer *)reversed; 25 | 26 | @end 27 | 28 | 29 | 30 | 31 | 32 | @interface OCATransformer : NSValueTransformer 33 | 34 | 35 | 36 | #pragma mark Class Info 37 | 38 | + (Class)valueClass; 39 | + (Class)transformedValueClass; 40 | + (BOOL)allowsReverseTransformation; 41 | 42 | 43 | 44 | #pragma mark Transformation 45 | 46 | - (id)transformedValue:(id)value; 47 | - (id)reverseTransformedValue:(id)value; 48 | 49 | 50 | 51 | #pragma mark Describing 52 | 53 | @property (nonatomic, readonly, copy) NSString *reverseDescription; 54 | - (instancetype)describe:(NSString *)description; 55 | - (instancetype)describe:(NSString *)description reverse:(NSString *)reverseDescription; 56 | 57 | 58 | 59 | #pragma mark Deriving Transformers 60 | 61 | - (OCATransformer *)reversed; 62 | - (OCATransformer *)specializeFromClass:(Class)fromClass toClass:(Class)toClass; 63 | 64 | 65 | 66 | #pragma mark Customizing Using Blocks 67 | 68 | + (Class)subclassForInputClass:(Class)inputClass outputClass:(Class)outputClass reversible:(BOOL)isReversible; 69 | - (OCATransformer *)initWithBlock:(OCATransformerBlock)block reverseBlock:(OCATransformerBlock)reverseBlock; 70 | 71 | + (OCATransformer *)fromClass:(Class)fromClass toClass:(Class)toClass symetric:(OCATransformerBlock)symetric; 72 | + (OCATransformer *)fromClass:(Class)fromClass toClass:(Class)toClass asymetric:(OCATransformerBlock)asymetric; 73 | + (OCATransformer *)fromClass:(Class)fromClass toClass:(Class)toClass transform:(OCATransformerBlock)transform reverse:(OCATransformerBlock)reverse; 74 | 75 | + (OCATransformer *)there:(NSValueTransformer *)forward back:(NSValueTransformer *)backward; 76 | 77 | 78 | 79 | @end 80 | 81 | 82 | 83 | 84 | 85 | extern OCATransformerBlock const OCATransformationDiscard; 86 | extern OCATransformerBlock const OCATransformationPass; 87 | 88 | 89 | 90 | 91 | 92 | @interface NSObject (NSValueTransformer) 93 | 94 | - (id)transform:(NSValueTransformer *)transformer __deprecated; 95 | 96 | @end 97 | 98 | 99 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer+Collections.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+Collections.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCATransformer+Base.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCATransformer (Collections) 16 | 17 | 18 | 19 | + (OCATransformer *)countCollection; 20 | + (OCATransformer *)objectForKey:(id)key; 21 | 22 | 23 | 24 | 25 | #pragma mark - 26 | #pragma mark Array 27 | #pragma mark - 28 | 29 | 30 | #pragma mark Creating Array 31 | 32 | + (OCATransformer *)branchArray:(NSArray *)transformers; 33 | + (OCATransformer *)wrapInArray; 34 | + (OCATransformer *)arrayFromFile; 35 | 36 | 37 | #pragma mark Getting Subarray 38 | 39 | + (OCATransformer *)objectsAtIndexes:(NSIndexSet *)indexes; 40 | + (OCATransformer *)subarrayToIndex:(NSInteger)index; 41 | + (OCATransformer *)subarrayFromIndex:(NSInteger)index; 42 | + (OCATransformer *)subarrayWithRange:(NSRange)range; 43 | 44 | 45 | #pragma mark Altering Array 46 | 47 | + (OCATransformer *)transformArray:(NSValueTransformer *)transformer; 48 | + (OCATransformer *)filterArray:(NSPredicate *)predicate; 49 | + (OCATransformer *)sortArray:(NSArray *)sortDescriptors; 50 | + (OCATransformer *)flattenArrayRecursively:(BOOL)recursively; 51 | + (OCATransformer *)randomizeArray; 52 | + (OCATransformer *)removeNullsFromArray; 53 | + (OCATransformer *)mutateArray:(void(^)(NSMutableArray *array))block; 54 | 55 | 56 | #pragma mark Disposing Array 57 | 58 | + (OCATransformer *)objectAtIndex:(NSInteger)index; 59 | + (OCATransformer *)joinWithString:(NSString *)string; 60 | + (OCATransformer *)joinWithAttributedString:(NSAttributedString *)attrString; 61 | + (OCATransformer *)joinWithString:(NSString *)string last:(NSString *)lastString; 62 | 63 | + (OCATransformer *)objectFromArray:(NSArray *)objects; 64 | + (OCATransformer *)indexInArray:(NSArray *)objects; 65 | 66 | 67 | 68 | #pragma mark - 69 | #pragma mark Dictionary 70 | #pragma mark - 71 | 72 | 73 | #pragma mark Creating Dictionary 74 | 75 | + (OCATransformer *)dictionaryFromFile; 76 | + (OCATransformer *)mappedArray:(NSValueTransformer *)transformer; 77 | + (OCATransformer *)keyedArray:(NSArray *)keys; 78 | + (OCATransformer *)mergeDictionaries; 79 | 80 | 81 | #pragma mark Altering Dictionary 82 | 83 | + (OCATransformer *)filteredDictionary:(NSPredicate *)predicate; 84 | + (OCATransformer *)transformValues:(NSValueTransformer *)transformer; 85 | + (OCATransformer *)mutateDictionary:(void(^)(NSMutableDictionary *dictionary))block; 86 | 87 | 88 | #pragma mark Disposing Dictionary 89 | 90 | + (OCATransformer *)joinPairs:(NSString *)string; 91 | + (OCATransformer *)keysForValue:(id)value; 92 | 93 | 94 | 95 | #pragma mark - 96 | #pragma mark Index Set 97 | #pragma mark - 98 | 99 | 100 | #pragma mark Creating Index Set 101 | 102 | + (OCATransformer *)indexSetFromArray; 103 | + (OCATransformer *)wrapIndex; 104 | + (OCATransformer *)wrapRange; 105 | 106 | 107 | #pragma mark Altering Index Set 108 | 109 | + (OCATransformer *)mutateIndexSet:(void(^)(NSMutableIndexSet *indexSet))block; 110 | 111 | 112 | #pragma mark Disposing Index Set 113 | 114 | + (OCATransformer *)lowestIndex; 115 | + (OCATransformer *)highestIndex; 116 | 117 | 118 | 119 | #pragma mark - 120 | #pragma mark Index Path 121 | #pragma mark - 122 | 123 | 124 | + (OCATransformer *)indexPathFromArray; 125 | + (OCATransformer *)indexPathInSection:(NSUInteger)section; 126 | 127 | 128 | 129 | @end 130 | 131 | 132 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer+Core.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+Core.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 31.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCATransformer+Base.h" 10 | #import "OCAKeyPathAccessor.h" 11 | #import "OCAStructureAccessor.h" 12 | 13 | 14 | 15 | 16 | 17 | @interface OCATransformer (Core) 18 | 19 | 20 | 21 | 22 | 23 | #pragma mark Basic 24 | 25 | + (OCATransformer *)pass; 26 | + (OCATransformer *)discard; 27 | + (OCATransformer *)replaceWith:(id)replacement; 28 | 29 | 30 | 31 | #pragma mark Conditions 32 | 33 | + (OCATransformer *)if:(NSPredicate *)predicate then:(NSValueTransformer *)thenTransformer else:(NSValueTransformer *)elseTransformer; 34 | + (OCATransformer *)passesPredicate:(NSPredicate *)predicate or:(id)replacement; 35 | + (OCATransformer *)replaceNil:(id)replacement; 36 | + (OCATransformer *)kindOfClass:(Class)theClass or:(id)replacement; 37 | + (OCATransformer *)ifYes:(id)yesReplacement ifNo:(id)noReplacement; 38 | 39 | 40 | 41 | #pragma mark Boolean 42 | 43 | + (OCATransformer *)evaluatePredicate:(NSPredicate *)predicate; 44 | + (OCATransformer *)negateBoolean; 45 | 46 | 47 | 48 | #pragma mark Accessors & Other 49 | 50 | + (OCATransformer *)access:(OCAAccessor *)accessor; 51 | + (OCATransformer *)modify:(OCAAccessor *)accessor value:(id)value; 52 | + (OCATransformer *)modify:(OCAAccessor *)accessor transformer:(NSValueTransformer *)transformer; 53 | + (OCATransformer *)evaluateExpression:(NSExpression *)expression; 54 | + (OCATransformer *)map:(NSDictionary *)dictionary; 55 | + (OCATransformer *)replace:(NSDictionary *)dictionary; 56 | + (OCATransformer *)makeCopy; 57 | 58 | 59 | 60 | #pragma mark Control Flow 61 | 62 | + (OCATransformer *)sequence:(NSArray *)transformers; 63 | + (OCATransformer *)convertTo:(Class)finalClass using:(NSArray *)transformers; 64 | + (OCATransformer *)repeat:(NSUInteger)count transformer:(NSValueTransformer *)transformer; 65 | 66 | 67 | 68 | #pragma mark Side Effects 69 | 70 | + (OCATransformer *)sideEffect:(void(^)(id value))block; 71 | + (OCATransformer *)debugPrintWithPrefix:(NSString *)prefix; 72 | 73 | 74 | 75 | 76 | 77 | @end 78 | 79 | 80 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer+NSData.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+NSData.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCATransformer+Base.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCATransformer (NSData) 16 | 17 | 18 | 19 | #pragma mark - 20 | #pragma mark Data 21 | #pragma mark - 22 | 23 | #pragma mark Creating Data 24 | 25 | + (OCATransformer *)dataFromFile; 26 | + (OCATransformer *)dataFromString; //! UTF-8 27 | + (OCATransformer *)subdataWithRange:(NSRange)range; 28 | 29 | 30 | #pragma mark Keyed Archivation 31 | 32 | + (OCATransformer *)archiveBinary:(BOOL)binary; 33 | + (OCATransformer *)unarchive; 34 | 35 | 36 | #pragma mark Property List Serialization 37 | 38 | + (OCATransformer *)serializePropertyListBinary:(BOOL)binary; 39 | + (OCATransformer *)deserializePropertyListMutable:(BOOL)isMutable; 40 | 41 | 42 | #pragma mark JSON Serialization 43 | 44 | + (OCATransformer *)serializeJSONPretty:(BOOL)pretty; 45 | + (OCATransformer *)deserializeJSONMutable:(BOOL)isMutable; 46 | 47 | 48 | #pragma mark Base64 Encoding 49 | 50 | + (OCATransformer *)decodeBase64Data; 51 | + (OCATransformer *)encodeBase64Data; 52 | 53 | 54 | #pragma mark Hexadecimal String 55 | 56 | + (OCATransformer *)dataFromHex; 57 | + (OCATransformer *)hexFromData; 58 | 59 | 60 | 61 | #pragma mark - 62 | #pragma mark UUID 63 | #pragma mark - 64 | 65 | #pragma mark Creating UUID 66 | 67 | + (OCATransformer *)UUIDFromData; 68 | + (OCATransformer *)UUIDFromString; 69 | 70 | #pragma mark Disposing UUID 71 | 72 | + (OCATransformer *)dataFromUUID; 73 | + (OCATransformer *)stringFromUUID; 74 | 75 | 76 | 77 | @end 78 | 79 | 80 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer+NSDate.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+NSDate.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCATransformer+Base.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCATransformer (NSDate) 16 | 17 | 18 | 19 | #pragma mark - 20 | #pragma mark Date 21 | #pragma mark - 22 | 23 | 24 | + (OCATransformer *)replaceWithCurrentDate; 25 | 26 | 27 | #pragma mark Working with Time Intervals 28 | 29 | + (OCATransformer *)dateFromTimeIntervalSinceUNIX:(BOOL)unix; 30 | + (OCATransformer *)timeIntervalSinceUNIX:(BOOL)unix; 31 | + (OCATransformer *)addTimeInterval:(NSTimeInterval)interval; 32 | + (OCATransformer *)timeIntervalSinceDate:(NSDate *)otherDate; 33 | + (OCATransformer *)timeIntervalToDate:(NSDate *)otherDate; 34 | + (OCATransformer *)timeIntervalSinceMidnight; 35 | 36 | 37 | #pragma mark Parsing Dates 38 | 39 | + (OCATransformer *)dateWithFormatter:(NSDateFormatter *)formatter; 40 | + (OCATransformer *)dateFromStringFormat:(NSString *)dateFormat; 41 | 42 | 43 | #pragma mark Formatting Dates 44 | 45 | + (OCATransformer *)stringWithDateFormatter:(NSDateFormatter *)formatter; 46 | + (OCATransformer *)stringWithDateFormat:(NSString *)dateFormat; 47 | + (OCATransformer *)stringWithDateStyle:(NSDateFormatterStyle)dateStyle timeStyle:(NSDateFormatterStyle)timeStyle; 48 | + (OCATransformer *)stringWithDateStyle:(NSDateFormatterStyle)dateStyle timeStyle:(NSDateFormatterStyle)timeStyle relative:(BOOL)doesRelative; 49 | 50 | 51 | #pragma mark Limiting Dates 52 | 53 | + (OCATransformer *)earlierDate:(NSDate *)otherDate; 54 | + (OCATransformer *)laterDate:(NSDate *)otherDate; 55 | + (OCATransformer *)roundDateToUnit:(NSCalendarUnit)unit mode:(NSRoundingMode)mode; 56 | 57 | 58 | #pragma mark Date Components 59 | 60 | + (OCATransformer *)dateComponents:(NSCalendarUnit)units; 61 | + (OCATransformer *)dateComponents:(NSCalendarUnit)units sinceDate:(NSDate *)otherDate; 62 | + (OCATransformer *)dateComponentsSinceCurrentDate:(NSCalendarUnit)units; 63 | + (OCATransformer *)dateComponentsUntilCurrentDate:(NSCalendarUnit)units; 64 | + (OCATransformer *)addDateComponents:(NSDateComponents *)components; 65 | + (OCATransformer *)modifyDateComponents:(NSCalendarUnit)units block:(void(^)(NSDateComponents *components))block; 66 | + (OCATransformer *)dateComponent:(NSCalendarUnit)unit; 67 | + (OCATransformer *)nameFromWeekdayShort:(BOOL)shortWeekdays; 68 | + (OCATransformer *)nameFromMonthShort:(BOOL)shortMonths; 69 | 70 | 71 | 72 | @end 73 | 74 | 75 | 76 | 77 | 78 | 79 | extern NSCalendarUnit const OCACalendarUnitDefault; 80 | 81 | 82 | 83 | @interface NSDateComponents (OCATransformer) 84 | 85 | - (NSInteger)oca_valueForUnit:(NSCalendarUnit)unit; 86 | - (void)oca_setValue:(NSInteger)value forUnit:(NSCalendarUnit)unit; 87 | 88 | @end 89 | 90 | 91 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer+NSString.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+NSString.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCATransformer+Base.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCATransformer (NSString) 16 | 17 | 18 | 19 | #pragma mark - 20 | #pragma mark String 21 | #pragma mark - 22 | 23 | 24 | #pragma mark Creating String 25 | 26 | + (OCATransformer *)stringFromFile; 27 | + (OCATransformer *)stringFromData; 28 | + (OCATransformer *)formatString:(NSString *)format; 29 | 30 | 31 | #pragma mark Getting Substring 32 | 33 | + (OCATransformer *)substringToIndex:(NSInteger)index; 34 | + (OCATransformer *)substringFromIndex:(NSInteger)index; 35 | + (OCATransformer *)substringWithRange:(NSRange)range; 36 | 37 | 38 | #pragma mark Altering String 39 | 40 | + (OCATransformer *)appendFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); 41 | + (OCATransformer *)trimWhitespace; 42 | + (OCATransformer *)replaceString:(NSString *)find withString:(NSString *)replace; 43 | + (OCATransformer *)mutateString:(void(^)(NSMutableString *string))block; 44 | + (OCATransformer *)capitalizeString; 45 | + (OCATransformer *)uppercaseString; 46 | + (OCATransformer *)lowercaseString; 47 | 48 | 49 | #pragma mark Splitting String 50 | 51 | + (OCATransformer *)splitString:(NSString *)split; 52 | + (OCATransformer *)splitByWhitespace; 53 | 54 | 55 | 56 | #pragma mark - 57 | #pragma mark Attributed String 58 | #pragma mark - 59 | 60 | 61 | #pragma mark Creating Attributed String 62 | 63 | + (OCATransformer *)stringWithAttributes:(NSDictionary *)attributes; 64 | 65 | 66 | #pragma mark Changing Attributes 67 | 68 | + (OCATransformer *)addAttributes:(NSDictionary *)attributes; 69 | + (OCATransformer *)addAttributes:(NSDictionary *)attributes range:(NSRange)range; 70 | + (OCATransformer *)transformAttribute:(NSString *)attribute transformer:(NSValueTransformer *)transformer; 71 | 72 | #pragma mark Altering Attributed String 73 | 74 | + (OCATransformer *)appendAttributedString:(NSAttributedString *)attributedString; 75 | + (OCATransformer *)attributedSubstringInRange:(NSRange)range; 76 | + (OCATransformer *)mutateAttributedString:(void(^)(NSMutableAttributedString *attributedString))block; 77 | 78 | 79 | 80 | #pragma mark - 81 | #pragma mark Number 82 | #pragma mark - 83 | 84 | 85 | #pragma mark Format Numbers 86 | 87 | + (OCATransformer *)stringWithNumberStyle:(NSNumberFormatterStyle)style fractionDigits:(NSUInteger)fractionalDigits; 88 | + (OCATransformer *)stringWithNumberFormatter:(NSNumberFormatter *)formatter; 89 | + (OCATransformer *)stringFromNumber; 90 | + (OCATransformer *)stringFromCountWithZero:(NSString *)zeroSuffix one:(NSString *)oneSuffix few:(NSString *)fewSuffix many:(NSString *)manySuffix; 91 | 92 | #pragma mark Format Byte Count 93 | 94 | + (OCATransformer *)stringWithMemoryByteCount; 95 | + (OCATransformer *)stringWithFileByteCount; 96 | + (OCATransformer *)stringWithByteCountFormatter:(NSByteCountFormatter *)formatter; 97 | 98 | 99 | #pragma mark Parse Numbers 100 | 101 | + (OCATransformer *)numberWithFormatter:(NSNumberFormatter *)formatter; 102 | + (OCATransformer *)numberFromString; 103 | 104 | 105 | #pragma mark - 106 | #pragma mark URL 107 | #pragma mark - 108 | 109 | 110 | #pragma mark Creating URL 111 | 112 | + (OCATransformer *)URLFromString; 113 | + (OCATransformer *)URLFromPath; 114 | + (OCATransformer *)URLWithBaseURL:(NSURL *)base; 115 | 116 | #pragma mark URL Components 117 | 118 | + (OCATransformer *)componentsOfURL; 119 | + (OCATransformer *)modifyURLComponents:(void(^)(NSURLComponents *components))block; 120 | 121 | 122 | 123 | @end 124 | 125 | 126 | -------------------------------------------------------------------------------- /Sources/Foundation/Transformers/OCATransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 31.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | 11 | #import "OCATransformer+Base.h" 12 | #import "OCATransformer+Core.h" 13 | #import "OCAMath.h" 14 | #import "OCATransformer+Collections.h" 15 | #import "OCATransformer+NSData.h" 16 | #import "OCATransformer+NSDate.h" 17 | #import "OCATransformer+NSString.h" 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/Accessors/OCAAccessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAAccessor.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 8.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCAAccessor : OCAObject 16 | 17 | 18 | 19 | #pragma mark Using Accessor 20 | 21 | - (id)accessObject:(id)object; 22 | - (id)modifyObject:(id)object withValue:(id)value; 23 | 24 | 25 | #pragma mark Class Validation of Accessor 26 | 27 | - (Class)objectClass; 28 | - (Class)valueClass; 29 | 30 | 31 | 32 | @end 33 | 34 | 35 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/Accessors/OCAAccessor.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAAccessor.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 8.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAAccessor.h" 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @implementation OCAAccessor 21 | 22 | 23 | 24 | 25 | 26 | #pragma mark Using Accessor 27 | 28 | 29 | - (id)accessObject:(id)object { 30 | return object; 31 | } 32 | 33 | 34 | - (id)modifyObject:(id)object withValue:(id)value { 35 | return object; 36 | } 37 | 38 | 39 | 40 | 41 | 42 | #pragma mark Clas Validation 43 | 44 | 45 | - (Class)objectClass { 46 | return nil; 47 | } 48 | 49 | 50 | - (Class)valueClass { 51 | return nil; 52 | } 53 | 54 | 55 | 56 | 57 | 58 | @end 59 | 60 | 61 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/Accessors/OCAIndexAccessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAIndexAccessor.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 17.4.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAAccessor.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCAIndexAccessor : OCAAccessor 16 | 17 | 18 | #define OCAIndex(index) ( [[OCAIndexAccessor alloc] initWithIndex:index] ) 19 | 20 | //! Uses so can be negative and can be out of range. 21 | - (instancetype)initWithIndex:(NSInteger)index; 22 | @property (nonatomic, readonly, assign) NSInteger index; 23 | 24 | 25 | @end 26 | 27 | 28 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/Accessors/OCAIndexAccessor.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAIndexAccessor.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 17.4.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAIndexAccessor.h" 10 | #import "NSArray+Ordinals.h" 11 | 12 | 13 | 14 | 15 | 16 | @implementation OCAIndexAccessor 17 | 18 | 19 | 20 | 21 | 22 | - (instancetype)initWithIndex:(NSInteger)index { 23 | self = [super init]; 24 | if (self) { 25 | self->_index = index; 26 | } 27 | return self; 28 | } 29 | 30 | 31 | - (Class)objectClass { 32 | return [NSArray class]; 33 | } 34 | 35 | 36 | 37 | 38 | 39 | - (id)accessObject:(NSArray *)array { 40 | return [array oca_valueAtIndex:self.index]; 41 | } 42 | 43 | 44 | - (id)modifyObject:(NSArray *)array withValue:(id)value { 45 | NSMutableArray *mutable = [array mutableCopy]; 46 | NSUInteger count = array.count; 47 | NSInteger index = self.index; 48 | 49 | NSInteger realIndex = (index >= 0? index : count + index); 50 | if (realIndex < 0) return array; 51 | if (realIndex >= count) return array; 52 | 53 | [mutable replaceObjectAtIndex:index withObject:(value ?: NSNull.null)]; 54 | return mutable; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | @end 62 | 63 | 64 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/NSArray+Ordinals.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Ordinals.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | 14 | 15 | @interface NSArray (Ordinals) 16 | 17 | 18 | - (id)oca_valueAtIndex:(NSInteger)index; 19 | 20 | 21 | @end 22 | 23 | 24 | 25 | 26 | 27 | extern NSUInteger OCANormalizeIndex(NSInteger index, NSUInteger length); 28 | extern NSRange OCANormalizeRange(NSRange range, NSUInteger length); 29 | 30 | 31 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/NSArray+Ordinals.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Ordinals.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 10.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Ordinals.h" 10 | 11 | 12 | 13 | 14 | 15 | @implementation NSArray (Ordinals) 16 | 17 | 18 | 19 | 20 | 21 | - (id)oca_valueAtIndex:(NSInteger)index { 22 | NSInteger realIndex = (index >= 0? index : self.count + index); 23 | if (realIndex < 0) return nil; 24 | if (realIndex >= self.count) return nil; 25 | 26 | id value = [self objectAtIndex:realIndex]; 27 | 28 | if (value == NSNull.null) return nil; 29 | return value; 30 | } 31 | 32 | 33 | 34 | 35 | 36 | @end 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | NSUInteger OCANormalizeIndex(NSInteger index, NSUInteger length) { 48 | 49 | if (index < 0) 50 | index = length + index; 51 | 52 | if (index < 0) 53 | return 0; 54 | 55 | if (index >= length) 56 | index = length; 57 | 58 | return index; 59 | } 60 | 61 | 62 | NSRange OCANormalizeRange(NSRange range, NSUInteger length) { 63 | 64 | if (range.location > length) 65 | range.location = length; 66 | 67 | NSUInteger end = length - range.location; 68 | 69 | if (range.length > end) 70 | range.length = end; 71 | 72 | return range; 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/NSValue+Boxing.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSValue+Boxing.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | #define OCABox(VALUE) \ 14 | (id)({ \ 15 | typeof(VALUE) v = (VALUE); \ 16 | [NSValue boxValue:&v objCType:@encode(typeof(v))]; \ 17 | }) \ 18 | 19 | 20 | 21 | #define OCAUnbox(VALUE, TYPE, REPLACEMENT) \ 22 | (TYPE)({ \ 23 | TYPE v; \ 24 | BOOL s = [(VALUE) unboxValue:&v objCType:@encode(TYPE)]; \ 25 | s? v : (REPLACEMENT); \ 26 | }) \ 27 | 28 | 29 | 30 | #define OCAUnboxPoint(VALUE) OCAUnbox(VALUE, CGPoint, CGPointZero) 31 | #define OCAUnboxSize(VALUE) OCAUnbox(VALUE, CGSize, CGSizeZero) 32 | #define OCAUnboxRect(VALUE) OCAUnbox(VALUE, CGRect, CGRectZero) 33 | 34 | 35 | 36 | @interface NSValue (Boxing) 37 | 38 | 39 | + (id)boxValue:(const void *)buffer objCType:(const char *)type; 40 | - (BOOL)unboxValue:(void *)buffer objCType:(const char *)type; 41 | 42 | + (BOOL)objCTypeIsNumeric:(const char *)type; 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/NSValue+Boxing.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSValue+Boxing.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "NSValue+Boxing.h" 10 | 11 | 12 | 13 | 14 | 15 | @implementation NSValue (Boxing) 16 | 17 | 18 | //TODO: Something better than macros. 19 | 20 | 21 | 22 | 23 | #define NSValueConditional(TYPE) if (strcmp(type, @encode(TYPE)) == 0) 24 | 25 | 26 | + (id)boxValue:(const void *)buffer objCType:(const char *)type { 27 | #define NSValueConditionalyBoxNumber(TYPE) NSValueConditional(TYPE) return @( * (TYPE *)buffer) 28 | 29 | NSValueConditional(void) return nil; 30 | 31 | NSValueConditionalyBoxNumber(char); 32 | NSValueConditionalyBoxNumber(short); 33 | NSValueConditionalyBoxNumber(int); 34 | NSValueConditionalyBoxNumber(long); 35 | NSValueConditionalyBoxNumber(long long); 36 | 37 | NSValueConditionalyBoxNumber(unsigned char); 38 | NSValueConditionalyBoxNumber(unsigned short); 39 | NSValueConditionalyBoxNumber(unsigned int); 40 | NSValueConditionalyBoxNumber(unsigned long); 41 | NSValueConditionalyBoxNumber(unsigned long long); 42 | 43 | NSValueConditionalyBoxNumber(float); 44 | NSValueConditionalyBoxNumber(double); 45 | 46 | NSValueConditionalyBoxNumber(_Bool); 47 | 48 | return [NSValue valueWithBytes:buffer objCType:type]; 49 | 50 | #undef NSValueConditionalyBoxNumber 51 | } 52 | 53 | 54 | - (BOOL)unboxValue:(void *)buffer objCType:(const char *)type { 55 | #define NSValueConditionalyUnboxNumber(TYPE, METHOD) \ 56 | NSValueConditional(TYPE) { \ 57 | if ([self isKindOfClass:NSNumber.class]) { \ 58 | * (TYPE *)buffer = [(NSNumber *)self METHOD]; \ 59 | return YES; \ 60 | } \ 61 | else return NO; \ 62 | } \ 63 | 64 | NSValueConditional(void) { 65 | return NO; 66 | } 67 | 68 | NSValueConditionalyUnboxNumber(char, charValue); 69 | NSValueConditionalyUnboxNumber(short, shortValue); 70 | NSValueConditionalyUnboxNumber(int, intValue); 71 | NSValueConditionalyUnboxNumber(long, longValue); 72 | NSValueConditionalyUnboxNumber(long long, longLongValue); 73 | 74 | NSValueConditionalyUnboxNumber(unsigned char, unsignedCharValue); 75 | NSValueConditionalyUnboxNumber(unsigned short, unsignedShortValue); 76 | NSValueConditionalyUnboxNumber(unsigned int, unsignedIntValue); 77 | NSValueConditionalyUnboxNumber(unsigned long, unsignedLongValue); 78 | NSValueConditionalyUnboxNumber(unsigned long long, unsignedLongLongValue); 79 | 80 | NSValueConditionalyUnboxNumber(float, floatValue); 81 | NSValueConditionalyUnboxNumber(double, doubleValue); 82 | 83 | NSValueConditionalyUnboxNumber(_Bool, boolValue); 84 | 85 | if (strcmp(type, self.objCType) == 0) { 86 | [self getValue:buffer]; 87 | return YES; 88 | } 89 | 90 | return NO; 91 | 92 | #undef NSValueConditionalyUnboxNumber 93 | } 94 | 95 | 96 | + (BOOL)objCTypeIsNumeric:(const char *)type { 97 | if ( ! type) return NO; 98 | 99 | NSValueConditional(char) return YES; 100 | NSValueConditional(short) return YES; 101 | NSValueConditional(int) return YES; 102 | NSValueConditional(long) return YES; 103 | NSValueConditional(long long) return YES; 104 | 105 | NSValueConditional(unsigned char) return YES; 106 | NSValueConditional(unsigned short) return YES; 107 | NSValueConditional(unsigned int) return YES; 108 | NSValueConditional(unsigned long) return YES; 109 | NSValueConditional(unsigned long long) return YES; 110 | 111 | NSValueConditional(float) return YES; 112 | NSValueConditional(double) return YES; 113 | 114 | NSValueConditional(_Bool) return YES; 115 | 116 | return NO; 117 | } 118 | 119 | 120 | #undef NSValueConditional 121 | 122 | 123 | 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCADebug.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCADebug.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAProducer.h" 11 | 12 | 13 | 14 | typedef void(^OCADebugBlock)(id); 15 | 16 | 17 | 18 | 19 | 20 | #define OCADebugStop ^(id value) { OCABreakpointStop(value); } 21 | extern void OCABreakpointStop(id value); 22 | 23 | 24 | #define OCADebugSound ^(id value) { OCABreakpointSound(value); } 25 | extern void OCABreakpointSound(id value); 26 | 27 | 28 | #define OCADebugLog(FLAG) ^(id value) { NSLog(@"%@: %@", @#FLAG, value); } 29 | 30 | 31 | 32 | 33 | 34 | @interface OCAProducer (OCADebug) 35 | 36 | 37 | - (OCAProducer *)debug:(OCADebugBlock)block; 38 | 39 | 40 | @end 41 | 42 | 43 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCADebug.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCADebug.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCADebug.h" 10 | #import "OCABridge.h" 11 | #import "OCATransformer+Core.h" 12 | 13 | 14 | 15 | 16 | 17 | extern void OCABreakpointStop(id value) { 18 | /* 19 | * This function is used for Symbolic Breakpoint. 20 | * If you stopped here, you can see the `value`, that was send from Producer to Consumer. 21 | * You can navigate up in the stack trace and you will see the the place where this happened. 22 | */ 23 | } 24 | 25 | 26 | extern void OCABreakpointSound(id value) { 27 | /* 28 | * This function is used for Symbolic Breakpoint. 29 | */ 30 | } 31 | 32 | 33 | 34 | 35 | 36 | @implementation OCAProducer (OCADebug) 37 | 38 | 39 | 40 | - (OCAProducer *)debug:(OCADebugBlock)block { 41 | OCABridge *debugBridge = [[OCABridge alloc] initWithTransformer:[OCATransformer sideEffect:block]]; 42 | [self addConsumer:debugBridge]; 43 | return debugBridge; 44 | } 45 | 46 | 47 | 48 | @end 49 | 50 | 51 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCADecomposer.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCADecomposer.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 8.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | 11 | 12 | 13 | typedef void(^OCADecomposerBlock)(__unsafe_unretained id owner); 14 | 15 | 16 | 17 | 18 | 19 | @interface OCADecomposer : OCAObject 20 | 21 | 22 | 23 | #pragma mark Managing Owned Objects 24 | 25 | - (void)addOwnedObject:(id)object cleanup:(OCADecomposerBlock)block; 26 | - (void)removeOwnedObject:(id)object; 27 | 28 | - (NSArray *)ownedObjects; 29 | - (id)findOwnedObjectOfClass:(Class)theClass usingBlock:(BOOL(^)(id ownedObject))filterBlock; 30 | 31 | 32 | 33 | @end 34 | 35 | 36 | 37 | 38 | 39 | @interface NSObject (OCADecomposer) 40 | 41 | 42 | - (OCADecomposer *)decomposer; 43 | 44 | 45 | @end 46 | 47 | 48 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAInvocationCatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInvocationCatcher.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 27.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | 14 | 15 | @interface OCAInvocationCatcher : NSProxy 16 | 17 | 18 | 19 | #define OCAInvocationCatch(TARGET, METHOD) \ 20 | (OCAInvocation *)({ \ 21 | OCAInvocationCatcher *catcher = [[OCAInvocationCatcher alloc] initWithTarget:(TARGET)]; \ 22 | [(typeof(TARGET))catcher METHOD]; \ 23 | [catcher.invocation retainArguments]; /* This is the reason why you cannot use this invocation with OCAInvoker. */ \ 24 | catcher.invocation; \ 25 | }) \ 26 | 27 | 28 | 29 | - (instancetype)initWithTarget:(id)target; 30 | @property (nonatomic, readonly, strong) id target; 31 | 32 | 33 | - (void)forwardInvocation:(NSInvocation *)invocation; 34 | @property (nonatomic, readonly, strong) NSInvocation *invocation; 35 | 36 | //! Argument are retained by this class, not relying on NSInvocation retain. 37 | @property (nonatomic, readonly, strong) NSArray *retainedArguments; 38 | 39 | 40 | 41 | @end 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | @interface NSInvocation (ObjectArguments) 50 | 51 | 52 | - (void)oca_enumerateObjectArgumentsUsingBlock:(void(^)(NSUInteger index, id argument))block; 53 | 54 | 55 | @end 56 | 57 | 58 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAInvocationCatcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInvocationCatcher.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 27.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAInvocationCatcher.h" 10 | 11 | 12 | 13 | 14 | 15 | @implementation OCAInvocationCatcher 16 | 17 | 18 | 19 | 20 | 21 | - (instancetype)initWithTarget:(id)target { 22 | // NSProxy doesn't have -init 23 | self->_target = target; 24 | return self; 25 | } 26 | 27 | 28 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 29 | return [self.target methodSignatureForSelector:selector]; 30 | } 31 | 32 | 33 | - (void)forwardInvocation:(NSInvocation *)invocation { 34 | [invocation invokeWithTarget:(__nonnull id)nil]; // Ah. 35 | invocation.target = self.target; 36 | self->_invocation = invocation; 37 | 38 | [self retainArguments]; //! Because NSInvocation is fucked up. 39 | } 40 | 41 | 42 | - (void)retainArguments { 43 | NSMutableArray *retainedArguments = [[NSMutableArray alloc] init]; 44 | [self.invocation oca_enumerateObjectArgumentsUsingBlock:^(NSUInteger index, id argument) { 45 | if (argument) { 46 | [retainedArguments addObject:argument]; 47 | } 48 | }]; 49 | self->_retainedArguments = retainedArguments; 50 | } 51 | 52 | 53 | 54 | 55 | 56 | @end 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | @implementation NSInvocation (ObjectArguments) 67 | 68 | 69 | 70 | - (void)oca_enumerateObjectArgumentsUsingBlock:(void(^)(NSUInteger index, id argument))block { 71 | NSUInteger count = self.methodSignature.numberOfArguments; 72 | for (NSUInteger index = 0; index < count; index++) { 73 | 74 | const char *cType = [self.methodSignature getArgumentTypeAtIndex:index]; 75 | if ([@(cType) isEqualToString:@(@encode(id))]) { 76 | // Only objects, including target. 77 | 78 | __unsafe_unretained id argument = nil; 79 | [self getArgument:&argument atIndex:index]; 80 | 81 | block(index, argument); 82 | } 83 | } 84 | } 85 | 86 | 87 | 88 | @end 89 | 90 | 91 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAKeyValueChange.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAKeyValueChange.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 29.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCAStructureAccessor.h" 11 | @class OCAKeyValueChangeSetting; 12 | @class OCAKeyValueChangeInsertion; 13 | @class OCAKeyValueChangeRemoval; 14 | @class OCAKeyValueChangeReplacement; 15 | @class OCAProperty; 16 | 17 | 18 | 19 | 20 | 21 | @interface OCAKeyValueChange : OCAObject 22 | 23 | 24 | - (instancetype)initWithObject:(NSObject *)object keyPath:(NSString *)keyPath change:(NSDictionary *)dictionary structureAccessor:(OCAStructureAccessor *)accessor; 25 | @property (nonatomic, readonly, weak) id object; 26 | @property (nonatomic, readonly, copy) NSString *keyPath; 27 | @property (nonatomic, readonly, copy) NSDictionary *changeDictionary; 28 | @property (nonatomic, readonly, strong) OCAStructureAccessor *accessor; 29 | 30 | @property (nonatomic, readonly, strong) id latestValue; 31 | @property (nonatomic, readonly) BOOL isPrior; 32 | @property (nonatomic, readonly) NSKeyValueChange kind; 33 | 34 | - (OCAKeyValueChangeSetting *)asSettingChange; 35 | - (OCAKeyValueChangeInsertion *)asInsertionChange; 36 | - (OCAKeyValueChangeRemoval *)asRemovalChange; 37 | - (OCAKeyValueChangeReplacement *)asReplacementChange; 38 | 39 | - (void)applyToProperty:(OCAProperty *)property; 40 | 41 | 42 | @end 43 | 44 | 45 | 46 | 47 | 48 | @interface OCAKeyValueChangeSetting : OCAKeyValueChange 49 | 50 | 51 | @property (nonatomic, readonly, assign) BOOL isInitial; 52 | @property (nonatomic, readonly) id previousValue; // Do not use to compare. 53 | 54 | - (BOOL)isLatestEqualToPrevious; 55 | 56 | - (instancetype)copyWithTransformedInsertedObjects:(NSValueTransformer *)transformer; 57 | 58 | 59 | @end 60 | 61 | 62 | 63 | 64 | 65 | @interface OCAKeyValueChangeInsertion : OCAKeyValueChange 66 | 67 | 68 | @property (nonatomic, readonly, strong) NSArray *insertedObjects; 69 | @property (nonatomic, readonly, strong) NSIndexSet *insertedIndexes; 70 | 71 | - (instancetype)copyWithTransformedInsertedObjects:(NSValueTransformer *)transformer; 72 | 73 | 74 | @end 75 | 76 | 77 | 78 | 79 | 80 | @interface OCAKeyValueChangeRemoval : OCAKeyValueChange 81 | 82 | 83 | @property (nonatomic, readonly, strong) NSArray *removedObjects; 84 | @property (nonatomic, readonly, strong) NSIndexSet *removedIndexes; 85 | 86 | 87 | @end 88 | 89 | 90 | 91 | 92 | 93 | @interface OCAKeyValueChangeReplacement : OCAKeyValueChange 94 | 95 | 96 | @property (nonatomic, readonly, strong) NSArray *removedObjects; 97 | @property (nonatomic, readonly, strong) NSArray *insertedObjects; 98 | @property (nonatomic, readonly, strong) NSIndexSet *replacedIndexes; 99 | 100 | 101 | - (instancetype)copyWithTransformedInsertedObjects:(NSValueTransformer *)transformer; 102 | 103 | 104 | @end 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAMutableArrayProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAMutableArrayProxy.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 17.4.15. 6 | // Copyright (c) 2015 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAAccessor.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCAMutableArrayProxy : NSProxy 16 | 17 | 18 | 19 | - (instancetype)initWithReceiver:(id)receiver accessor:(OCAAccessor *)accessor; 20 | - (NSMutableArray *)asMutableArray; 21 | 22 | @property (nonatomic, readonly, strong) id receiver; 23 | @property (nonatomic, readonly, strong) OCAAccessor *accessor; 24 | 25 | 26 | @end 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAMutableArrayProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAMutableArrayProxy.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 17.4.15. 6 | // Copyright (c) 2015 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMutableArrayProxy.h" 10 | 11 | 12 | 13 | 14 | 15 | @implementation OCAMutableArrayProxy 16 | 17 | 18 | 19 | 20 | 21 | - (instancetype)initWithReceiver:(NSObject *)receiver accessor:(OCAAccessor *)accessor { 22 | self->_receiver = receiver; 23 | self->_accessor = accessor; 24 | return self; 25 | } 26 | 27 | 28 | - (NSMutableArray *)asMutableArray { 29 | return (NSMutableArray *)self; 30 | } 31 | 32 | 33 | 34 | 35 | 36 | + (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 37 | return [NSMutableArray methodSignatureForSelector:selector]; 38 | } 39 | 40 | 41 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 42 | return [NSMutableArray instanceMethodSignatureForSelector:selector]; 43 | } 44 | 45 | 46 | - (void)forwardInvocation:(NSInvocation *)invocation { 47 | NSArray *array = [self.accessor accessObject: self.receiver]; 48 | if ( ! [array isKindOfClass:[NSArray class]]) { 49 | array = nil; 50 | } 51 | NSMutableArray *mutableArray = [array mutableCopy]; 52 | [invocation invokeWithTarget: mutableArray]; 53 | [self.accessor modifyObject: self.receiver withValue: mutableArray]; 54 | } 55 | 56 | 57 | 58 | 59 | 60 | @end 61 | 62 | 63 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAPlaceholderObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPlaceholderObject.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | 11 | 12 | 13 | 14 | 15 | @interface OCAPlaceholderObject : OCAObject 16 | 17 | 18 | 19 | #define OCAPH(CLASS) OCAPlaceholder(CLASS) 20 | #define OCAPlaceholder(CLASS) ((CLASS *)[OCAPlaceholderObject placeholderForClass:[CLASS class]]) 21 | 22 | + (instancetype)placeholderForClass:(Class)theClass; 23 | 24 | @property (atomic, readonly, strong) Class representedClass; 25 | 26 | 27 | 28 | @end 29 | 30 | 31 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAPlaceholderObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPlaceholderObject.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPlaceholderObject.h" 10 | #import 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAPlaceholderObject 22 | 23 | 24 | 25 | 26 | 27 | + (instancetype)placeholderForClass:(Class)class { 28 | static void *OCAPlaceholderObjectKey = &OCAPlaceholderObjectKey; 29 | OCAPlaceholderObject *placeholder = objc_getAssociatedObject(class, OCAPlaceholderObjectKey); 30 | if (placeholder) return placeholder; 31 | 32 | placeholder = [[self alloc] initWithRepresentedClass:class]; 33 | objc_setAssociatedObject(class, OCAPlaceholderObjectKey, placeholder, OBJC_ASSOCIATION_RETAIN); 34 | return placeholder; 35 | } 36 | 37 | 38 | - (instancetype)init { 39 | return [self initWithRepresentedClass:nil]; 40 | } 41 | 42 | 43 | - (instancetype)initWithRepresentedClass:(Class)class { 44 | self = [super init]; 45 | if (self) { 46 | self->_representedClass = class; 47 | } 48 | return self; 49 | } 50 | 51 | 52 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { 53 | return [self.representedClass instanceMethodSignatureForSelector:aSelector]; 54 | } 55 | 56 | 57 | - (void)forwardInvocation:(NSInvocation *)invocation { 58 | [invocation invokeWithTarget:(__nonnull id)nil]; // Ah. 59 | } 60 | 61 | 62 | 63 | 64 | 65 | @end 66 | 67 | 68 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAQueue.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 6.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | 11 | 12 | 13 | typedef void (^OCAQueueBlock)(void); 14 | 15 | 16 | 17 | 18 | 19 | @interface OCAQueue : OCAObject 20 | 21 | 22 | 23 | #pragma mark Getting Shared Queues 24 | 25 | + (instancetype)main; 26 | + (instancetype)interactive; 27 | + (instancetype)user; 28 | + (instancetype)utility; 29 | + (instancetype)background; 30 | 31 | 32 | #pragma mark Accessing Current Queue 33 | 34 | + (instancetype)current; 35 | 36 | 37 | #pragma mark Creating Queues 38 | 39 | - (instancetype)initWithName:(NSString *)name concurrent:(BOOL)isConcurrent targetQueue:(OCAQueue *)targetQueue; 40 | 41 | + (instancetype)serialQueue:(NSString *)name; 42 | + (instancetype)concurrentQueue:(NSString *)name; 43 | 44 | 45 | #pragma mark Attributes of Queue 46 | 47 | @property (atomic, readonly, copy) NSString *name; 48 | @property (atomic, readonly, assign) BOOL isConcurrent; 49 | @property (atomic, readonly, strong) dispatch_queue_t dispatchQueue; 50 | 51 | 52 | #pragma mark Adding Blocks to Queue 53 | 54 | - (void)performBlock:(OCAQueueBlock)block; 55 | - (void)performBlockAndWait:(OCAQueueBlock)block; 56 | - (void)performBlockAndTryWait:(OCAQueueBlock)block; 57 | 58 | - (void)performBarrierBlock:(OCAQueueBlock)block; 59 | - (void)performBarrierBlockAndWait:(OCAQueueBlock)block; 60 | 61 | - (void)performAfter:(NSTimeInterval)delay block:(OCAQueueBlock)block; 62 | - (void)performMultiple:(NSUInteger)count blocks:(void(^)(NSUInteger i))block; 63 | 64 | 65 | #pragma mark Accessing Target Queue 66 | 67 | @property (atomic, readonly, strong) OCAQueue *targetQueue; 68 | - (BOOL)isTargetedTo:(OCAQueue *)queue; 69 | 70 | 71 | 72 | @end 73 | 74 | 75 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCASemaphore.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCASemaphore.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 31.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | 11 | 12 | 13 | 14 | 15 | /// Simple wrapper of GCD semaphore. 16 | @interface OCASemaphore : OCAObject 17 | 18 | 19 | 20 | - (instancetype)initWithValue:(NSUInteger)integer; 21 | 22 | - (void)signal; 23 | 24 | - (void)wait; 25 | - (BOOL)waitFor:(NSTimeInterval)interval; 26 | - (BOOL)waitUntil:(NSDate *)date; 27 | 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCASemaphore.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCASemaphore.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 31.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCASemaphore.h" 10 | 11 | 12 | 13 | @interface OCASemaphore () 14 | 15 | @property (atomic, readonly, strong) dispatch_semaphore_t semaphore; 16 | 17 | @end 18 | 19 | 20 | 21 | 22 | 23 | @implementation OCASemaphore 24 | 25 | 26 | 27 | 28 | 29 | #pragma mark Creating Semaphore 30 | 31 | 32 | - (instancetype)init { 33 | return [self initWithValue:0]; 34 | } 35 | 36 | 37 | - (instancetype)initWithValue:(NSUInteger)integer { 38 | self = [super init]; 39 | if (self) { 40 | self->_semaphore = dispatch_semaphore_create(integer); 41 | } 42 | return self; 43 | } 44 | 45 | 46 | 47 | 48 | 49 | #pragma mark Using Semaphore 50 | 51 | 52 | - (void)signal { 53 | dispatch_semaphore_signal(self.semaphore); 54 | } 55 | 56 | 57 | - (void)wait { 58 | dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); 59 | } 60 | 61 | 62 | - (BOOL)waitFor:(NSTimeInterval)interval { 63 | dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC); 64 | long timedOut = dispatch_semaphore_wait(self.semaphore, time); 65 | return ! timedOut; 66 | } 67 | 68 | 69 | - (BOOL)waitUntil:(NSDate *)date { 70 | return [self waitFor:date.timeIntervalSinceNow]; 71 | } 72 | 73 | 74 | 75 | 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCASwizzling.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCASwizzling.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 6.5.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAConsumer.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface NSObject (OCASwizzling) 17 | 18 | 19 | + (void)swizzleSelector:(SEL)original with:(SEL)replacement; 20 | 21 | 22 | + (void)implementOrderedCollectionAccessorsForKey:(NSString *)key; 23 | + (void)implementOrderedCollectionAccessorsForKey:(NSString *)key insertionCallback:(id)insertionCallback removalCallback:(id)removalCallback; 24 | 25 | 26 | @end 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAVariadic.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAVariadic.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 1.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | 14 | 15 | /// Returns string formatted using variable argument list format. Pass the formattign string as an argument. 16 | #define OCAStringFromFormat(format) \ 17 | (NSString *)({ \ 18 | va_list __vargs; \ 19 | va_start(__vargs, format); \ 20 | NSString *__string = [[NSString alloc] initWithFormat:format arguments:__vargs]; \ 21 | va_end(__vargs); \ 22 | __string; \ 23 | }) 24 | 25 | 26 | 27 | /// Collects variable argument list that begins with `first` into mutable array. Any arrays found in the list are expanded. 28 | #define OCAArrayFromVariadicArguments(first) \ 29 | (NSMutableArray *)({ \ 30 | va_list list; \ 31 | va_start(list, first); \ 32 | NSMutableArray *array = OCAArrayFromVariadicList(first, list); \ 33 | va_end(list); \ 34 | array; \ 35 | }) 36 | 37 | 38 | 39 | /// Helper macro for providing an array to variable argument method. Such method must handle these cases, for example using OCAArrayFromVariadicArguments. 40 | #define OCAVariadic(ARRAY) (id)(ARRAY), nil 41 | 42 | 43 | 44 | /// Function that is used by OCAArrayFromVariadicArguments. Collects object from variadic list into mutable array. Any arrays found in the list are epanded. 45 | extern NSMutableArray * OCAArrayFromVariadicList(id first, va_list list); 46 | 47 | 48 | -------------------------------------------------------------------------------- /Sources/Foundation/Utilities/OCAVariadic.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAVariadic.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 1.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAVariadic.h" 10 | 11 | 12 | 13 | 14 | 15 | NSMutableArray * OCAArrayFromVariadicList(id first, va_list list) { 16 | NSMutableArray *array = [[NSMutableArray alloc] init]; 17 | id object = first; 18 | while (object) { 19 | if ([object isKindOfClass:[NSArray class]]) { 20 | [array addObjectsFromArray:(NSArray *)object]; 21 | } 22 | else { 23 | [array addObject:object]; 24 | } 25 | object = va_arg(list, id); 26 | } 27 | return array; 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /Sources/Geometry/OCAGeometry+Functions.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAGeometry+Functions.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 27.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "NSValue+Boxing.h" 12 | #import "OCAObject.h" 13 | 14 | #if OCA_iOS 15 | #import 16 | #endif 17 | 18 | 19 | 20 | 21 | 22 | #pragma mark - 23 | #pragma mark Rounding 24 | 25 | extern CGFloat OCAGeometryDefaultScale(void); 26 | extern CGFloat OCAGeometryRound(CGFloat value, CGFloat scale); 27 | extern CGFloat OCAGeometryFloor(CGFloat value, CGFloat scale); 28 | extern CGFloat OCAGeometryCeil(CGFloat value, CGFloat scale); 29 | 30 | 31 | 32 | 33 | 34 | #pragma mark - 35 | #pragma mark Point 36 | 37 | extern CGPoint OCAPointFromString(NSString *); 38 | extern NSString * OCAStringFromPoint(CGPoint); 39 | 40 | extern CGPoint OCAPointAddPoint(CGPoint, CGPoint); 41 | extern CGPoint OCAPointSubtractPoint(CGPoint, CGPoint); 42 | extern CGPoint OCAPointMultiply(CGPoint point, CGFloat multipler); 43 | extern CGPoint OCAPointNormalize(CGPoint); 44 | 45 | extern CGPoint OCAPointRound(CGPoint point, CGFloat scale); 46 | extern CGPoint OCAPointFloor(CGPoint point, CGFloat scale); 47 | extern CGPoint OCAPointCeil(CGPoint point, CGFloat scale); 48 | 49 | extern CGFloat OCAPointDistanceToPoint(CGPoint, CGPoint); 50 | extern CGFloat OCAPointGetMagnitude(CGPoint); 51 | extern CGFloat OCAPointGetAngle(CGPoint); 52 | 53 | 54 | 55 | 56 | 57 | #pragma mark - 58 | #pragma mark Size 59 | 60 | extern CGSize OCASizeFromString(NSString *); 61 | extern NSString * OCAStringFromSize(CGSize); 62 | 63 | extern CGSize OCASizeExtendBySize(CGSize, CGSize); 64 | extern CGSize OCASizeShrinkBySize(CGSize, CGSize); 65 | extern CGSize OCASizeMultiply(CGSize, CGFloat); 66 | extern CGSize OCASizeStandardize(CGSize size); 67 | 68 | extern CGSize OCASizeRound(CGSize size, CGFloat scale); 69 | extern CGSize OCASizeFloor(CGSize size, CGFloat scale); 70 | extern CGSize OCASizeCeil(CGSize size, CGFloat scale); 71 | 72 | extern CGFloat OCASizeGetArea(CGSize size); 73 | extern CGFloat OCASizeGetRatio(CGSize size); 74 | 75 | 76 | 77 | 78 | 79 | #pragma mark - 80 | #pragma mark Rectangle 81 | 82 | extern CGRect OCARectFromString(NSString *); 83 | extern NSString * OCAStringFromRect(CGRect); 84 | 85 | extern CGRect OCARectRound(CGRect rect, CGFloat scale); 86 | extern CGRect OCARectCeil(CGRect rect, CGFloat scale); 87 | extern CGRect OCARectFloor(CGRect rect, CGFloat scale); 88 | 89 | extern CGPoint OCARectGetRelativePoint(CGRect rect, CGPoint point); 90 | extern CGFloat OCARectGetEdge(CGRect, CGRectEdge); 91 | 92 | 93 | 94 | 95 | 96 | #if OCA_iOS 97 | 98 | #pragma mark - 99 | #pragma mark Edge Insets 100 | 101 | extern UIEdgeInsets OCAEdgeInsetsAddEdgeInsets(UIEdgeInsets, UIEdgeInsets); 102 | extern UIEdgeInsets OCAEdgeInsetsSubtractEdgeInsets(UIEdgeInsets, UIEdgeInsets); 103 | extern UIEdgeInsets OCAEdgeInsetsMultiply(UIEdgeInsets insets, CGFloat multipler); 104 | 105 | extern UIEdgeInsets OCAEdgeInsetsRound(UIEdgeInsets insets, CGFloat scale); 106 | extern UIEdgeInsets OCAEdgeInsetsFloor(UIEdgeInsets insets, CGFloat scale); 107 | extern UIEdgeInsets OCAEdgeInsetsCeil(UIEdgeInsets insets, CGFloat scale); 108 | 109 | #endif 110 | 111 | 112 | -------------------------------------------------------------------------------- /Sources/Geometry/OCAGeometry.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectiveChain.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | 11 | #import "OCAGeometry+Functions.h" 12 | 13 | 14 | #import "OCAGeometry+Functions.h" 15 | #import "OCATransformer+CGPoint.h" 16 | #import "OCATransformer+CGSize.h" 17 | #import "OCATransformer+CGRect.h" 18 | #import "OCATransformer+CGAffineTransform.h" 19 | #import "OCATransformer+UIEdgeInsets.h" 20 | #import "OCATransformer+CATransform3D.h" 21 | //TODO: CGPath 22 | 23 | 24 | #import "OCAPredicate+CGPoint.h" 25 | #import "OCAPredicate+CGSize.h" 26 | #import "OCAPredicate+CGRect.h" 27 | #import "OCAPredicate+CGAffineTransform.h" 28 | #import "OCAPredicate+UIEdgeInsets.h" 29 | #import "OCAPredicate+CATransform3D.h" 30 | 31 | 32 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CATransform3D.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CATransform3D.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAPredicate.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCAPredicate (CATransform3D) 17 | 18 | 19 | 20 | + (NSPredicate *)predicateForTransform3D:(BOOL(^)(CATransform3D t))block; 21 | 22 | + (NSPredicate *)isTransform3DEqualTo:(CATransform3D)otherTransform3D; 23 | + (NSPredicate *)isTransform3DIdentity; 24 | 25 | + (NSPredicate *)isTransform3DAffineTransform; 26 | 27 | 28 | 29 | @end 30 | 31 | 32 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CATransform3D.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CATransform3D.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate+CATransform3D.h" 10 | #import "OCAGeometry+Functions.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAPredicate (CATransform3D) 22 | 23 | 24 | 25 | 26 | 27 | + (NSPredicate *)predicateForTransform3D:(BOOL(^)(CATransform3D t))block { 28 | return [OCAPredicate predicateForClass:[NSValue class] block:^BOOL(NSValue *value) { 29 | CATransform3D t; 30 | BOOL success = [value unboxValue:&t objCType:@encode(CATransform3D)]; 31 | if ( ! success) return NO; 32 | 33 | return block(t); 34 | }]; 35 | } 36 | 37 | 38 | + (NSPredicate *)isTransform3DEqualTo:(CATransform3D)otherTransform3D { 39 | return [OCAPredicate predicateForTransform3D:^BOOL(CATransform3D t) { 40 | return CATransform3DEqualToTransform(t, otherTransform3D); 41 | }]; 42 | } 43 | 44 | 45 | + (NSPredicate *)isTransform3DIdentity { 46 | return [OCAPredicate predicateForTransform3D:^BOOL(CATransform3D t) { 47 | return CATransform3DIsIdentity(t); 48 | }]; 49 | } 50 | 51 | 52 | + (NSPredicate *)isTransform3DAffineTransform { 53 | return [OCAPredicate predicateForTransform3D:^BOOL(CATransform3D t) { 54 | return CATransform3DIsAffine(t); 55 | }]; 56 | } 57 | 58 | 59 | 60 | 61 | 62 | @end 63 | 64 | 65 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGAffineTransform.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGAffineTransform.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAPredicate.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCAPredicate (CGAffineTransform) 17 | 18 | 19 | 20 | + (NSPredicate *)predicateForAffineTransform:(BOOL(^)(CGAffineTransform t))block; 21 | 22 | + (NSPredicate *)isAffineTransformEqualTo:(CGAffineTransform)otherAffineTransform; 23 | + (NSPredicate *)isAffineTransformIdentity; 24 | 25 | 26 | 27 | @end 28 | 29 | 30 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGAffineTransform.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGAffineTransform.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate+CGAffineTransform.h" 10 | #import "OCAGeometry+Functions.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAPredicate (CGAffineTransform) 22 | 23 | 24 | 25 | 26 | 27 | + (NSPredicate *)predicateForAffineTransform:(BOOL(^)(CGAffineTransform t))block { 28 | return [OCAPredicate predicateForClass:[NSValue class] block:^BOOL(NSValue *value) { 29 | CGAffineTransform t; 30 | BOOL success = [value unboxValue:&t objCType:@encode(CGAffineTransform)]; 31 | if ( ! success) return NO; 32 | 33 | return block(t); 34 | }]; 35 | } 36 | 37 | 38 | + (NSPredicate *)isAffineTransformIdentity { 39 | return [OCAPredicate predicateForAffineTransform:^BOOL(CGAffineTransform t) { 40 | return CGAffineTransformIsIdentity(t); 41 | }]; 42 | } 43 | 44 | 45 | + (NSPredicate *)isAffineTransformEqualTo:(CGAffineTransform)otherAffineTransform { 46 | return [OCAPredicate predicateForAffineTransform:^BOOL(CGAffineTransform t) { 47 | return CGAffineTransformEqualToTransform(t, otherAffineTransform); 48 | }]; 49 | } 50 | 51 | 52 | 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGPoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGPoint.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAPredicate.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCAPredicate (CGPoint) 17 | 18 | 19 | 20 | + (NSPredicate *)predicateForPoint:(BOOL(^)(CGPoint point))block; 21 | 22 | + (NSPredicate *)isPointEqualTo:(CGPoint)otherPoint; 23 | + (NSPredicate *)isPointZero; 24 | 25 | + (NSPredicate *)isPointFurtherFrom:(CGPoint)otherPoint than:(CGFloat)distance; 26 | + (NSPredicate *)isPointCloserTo:(CGPoint)otherPoint than:(CGFloat)distance; 27 | 28 | + (NSPredicate *)isPointContainedInRect:(CGRect)rect; 29 | 30 | 31 | 32 | @end 33 | 34 | 35 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGPoint.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGPoint.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate+CGPoint.h" 10 | #import "OCAGeometry+Functions.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAPredicate (CGPoint) 22 | 23 | 24 | 25 | 26 | 27 | + (NSPredicate *)predicateForPoint:(BOOL(^)(CGPoint point))block { 28 | return [OCAPredicate predicateForClass:[NSValue class] block:^BOOL(NSValue *value) { 29 | CGPoint point; 30 | BOOL success = [value unboxValue:&point objCType:@encode(CGPoint)]; 31 | if ( ! success) return NO; 32 | 33 | return block(point); 34 | }]; 35 | } 36 | 37 | 38 | + (NSPredicate *)isPointEqualTo:(CGPoint)otherPoint { 39 | return [OCAPredicate predicateForPoint:^BOOL(CGPoint point) { 40 | return CGPointEqualToPoint(point, otherPoint); 41 | }]; 42 | } 43 | 44 | 45 | + (NSPredicate *)isPointZero { 46 | return [OCAPredicate isPointEqualTo:CGPointZero]; 47 | } 48 | 49 | 50 | + (NSPredicate *)isPointFurtherFrom:(CGPoint)otherPoint than:(CGFloat)distance { 51 | return [OCAPredicate predicateForPoint:^BOOL(CGPoint point) { 52 | return (OCAPointDistanceToPoint(point, otherPoint) >= distance); 53 | }]; 54 | } 55 | 56 | 57 | + (NSPredicate *)isPointCloserTo:(CGPoint)otherPoint than:(CGFloat)distance { 58 | return [OCAPredicate predicateForPoint:^BOOL(CGPoint point) { 59 | return (OCAPointDistanceToPoint(point, otherPoint) <= distance); 60 | }]; 61 | } 62 | 63 | 64 | + (NSPredicate *)isPointContainedInRect:(CGRect)rect { 65 | return [OCAPredicate predicateForPoint:^BOOL(CGPoint point) { 66 | return CGRectContainsPoint(rect, point); 67 | }]; 68 | } 69 | 70 | 71 | 72 | 73 | 74 | @end 75 | 76 | 77 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGRect.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGRect.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAPredicate.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCAPredicate (CGRect) 17 | 18 | 19 | 20 | + (NSPredicate *)predicateForRect:(BOOL(^)(CGRect rect))block; 21 | 22 | + (NSPredicate *)isRectEqualTo:(CGRect)otherRect; 23 | + (NSPredicate *)isRectZero; 24 | + (NSPredicate *)isRectEmpty; 25 | + (NSPredicate *)isRectNull; 26 | + (NSPredicate *)isRectInfinite; 27 | 28 | + (NSPredicate *)isRectContainsPoint:(CGPoint)point; 29 | + (NSPredicate *)isRectContainsRect:(CGRect)otherRect; 30 | + (NSPredicate *)isRectContainedInRect:(CGRect)otherRect; 31 | + (NSPredicate *)isRectIntersects:(CGRect)otherRect; 32 | 33 | 34 | 35 | @end 36 | 37 | 38 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGRect.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGRect.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate+CGRect.h" 10 | #import "OCAGeometry+Functions.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAPredicate (CGRect) 22 | 23 | 24 | 25 | 26 | 27 | + (NSPredicate *)predicateForRect:(BOOL(^)(CGRect rect))block { 28 | return [OCAPredicate predicateForClass:[NSValue class] block:^BOOL(NSValue *value) { 29 | CGRect rect; 30 | BOOL success = [value unboxValue:&rect objCType:@encode(CGRect)]; 31 | if ( ! success) return NO; 32 | 33 | return block(rect); 34 | }]; 35 | } 36 | 37 | 38 | + (NSPredicate *)isRectEqualTo:(CGRect)otherRect { 39 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 40 | return CGRectEqualToRect(rect, otherRect); 41 | }]; 42 | } 43 | 44 | 45 | + (NSPredicate *)isRectZero { 46 | return [OCAPredicate isRectEqualTo:CGRectZero]; 47 | } 48 | 49 | 50 | + (NSPredicate *)isRectEmpty { 51 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 52 | return CGRectIsEmpty(rect); 53 | }]; 54 | } 55 | 56 | 57 | + (NSPredicate *)isRectNull { 58 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 59 | return CGRectIsNull(rect); 60 | }]; 61 | } 62 | 63 | 64 | + (NSPredicate *)isRectInfinite { 65 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 66 | return CGRectIsInfinite(rect); 67 | }]; 68 | } 69 | 70 | 71 | + (NSPredicate *)isRectContainsPoint:(CGPoint)point { 72 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 73 | return CGRectContainsPoint(rect, point); 74 | }]; 75 | } 76 | 77 | 78 | + (NSPredicate *)isRectContainsRect:(CGRect)otherRect { 79 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 80 | return CGRectContainsRect(rect, otherRect); 81 | }]; 82 | } 83 | 84 | 85 | + (NSPredicate *)isRectContainedInRect:(CGRect)otherRect { 86 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 87 | return CGRectContainsRect(otherRect, rect); 88 | }]; 89 | } 90 | 91 | 92 | + (NSPredicate *)isRectIntersects:(CGRect)otherRect { 93 | return [OCAPredicate predicateForRect:^BOOL(CGRect rect) { 94 | return CGRectIntersectsRect(rect, otherRect); 95 | }]; 96 | } 97 | 98 | 99 | 100 | 101 | 102 | @end 103 | 104 | 105 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGSize.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGSize.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAPredicate.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCAPredicate (CGSize) 17 | 18 | 19 | 20 | + (NSPredicate *)predicateForSize:(BOOL(^)(CGSize size))block; 21 | 22 | + (NSPredicate *)isSizeEqualTo:(CGSize)otherSize; 23 | + (NSPredicate *)isSizeZero; 24 | 25 | 26 | 27 | @end 28 | 29 | 30 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+CGSize.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+CGSize.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate+CGSize.h" 10 | #import "OCAGeometry+Functions.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAPredicate (CGSize) 22 | 23 | 24 | 25 | 26 | 27 | + (NSPredicate *)predicateForSize:(BOOL(^)(CGSize size))block { 28 | return [OCAPredicate predicateForClass:[NSValue class] block:^BOOL(NSValue *value) { 29 | CGSize size; 30 | BOOL success = [value unboxValue:&size objCType:@encode(CGSize)]; 31 | if ( ! success) return NO; 32 | 33 | return block(size); 34 | }]; 35 | } 36 | 37 | 38 | + (NSPredicate *)isSizeEqualTo:(CGSize)otherSize { 39 | return [OCAPredicate predicateForSize:^BOOL(CGSize size) { 40 | return CGSizeEqualToSize(size, otherSize); 41 | }]; 42 | } 43 | 44 | 45 | + (NSPredicate *)isSizeZero { 46 | return [OCAPredicate isSizeEqualTo:CGSizeZero]; 47 | } 48 | 49 | 50 | 51 | 52 | 53 | @end 54 | 55 | 56 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+UIEdgeInsets.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+UIEdgeInsets.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate.h" 10 | 11 | 12 | #if OCA_iOS 13 | 14 | #import 15 | 16 | 17 | 18 | 19 | 20 | @interface OCAPredicate (UIEdgeInsets) 21 | 22 | 23 | 24 | + (NSPredicate *)predicateForEdgeInsets:(BOOL(^)(UIEdgeInsets insets))block; 25 | 26 | + (NSPredicate *)isEdgeInsetsEqualTo:(UIEdgeInsets)otherInsets; 27 | + (NSPredicate *)isEdgeInsetsZero; 28 | 29 | 30 | 31 | @end 32 | 33 | #endif 34 | 35 | 36 | -------------------------------------------------------------------------------- /Sources/Geometry/Predicates/OCAPredicate+UIEdgeInsets.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPredicate+UIEdgeInsets.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 28.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAPredicate+UIEdgeInsets.h" 10 | #import "OCAGeometry+Functions.h" 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | #if OCA_iOS 22 | 23 | 24 | @implementation OCAPredicate (UIEdgeInsets) 25 | 26 | 27 | 28 | 29 | 30 | + (NSPredicate *)predicateForEdgeInsets:(BOOL(^)(UIEdgeInsets insets))block { 31 | return [OCAPredicate predicateForClass:[NSValue class] block:^BOOL(NSValue *value) { 32 | UIEdgeInsets insets; 33 | BOOL success = [value unboxValue:&insets objCType:@encode(UIEdgeInsets)]; 34 | if ( ! success) return NO; 35 | 36 | return block(insets); 37 | }]; 38 | } 39 | 40 | 41 | + (NSPredicate *)isEdgeInsetsEqualTo:(UIEdgeInsets)otherInsets { 42 | return [OCAPredicate predicateForEdgeInsets:^BOOL(UIEdgeInsets insets) { 43 | return UIEdgeInsetsEqualToEdgeInsets(insets, otherInsets); 44 | }]; 45 | } 46 | 47 | 48 | + (NSPredicate *)isEdgeInsetsZero { 49 | return [OCAPredicate predicateForEdgeInsets:^BOOL(UIEdgeInsets insets) { 50 | return UIEdgeInsetsEqualToEdgeInsets(insets, UIEdgeInsetsZero); 51 | }]; 52 | } 53 | 54 | 55 | 56 | 57 | 58 | @end 59 | 60 | 61 | #endif 62 | 63 | 64 | -------------------------------------------------------------------------------- /Sources/Geometry/Transformers/OCATransformer+CATransform3D.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+CATransform3D.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 13.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCATransformer+Base.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCATransformer (CATransform3D) 17 | 18 | 19 | 20 | #pragma mark Creating 3D Transforms 21 | 22 | + (OCATransformer *)transform3DFromScaleWithZ:(CGFloat)zScale; 23 | + (OCATransformer *)transform3DFromXRotation; 24 | + (OCATransformer *)transform3DFromYRotation; 25 | + (OCATransformer *)transform3DFromZRotation; 26 | + (OCATransformer *)transform3DFromTranslationWithZ:(CGFloat)zTranslation; 27 | + (OCATransformer *)transform3DFromAffineTransform; 28 | 29 | 30 | #pragma mark Modifying 3D Transforms 31 | 32 | + (OCATransformer *)modifyTransform3D:(CATransform3D(^)(CATransform3D t))block; 33 | + (OCATransformer *)concatTransform3D:(CATransform3D)otherTransform3D; 34 | 35 | 36 | #pragma mark Disposing 3D Transforms 37 | 38 | + (OCATransformer *)affineTransformFromTransform3D; 39 | 40 | 41 | 42 | @end 43 | 44 | 45 | -------------------------------------------------------------------------------- /Sources/Geometry/Transformers/OCATransformer+CGAffineTransform.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+CGAffineTransform.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 13.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCATransformer+Base.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCATransformer (CGAffineTransform) 17 | 18 | 19 | 20 | #pragma mark Creating Affine Transforms 21 | 22 | + (OCATransformer *)affineTransformFromScale; 23 | + (OCATransformer *)affineTransformFromScales; 24 | + (OCATransformer *)affineTransformFromRotation; 25 | + (OCATransformer *)affineTransformFromTranslation; 26 | #if OCA_iOS 27 | + (OCATransformer *)affineTransformFromString; 28 | #endif 29 | 30 | 31 | #pragma mark Modifying Affine Transforms 32 | 33 | + (OCATransformer *)modifyAffineTransform:(CGAffineTransform(^)(CGAffineTransform t))block; 34 | + (OCATransformer *)modifyAffineTransform:(CGAffineTransform(^)(CGAffineTransform t))block reverse:(CGAffineTransform(^)(CGAffineTransform t))reverseBlock; 35 | + (OCATransformer *)concatAffineTransform:(CGAffineTransform)otherAffineTransform; 36 | + (OCATransformer *)affineTransformScale:(CGSize)scale; 37 | + (OCATransformer *)affineTransformRotate:(CGFloat)rotation; 38 | + (OCATransformer *)affineTransformTranslate:(CGPoint)translation; 39 | + (OCATransformer *)invertAffineTransform; 40 | 41 | 42 | 43 | @end 44 | 45 | 46 | -------------------------------------------------------------------------------- /Sources/Geometry/Transformers/OCATransformer+CGPoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+CGPoint.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 13.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "OCATransformer+Base.h" 12 | 13 | 14 | 15 | 16 | 17 | @interface OCATransformer (CGPoint) 18 | 19 | 20 | 21 | 22 | 23 | #pragma mark Creating Points 24 | 25 | + (OCATransformer *)pointFromString; 26 | + (OCATransformer *)makePoint; 27 | + (OCATransformer *)makePointWithX:(CGFloat)x; 28 | + (OCATransformer *)makePointWithY:(CGFloat)y; 29 | 30 | 31 | #pragma mark Modifying Points 32 | 33 | + (OCATransformer *)modifyPoint:(CGPoint(^)(CGPoint point))block; 34 | + (OCATransformer *)modifyPoint:(CGPoint(^)(CGPoint point))block reverse:(CGPoint(^)(CGPoint point))reverseBlock; 35 | + (OCATransformer *)addPoint:(CGPoint)otherPoint; 36 | + (OCATransformer *)subtractPoint:(CGPoint)otherPoint; 37 | + (OCATransformer *)multiplyPointBy:(CGFloat)multiplier; 38 | + (OCATransformer *)transformPoint:(CGAffineTransform)affineTransform; 39 | + (OCATransformer *)roundPointTo:(CGFloat)scale; 40 | + (OCATransformer *)floorPointTo:(CGFloat)scale; 41 | + (OCATransformer *)ceilPointTo:(CGFloat)scale; 42 | + (OCATransformer *)normalizePoint; 43 | 44 | 45 | #pragma mark Disposing Points 46 | 47 | + (OCATransformer *)stringFromPoint; 48 | + (OCATransformer *)distanceToPoint:(CGPoint)otherPoint; 49 | + (OCATransformer *)pointMagnitude; 50 | + (OCATransformer *)pointAngle; 51 | 52 | 53 | 54 | 55 | 56 | @end 57 | 58 | 59 | -------------------------------------------------------------------------------- /Sources/Geometry/Transformers/OCATransformer+CGRect.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+CGRect.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 13.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "OCATransformer+Base.h" 12 | 13 | #if OCA_iOS 14 | #import 15 | #endif 16 | 17 | 18 | 19 | 20 | 21 | @interface OCATransformer (CGRect) 22 | 23 | 24 | 25 | 26 | 27 | #pragma mark - 28 | #pragma mark Transformers 29 | #pragma mark - 30 | 31 | 32 | #pragma mark Creating Rectangles 33 | 34 | + (OCATransformer *)rectFromString; 35 | + (OCATransformer *)rectFromSizeWith:(CGPoint)origin; 36 | + (OCATransformer *)rectFromPointWith:(CGSize)size; 37 | 38 | 39 | #pragma mark Modifying Rectangles 40 | 41 | + (OCATransformer *)modifyRect:(CGRect(^)(CGRect rect))block; 42 | + (OCATransformer *)modifyRect:(CGRect(^)(CGRect rect))block reverse:(CGRect(^)(CGRect rect))reverseBlock; 43 | #if OCA_iOS 44 | + (OCATransformer *)insetRect:(UIEdgeInsets)insets; 45 | #endif 46 | + (OCATransformer *)transformRect:(CGAffineTransform)affineTransform; 47 | + (OCATransformer *)roundRectTo:(CGFloat)scale; 48 | + (OCATransformer *)floorRectTo:(CGFloat)scale; 49 | + (OCATransformer *)ceilRectTo:(CGFloat)scale; 50 | + (OCATransformer *)unionWith:(CGRect)otherRect; 51 | + (OCATransformer *)intersectionWith:(CGRect)otherRect; 52 | + (OCATransformer *)standardizeRect; 53 | 54 | 55 | #pragma mark Disposing Rectangles 56 | 57 | + (OCATransformer *)stringFromRect; 58 | + (OCATransformer *)rectRelativePoint:(CGPoint)relativePoint; 59 | + (OCATransformer *)rectCenter; 60 | + (OCATransformer *)rectEdge:(CGRectEdge)edge; 61 | 62 | 63 | 64 | 65 | 66 | @end 67 | 68 | 69 | -------------------------------------------------------------------------------- /Sources/Geometry/Transformers/OCATransformer+CGSize.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+CGSize.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 13.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "OCATransformer+Base.h" 12 | 13 | 14 | 15 | 16 | 17 | @interface OCATransformer (CGSize) 18 | 19 | 20 | 21 | 22 | 23 | #pragma mark Creating Sizes 24 | 25 | + (OCATransformer *)sizeFromString; 26 | + (OCATransformer *)makeSize; 27 | + (OCATransformer *)makeSizeWithWidth:(CGFloat)width; 28 | + (OCATransformer *)makeSizeWithHeight:(CGFloat)height; 29 | 30 | 31 | #pragma mark Modifying Sizes 32 | 33 | + (OCATransformer *)modifySize:(CGSize(^)(CGSize size))block; 34 | + (OCATransformer *)extendSizeBy:(CGSize)otherSize; 35 | + (OCATransformer *)shrinkSizeBy:(CGSize)otherSize; 36 | + (OCATransformer *)multiplySizeBy:(CGFloat)multiplier; 37 | + (OCATransformer *)transformSize:(CGAffineTransform)affineTransform; 38 | + (OCATransformer *)roundSizeTo:(CGFloat)scale; 39 | + (OCATransformer *)floorSizeTo:(CGFloat)scale; 40 | + (OCATransformer *)ceilSizeTo:(CGFloat)scale; 41 | + (OCATransformer *)standardizeSize; 42 | 43 | 44 | #pragma mark Disposing Sizes 45 | 46 | + (OCATransformer *)stringFromSize; 47 | + (OCATransformer *)sizeArea; 48 | + (OCATransformer *)sizeRatio; 49 | 50 | 51 | 52 | 53 | 54 | @end 55 | 56 | 57 | -------------------------------------------------------------------------------- /Sources/Geometry/Transformers/OCATransformer+UIEdgeInsets.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+UIEdgeInsets.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 13.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | #import "OCATransformer+Base.h" 11 | 12 | 13 | #if OCA_iOS 14 | 15 | #import 16 | 17 | 18 | 19 | 20 | 21 | 22 | @interface OCATransformer (UIEdgeInsets) 23 | 24 | 25 | 26 | #pragma mark Creating Edge Insets 27 | 28 | + (OCATransformer *)edgeInsetsFromEdges:(UIRectEdge)edges; 29 | + (OCATransformer *)edgeInsetsFromString; 30 | 31 | 32 | #pragma mark Modifying Edge Insets 33 | 34 | + (OCATransformer *)modifyEdgeInsets:(UIEdgeInsets(^)(UIEdgeInsets insets))block; 35 | + (OCATransformer *)modifyEdgeInsets:(UIEdgeInsets(^)(UIEdgeInsets insets))block reverse:(UIEdgeInsets(^)(UIEdgeInsets insets))reverseBlock; 36 | + (OCATransformer *)addEdgeInsets:(UIEdgeInsets)otherInsets; 37 | + (OCATransformer *)subtractEdgeInsets:(UIEdgeInsets)otherInsets; 38 | + (OCATransformer *)multiplyEdgeInsets:(CGFloat)multiplier; 39 | + (OCATransformer *)roundEdgeInsetsTo:(CGFloat)scale; 40 | + (OCATransformer *)ceilEdgeInsetsTo:(CGFloat)scale; 41 | + (OCATransformer *)floorEdgeInsetsTo:(CGFloat)scale; 42 | 43 | 44 | #pragma mark Disposing Edge Insets 45 | 46 | + (OCATransformer *)stringFromEdgeInsets; 47 | + (OCATransformer *)edgeInsetsGetHorizontal; 48 | + (OCATransformer *)edgeInsetsGetVertical; 49 | 50 | 51 | 52 | @end 53 | 54 | #endif // OCA_iOS 55 | 56 | 57 | -------------------------------------------------------------------------------- /Sources/ObjectiveChain.h: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectiveChain.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | #import "OCAFoundation.h" 11 | #import "OCAGeometry.h" 12 | #if OCA_iOS 13 | #import "OCAUIKit.h" 14 | #endif 15 | 16 | 17 | -------------------------------------------------------------------------------- /Sources/README.md: -------------------------------------------------------------------------------- 1 | **Objective-Chain** is made up of *Foundation* and additional libraries that are built on top of it: 2 | 3 | ## [Foundation](./Foundation) 4 | - Defines abstract ***Producer*** and ***Consumer*** as well as some of their basic concrete subclasses: *Command*, *Bridge*, *Hub*, *Subscriber*. 5 | - Factories for the most essential ***Transformers*** and ***Predicates***. 6 | - Provides ***Accessor*** classes for accessing and modifying key-paths and structure members. 7 | - Implementes ***Property Bridge***, one of the main *Producers / Consumers* which uses KVO and KVC. 8 | - Wrapper for GCD queues as a ***Queue*** class, which is used by *Connections*. 9 | - Includes some utility categories and classes that are used in other classes. 10 | - Provides a set of ***Math*** transformers for doing arithmetic, rounding or trigonometry. 11 | 12 | ## [Geometry](./Geometry) 13 | - Provides ***Transformers*** and ***Predicates*** for working with geometric structures like *Point*, *Size*, *Rectangle*, *Affine Transfrom* and *Edge Insets* for iOS targets. 14 | 15 | ## UIKit 16 | *To be implemented…* 17 | -------------------------------------------------------------------------------- /Sources/UIKit/Consumers/OCAUIKit+UIButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAUIKit+UIButton.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "OCAObject.h" 12 | #import "OCATransformer.h" 13 | #import "OCASubscriber.h" 14 | 15 | 16 | 17 | 18 | 19 | @interface OCAUIKit : OCAObject 20 | 21 | 22 | 23 | #pragma mark UIButton 24 | 25 | + (id)setTitleOfButton:(UIButton *)button forControlState:(UIControlState)state; 26 | + (id)setAttributedTitleOfButton:(UIButton *)button forControlState:(UIControlState)state; 27 | + (id)setTitleColorOfButton:(UIButton *)button forControlState:(UIControlState)state; 28 | + (id)setTitleShadowColorOfButton:(UIButton *)button forControlState:(UIControlState)state; 29 | + (id)setImageOfButton:(UIButton *)button forControlState:(UIControlState)state; 30 | + (id)setBackgroundImageOfButton:(UIButton *)button forControlState:(UIControlState)state; 31 | 32 | 33 | 34 | @end 35 | 36 | 37 | -------------------------------------------------------------------------------- /Sources/UIKit/Consumers/OCAUIKit+UIButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAUIKit+Base.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAUIKit+UIButton.h" 10 | 11 | 12 | 13 | 14 | 15 | @implementation OCAUIKit 16 | 17 | 18 | 19 | 20 | 21 | #pragma mark UIButton 22 | 23 | 24 | + (id)setTitleOfButton:(UIButton *)button forControlState:(UIControlState)state { 25 | return [OCASubscriber subscribeForClass:[NSString class] handler:^(NSString *input) { 26 | [button setTitle:input forState:state]; 27 | }]; 28 | } 29 | 30 | 31 | + (id)setAttributedTitleOfButton:(UIButton *)button forControlState:(UIControlState)state { 32 | return [OCASubscriber subscribeForClass:[NSAttributedString class] handler:^(NSAttributedString *input) { 33 | [button setAttributedTitle:input forState:state]; 34 | }]; 35 | } 36 | 37 | 38 | + (id)setTitleColorOfButton:(UIButton *)button forControlState:(UIControlState)state { 39 | return [OCASubscriber subscribeForClass:[UIColor class] handler:^(UIColor *input) { 40 | [button setTitleColor:input forState:state]; 41 | }]; 42 | } 43 | 44 | 45 | + (id)setTitleShadowColorOfButton:(UIButton *)button forControlState:(UIControlState)state { 46 | return [OCASubscriber subscribeForClass:[UIColor class] handler:^(UIColor *input) { 47 | [button setTitleShadowColor:input forState:state]; 48 | }]; 49 | } 50 | 51 | 52 | + (id)setImageOfButton:(UIButton *)button forControlState:(UIControlState)state { 53 | return [OCASubscriber subscribeForClass:[UIImage class] handler:^(UIImage *input) { 54 | [button setImage:input forState:state]; 55 | }]; 56 | } 57 | 58 | 59 | + (id)setBackgroundImageOfButton:(UIButton *)button forControlState:(UIControlState)state { 60 | return [OCASubscriber subscribeForClass:[UIImage class] handler:^(UIImage *input) { 61 | [button setBackgroundImage:input forState:state]; 62 | }]; 63 | } 64 | 65 | 66 | 67 | 68 | 69 | @end 70 | 71 | 72 | -------------------------------------------------------------------------------- /Sources/UIKit/Mediators/OCAContext+UIKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAContext+UIKit.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "OCAContext.h" 12 | 13 | 14 | 15 | 16 | 17 | @interface OCAContext (UIKit) 18 | 19 | 20 | 21 | #pragma mark View Animations 22 | 23 | + (OCAContext *)noAnimations; 24 | + (OCAContext *)animateWithDuration:(NSTimeInterval)duration; 25 | + (OCAContext *)animateWithDelay:(NSTimeInterval)delay duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options; 26 | //TODO: Animate with spring animation 27 | //TODO: Animate with System animation 28 | + (OCAContext *)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options; 29 | + (OCAContext *)crossDissolveView:(UIView *)view duration:(NSTimeInterval)duration; 30 | 31 | 32 | 33 | #pragma mark Core Animation 34 | 35 | + (OCAContext *)disableImplicitAnimations; 36 | 37 | 38 | 39 | @end 40 | 41 | 42 | -------------------------------------------------------------------------------- /Sources/UIKit/Mediators/OCAContext+UIKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAContext+UIKit.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAContext+UIKit.h" 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @implementation OCAContext (UIKit) 21 | 22 | 23 | 24 | 25 | 26 | #pragma mark View Animations 27 | 28 | 29 | + (OCAContext *)noAnimations { 30 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 31 | [UIView performWithoutAnimation:executionBlock]; 32 | }]; 33 | } 34 | 35 | 36 | + (OCAContext *)animateWithDuration:(NSTimeInterval)duration { 37 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 38 | [UIView animateWithDuration:duration animations:executionBlock]; 39 | }]; 40 | } 41 | 42 | 43 | + (OCAContext *)animateWithDelay:(NSTimeInterval)delay duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options { 44 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 45 | [UIView animateWithDuration:duration delay:delay options:options animations:executionBlock completion:nil]; 46 | }]; 47 | } 48 | 49 | 50 | + (OCAContext *)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options { 51 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 52 | [UIView transitionWithView:view duration:duration options:options animations:executionBlock completion:nil]; 53 | }]; 54 | } 55 | 56 | 57 | + (OCAContext *)crossDissolveView:(UIView *)view duration:(NSTimeInterval)duration { 58 | return [self transitionWithView:view duration:duration options:UIViewAnimationOptionTransitionCrossDissolve]; 59 | } 60 | 61 | 62 | 63 | 64 | 65 | #pragma mark Core Animation 66 | 67 | 68 | + (OCAContext *)disableImplicitAnimations { 69 | return [[self alloc] initWithDefinitionBlock:^(OCAContextExecutionBlock executionBlock) { 70 | [CATransaction begin]; 71 | [CATransaction setDisableActions:YES]; 72 | 73 | executionBlock(); 74 | 75 | [CATransaction commit]; 76 | }]; 77 | } 78 | 79 | 80 | 81 | 82 | 83 | @end 84 | 85 | 86 | -------------------------------------------------------------------------------- /Sources/UIKit/Mediators/OCAFader.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAFader.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 16.5.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAMediator.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCAFader : OCAMediator 17 | 18 | 19 | - (instancetype)initWithView:(UIView *)view visiblePredicate:(NSPredicate *)predicate duration:(NSTimeInterval)duration; 20 | + (instancetype)fade:(UIView *)view duration:(NSTimeInterval)duration; 21 | + (instancetype)fade:(UIView *)view visible:(NSPredicate *)predicate duration:(NSTimeInterval)duration; 22 | 23 | @property (atomic, readonly, weak) UIView *view; 24 | @property (atomic, readonly, strong) NSPredicate *visiblePredicate; 25 | @property (atomic, readonly, assign) NSTimeInterval duration; 26 | 27 | 28 | @end 29 | 30 | 31 | -------------------------------------------------------------------------------- /Sources/UIKit/Mediators/OCAFader.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAFader.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 16.5.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAFader.h" 10 | #import "OCAProducer+Subclass.h" 11 | #import "OCADecomposer.h" 12 | #import "OCAPredicate.h" 13 | 14 | 15 | 16 | 17 | 18 | @implementation OCAFader 19 | 20 | 21 | 22 | 23 | 24 | - (instancetype)initWithView:(UIView *)view visiblePredicate:(NSPredicate *)predicate duration:(NSTimeInterval)duration { 25 | self = [super initWithValueClass:nil]; 26 | if (self) { 27 | self->_view = view; 28 | [view.decomposer addOwnedObject:self cleanup:nil]; 29 | 30 | self->_visiblePredicate = predicate ?: [[OCAPredicate isEmpty] negate]; 31 | self->_duration = duration; 32 | } 33 | return self; 34 | } 35 | 36 | 37 | + (instancetype)fade:(UIView *)view duration:(NSTimeInterval)duration { 38 | return [[self alloc] initWithView:view visiblePredicate:nil duration:duration]; 39 | } 40 | 41 | 42 | + (instancetype)fade:(UIView *)view visible:(NSPredicate *)predicate duration:(NSTimeInterval)duration { 43 | return [[self alloc] initWithView:view visiblePredicate:predicate duration:duration]; 44 | } 45 | 46 | 47 | - (void)consumeValue:(id)value { 48 | BOOL isVisible = [self.visiblePredicate evaluateWithObject:value]; 49 | 50 | if (isVisible) [self produceValue:value]; 51 | 52 | [UIView animateWithDuration:self.duration 53 | delay:0 54 | options:(UIViewAnimationOptionBeginFromCurrentState) 55 | animations:^{ 56 | self.view.alpha = (isVisible? 1 : 0); 57 | } 58 | completion:^(BOOL finished) { 59 | if ( ! isVisible) [self produceValue:value]; 60 | }]; 61 | } 62 | 63 | 64 | - (void)finishConsumingWithError:(NSError *)error { 65 | 66 | } 67 | 68 | 69 | 70 | 71 | 72 | @end 73 | 74 | 75 | -------------------------------------------------------------------------------- /Sources/UIKit/OCAUIKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAUIKit.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | 10 | 11 | #import "OCATargetter.h" 12 | #import "UIView+tintColor.h" 13 | #import "UITextField+editedText.h" 14 | #import "UIView+Gestures.h" 15 | 16 | #import "OCAContext+UIKit.h" 17 | #import "OCAFader.h" 18 | 19 | #import "OCAUIKit+UIButton.h" //TODO: Replace with Button State Consumer 20 | 21 | #import "OCATransformer+UIColor.h" 22 | #import "OCATransformer+UIImage.h" 23 | 24 | #import "OCAErrorRecoveryAttempter.h" 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/UIKit/Producers/OCATargetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATargetter.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAProducer.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCATargetter : OCAProducer 17 | 18 | 19 | 20 | - (instancetype)initWithOwner:(id)owner; 21 | 22 | + (instancetype)targetForControl:(UIControl *)control events:(UIControlEvents)events; 23 | + (instancetype)targetForBarButtonItem:(UIBarButtonItem *)barButtonItem; 24 | + (instancetype)targetGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer; 25 | 26 | @property (atomic, readonly, weak) id owner; 27 | 28 | @property (atomic, readonly, assign) SEL action; 29 | 30 | 31 | 32 | @end 33 | 34 | 35 | 36 | 37 | 38 | @interface UIControl (OCATargetter) 39 | 40 | - (OCATargetter *)producerForEvent:(UIControlEvents)event; 41 | 42 | @end 43 | 44 | 45 | 46 | 47 | 48 | @interface UIButton (OCATargetter) 49 | 50 | - (OCATargetter *)producer; 51 | 52 | @end 53 | 54 | 55 | 56 | 57 | 58 | @interface UIBarButtonItem (OCATargetter) 59 | 60 | - (OCATargetter *)producer; 61 | 62 | @end 63 | 64 | 65 | 66 | 67 | 68 | @interface UIGestureRecognizer (OCATargetter) 69 | 70 | - (OCATargetter *)producer; 71 | - (OCAProducer *)producerForState:(UIGestureRecognizerState)state; 72 | - (OCAProducer *)callback; // Recognized state. 73 | 74 | @end 75 | 76 | 77 | 78 | 79 | 80 | @interface UITextField (OCATargetter) 81 | 82 | - (OCAProducer *)producerForText; 83 | 84 | - (OCAProducer *)producerForEndEditing; 85 | 86 | @end 87 | 88 | 89 | 90 | 91 | 92 | @interface UISlider (OCATargetter) 93 | 94 | - (OCAProducer *)producerForValue; 95 | 96 | @end 97 | 98 | 99 | 100 | 101 | 102 | @interface UIStepper (OCATargetter) 103 | 104 | - (OCAProducer *)producerForValue; 105 | 106 | @end 107 | 108 | 109 | -------------------------------------------------------------------------------- /Sources/UIKit/Producers/UITextField+editedText.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+editedText.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 6.5.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | 14 | 15 | @interface UITextField (editedText) 16 | 17 | 18 | @property (nonatomic, readwrite, copy) NSString *editedText; 19 | 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /Sources/UIKit/Producers/UITextField+editedText.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+editedText.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 6.5.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCASwizzling.h" 11 | #import "UITextField+editedText.h" 12 | #import "OCATargetter.h" 13 | #import "OCAProperty.h" 14 | #import "OCAHub.h" 15 | 16 | 17 | 18 | 19 | 20 | @implementation UITextField (editedText) 21 | 22 | 23 | 24 | 25 | + (void)load { 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^{ 28 | [self swizzleSelector:@selector(initWithFrame:) 29 | with:@selector(initWithFrame_oca:)]; 30 | }); 31 | } 32 | 33 | 34 | //! Method should begin with “init”, so ARC understands the semantics. 35 | - (instancetype)initWithFrame_oca:(CGRect)frame { 36 | self = [self initWithFrame_oca:frame]; 37 | if (self) { 38 | 39 | OCAWeakify(self); 40 | 41 | [[OCAHub merge: 42 | [self producerForEvent:UIControlEventEditingChanged], 43 | OCAProperty(self, text, NSString), 44 | nil] subscribe:^{ 45 | OCAStrongify(self); 46 | 47 | self.editedText = self.text; 48 | }]; 49 | } 50 | return self; 51 | } 52 | 53 | 54 | - (NSString *)editedText { 55 | return objc_getAssociatedObject(self, _cmd); 56 | } 57 | 58 | 59 | - (void)setEditedText:(NSString *)editedText { 60 | objc_setAssociatedObject(self, @selector(editedText), editedText, OBJC_ASSOCIATION_COPY_NONATOMIC); 61 | 62 | if ( ! OCAEqual(editedText, self.text)) { 63 | self.text = editedText; 64 | } 65 | } 66 | 67 | 68 | 69 | 70 | 71 | @end 72 | 73 | 74 | -------------------------------------------------------------------------------- /Sources/UIKit/Producers/UIView+Gestures.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Gestures.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 4.12.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCATargetter.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface UIView (Gestures) 17 | 18 | 19 | - (OCAProducer *)onTap; 20 | - (OCAProducer *)onDoubleTap; 21 | - (OCAProducer *)onTwinTap; 22 | - (OCAProducer *)callbackForTaps:(NSUInteger)taps touches:(NSUInteger)touches; 23 | 24 | - (OCAProducer *)onSwipeUp; 25 | - (OCAProducer *)onSwipeDown; 26 | - (OCAProducer *)onSwipeLeft; 27 | - (OCAProducer *)onSwipeRight; 28 | - (OCAProducer *)callbackForSwipe:(UISwipeGestureRecognizerDirection)direction touches:(NSUInteger)touches; 29 | 30 | - (OCAProducer *)onHold; 31 | - (OCAProducer *)onTapAndHold; 32 | - (OCAProducer *)onHoldFor:(NSTimeInterval)duration; 33 | - (OCAProducer *)callbackForHold:(NSTimeInterval)duration taps:(NSUInteger)taps touches:(NSUInteger)touches movement:(CGFloat)allowable; 34 | 35 | - (OCAProducer *)onZoomIn; 36 | - (OCAProducer *)onZoomOut; 37 | - (OCAProducer *)onZoomAbove:(CGFloat)scale; 38 | - (OCAProducer *)onZoomBelow:(CGFloat)scale; 39 | - (OCAProducer *)callbackForZoomWithPredicate:(NSPredicate *)predicate; 40 | 41 | - (OCAProducer *)onRotateLeft; 42 | - (OCAProducer *)onRotateRight; 43 | - (OCAProducer *)onRotateLeft:(CGFloat)degrees; 44 | - (OCAProducer *)onRotateRight:(CGFloat)degrees; 45 | - (OCAProducer *)callbackForRotationWithPredicate:(NSPredicate *)predicate; 46 | 47 | 48 | @end 49 | 50 | 51 | -------------------------------------------------------------------------------- /Sources/UIKit/Producers/UIView+tintColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+tintColor.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 17.4.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCAProducer.h" 11 | 12 | 13 | 14 | 15 | @interface UIView (tintColor) 16 | 17 | 18 | @property (nonatomic, readonly, strong) OCAProducer *tintColorProducer; 19 | 20 | 21 | @end 22 | 23 | 24 | -------------------------------------------------------------------------------- /Sources/UIKit/Producers/UIView+tintColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+tintColor.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 17.4.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIView+tintColor.h" 11 | #import "OCASwizzling.h" 12 | #import "OCACommand.h" 13 | 14 | 15 | 16 | 17 | 18 | @implementation UIView (tintColor) 19 | 20 | 21 | 22 | 23 | 24 | + (void)load { 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | [self swizzleSelector:@selector(tintColorDidChange) 28 | with:@selector(oca_tintColorDidChange)]; 29 | }); 30 | } 31 | 32 | 33 | - (void)oca_tintColorDidChange { 34 | [self oca_tintColorDidChange]; // Calling original implementation. 35 | 36 | OCACommand *command = [self oca_tintColorCommandAndCreateIfNone:NO]; 37 | [command sendValue:self.tintColor]; 38 | } 39 | 40 | 41 | - (OCACommand *)oca_tintColorCommandAndCreateIfNone:(BOOL)create { 42 | OCACommand *command = objc_getAssociatedObject(self, _cmd); 43 | 44 | if ( ! command && create) { 45 | command = [OCACommand commandForClass:[UIColor class]]; 46 | [command sendValue:self.tintColor]; // Initial value. 47 | objc_setAssociatedObject(self, _cmd, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 48 | } 49 | return command; 50 | } 51 | 52 | 53 | - (OCAProducer *)tintColorProducer { 54 | return [self oca_tintColorCommandAndCreateIfNone:YES]; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | @end 62 | 63 | 64 | -------------------------------------------------------------------------------- /Sources/UIKit/Transformers/OCATransformer+UIColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+UIColor.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCATransformer+Base.h" 11 | 12 | 13 | 14 | 15 | 16 | @interface OCATransformer (UIColor) 17 | 18 | 19 | 20 | + (OCATransformer *)colorFromCGColor; 21 | + (OCATransformer *)colorGetCGColor; 22 | 23 | + (OCATransformer *)colorWithAlpha:(CGFloat)alpha; 24 | 25 | 26 | 27 | @end 28 | 29 | 30 | -------------------------------------------------------------------------------- /Sources/UIKit/Transformers/OCATransformer+UIColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+UIColor.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 15.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCATransformer+UIColor.h" 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @implementation OCATransformer (UIColor) 21 | 22 | 23 | 24 | 25 | 26 | + (OCATransformer *)colorFromCGColor { 27 | return [[OCATransformer colorGetCGColor] reversed]; 28 | } 29 | 30 | 31 | + (OCATransformer *)colorGetCGColor { 32 | return [[OCATransformer fromClass:[UIColor class] toClass:nil 33 | transform:^id(UIColor *input) { 34 | 35 | return (id)input.CGColor; 36 | 37 | } reverse:^UIColor *(id input) { 38 | 39 | CGColorRef color = (__bridge CGColorRef)input; 40 | return [UIColor colorWithCGColor:color]; 41 | }] 42 | describe:@"CGColor from UIColor" 43 | reverse:@"UIColor from CGColor"]; 44 | } 45 | 46 | 47 | + (OCATransformer *)colorWithAlpha:(CGFloat)alpha { 48 | return [[OCATransformer fromClass:[UIColor class] toClass:[UIColor class] 49 | transform:^UIColor *(UIColor *input) { 50 | return [input colorWithAlphaComponent:alpha]; 51 | 52 | } reverse:OCATransformationPass] 53 | describe:[NSString stringWithFormat:@"Color with aplha %@", @(alpha)] 54 | reverse:@"pass"]; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | @end 62 | 63 | 64 | -------------------------------------------------------------------------------- /Sources/UIKit/Transformers/OCATransformer+UIImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCATransformer+UIImage.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 12.5.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "OCATransformer+Base.h" 13 | 14 | 15 | 16 | 17 | 18 | @interface OCATransformer (UIImage) 19 | 20 | 21 | 22 | + (OCATransformer *)resizeImageTo:(CGSize)size scale:(CGFloat)scale; 23 | //TODO: +resizeImageTo:mode: with UIViewContentMode 24 | 25 | + (OCATransformer *)clipImageTo:(UIBezierPath *)path; 26 | + (OCATransformer *)clipImageToCircle; 27 | 28 | + (OCATransformer *)setImageRenderingMode:(UIImageRenderingMode)mode; 29 | 30 | + (OCATransformer *)filterImage:(CIFilter *)filter, ... NS_REQUIRES_NIL_TERMINATION; 31 | 32 | 33 | 34 | @end 35 | 36 | 37 | -------------------------------------------------------------------------------- /Sources/UIKit/Utilities/OCAErrorRecoveryAttempter.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAErrorRecoveryAttempter.h 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 24.4.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "OCAProducer.h" 11 | #import "OCATransformer.h" 12 | 13 | 14 | //! Inspired by https://github.com/realmacsoftware/RMErrorRecoveryAttempter 15 | 16 | 17 | 18 | @interface OCAErrorRecoveryAttempter : OCAObject 19 | 20 | 21 | 22 | @property (atomic, readwrite, copy) NSString *failureReason; 23 | @property (atomic, readwrite, copy) NSString *recoverySuggestion; 24 | 25 | - (OCAProducer *)addRecoveryOptionWithTitle:(NSString *)title; 26 | - (OCAProducer *)recoveryOptionAtIndex:(NSUInteger)index; 27 | 28 | - (NSString *)recoveryOptionTitles; 29 | 30 | - (NSError *)recoverableError:(NSError *)error; 31 | 32 | 33 | 34 | @end 35 | 36 | 37 | -------------------------------------------------------------------------------- /Sources/UIKit/Utilities/OCAErrorRecoveryAttempter.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAErrorRecoveryAttempter.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 24.4.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAErrorRecoveryAttempter.h" 10 | #import "OCACommand.h" 11 | @import ObjectiveC.message; 12 | 13 | 14 | 15 | 16 | 17 | @interface OCAErrorRecoveryAttempter () 18 | 19 | @property (atomic, readonly, strong) NSMutableArray *titles; 20 | @property (atomic, readonly, strong) NSMutableArray *options; 21 | 22 | @end 23 | 24 | 25 | 26 | 27 | 28 | @implementation OCAErrorRecoveryAttempter 29 | 30 | 31 | 32 | 33 | 34 | - (instancetype)init { 35 | self = [super init]; 36 | if (self) { 37 | self->_titles = [[NSMutableArray alloc] init]; 38 | self->_options = [[NSMutableArray alloc] init]; 39 | } 40 | return self; 41 | } 42 | 43 | 44 | 45 | 46 | 47 | - (OCAProducer *)addRecoveryOptionWithTitle:(NSString *)title { 48 | OCACommand *option = [OCACommand commandForClass:[NSError class]]; 49 | [self.titles addObject:title]; 50 | [self.options addObject:option]; 51 | return option; 52 | } 53 | 54 | 55 | - (OCAProducer *)recoveryOptionAtIndex:(NSUInteger)index { 56 | return [self.options objectAtIndex:index]; 57 | } 58 | 59 | 60 | 61 | 62 | 63 | - (NSString *)recoveryOptionTitles { 64 | return [self.titles copy]; 65 | } 66 | 67 | 68 | - (NSError *)recoverableError:(NSError *)error { 69 | NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo]; 70 | [userInfo setObject:self forKey:NSRecoveryAttempterErrorKey]; 71 | [userInfo setObject:[self recoveryOptionTitles] forKey:NSLocalizedRecoveryOptionsErrorKey]; 72 | if (self.failureReason.length) [userInfo setObject:self.failureReason forKey:NSLocalizedFailureReasonErrorKey]; 73 | if (self.recoverySuggestion.length) [userInfo setObject:self.recoverySuggestion forKey:NSLocalizedRecoverySuggestionErrorKey]; 74 | return [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; 75 | } 76 | 77 | 78 | 79 | 80 | 81 | - (BOOL)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex { 82 | OCACommand *option = [self.options objectAtIndex:recoveryOptionIndex]; 83 | [option sendValue:error]; 84 | 85 | return YES; // We don't have mechanism to report this. 86 | } 87 | 88 | 89 | - (void)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex delegate:(id)delegate didRecoverSelector:(SEL)didRecoverSelector contextInfo:(void *)contextInfo { 90 | OCACommand *option = [self.options objectAtIndex:recoveryOptionIndex]; 91 | [option sendValue:error]; 92 | 93 | void (*didRecoverMessage)(id, SEL, BOOL, void *) = (typeof(didRecoverMessage))objc_msgSend; 94 | didRecoverMessage(delegate, didRecoverSelector, YES, contextInfo); 95 | // ... don't ask me. 96 | } 97 | 98 | 99 | 100 | 101 | 102 | @end 103 | 104 | 105 | -------------------------------------------------------------------------------- /Tests/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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/OCAFilterTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAFilterTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 2.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAFilter.h" 10 | #import "OCACommand.h" 11 | #import "OCASubscriber.h" 12 | #import "OCAPredicate.h" 13 | 14 | 15 | 16 | 17 | 18 | @interface OCAFilterTest : XCTestCase 19 | 20 | 21 | @property (nonatomic, readwrite, strong) OCACommand *command; 22 | @property (nonatomic, readwrite, strong) OCASubscriber *subscriber; 23 | @property (nonatomic, readwrite, strong) NSMutableArray *received; 24 | 25 | 26 | @end 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @implementation OCAFilterTest 38 | 39 | 40 | 41 | 42 | - (void)setUp { 43 | [super setUp]; 44 | 45 | self.command = [OCACommand commandForClass:[NSString class]]; 46 | NSMutableArray *received = [[NSMutableArray alloc] init]; 47 | self.received = received; 48 | self.subscriber = [OCASubscriber subscribeForClass:[NSString class] handler:^(NSString *value) { 49 | [received addObject:value]; 50 | }]; 51 | } 52 | 53 | 54 | - (void)test_simplePredicate { 55 | [[self.command 56 | filterValues:[OCAPredicate beginsWith:@"A"]] 57 | connectTo:self.subscriber]; 58 | 59 | [self.command sendValues:@[ @"Hello", @"Aloha", @"Hi", @"Ahoy" ]]; 60 | 61 | NSArray *expected = @[ @"Aloha", @"Ahoy" ]; 62 | XCTAssertEqualObjects(self.received, expected); 63 | } 64 | 65 | 66 | - (void)test_skipFirst { 67 | [[self.command 68 | skipFirst:2] 69 | connectTo:self.subscriber]; 70 | 71 | [self.command sendValues:@[ @"Hello", @"Aloha", @"Hi", @"Ahoy" ]]; 72 | 73 | NSArray *expected = @[ @"Hi", @"Ahoy" ]; 74 | XCTAssertEqualObjects(self.received, expected); 75 | } 76 | 77 | 78 | - (void)test_skipEqual { 79 | [[self.command 80 | skipEqual] 81 | connectTo:self.subscriber]; 82 | 83 | [self.command sendValues:@[ @"Hello", @"Hello", @"Hi", @"Hello", @"Hi", @"Hi" ]]; 84 | 85 | NSArray *expected = @[ @"Hello", @"Hi", @"Hello", @"Hi" ]; 86 | XCTAssertEqualObjects(self.received, expected); 87 | } 88 | 89 | 90 | 91 | 92 | @end 93 | 94 | 95 | -------------------------------------------------------------------------------- /Tests/OCAHubTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAHubTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 1.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAHub.h" 10 | #import "OCACommand.h" 11 | #import "OCASubscriber.h" 12 | #import "OCAVariadic.h" 13 | 14 | 15 | 16 | 17 | 18 | @interface OCAHubTest : XCTestCase 19 | 20 | @property (nonatomic, readwrite, strong) OCACommand *commandA; 21 | @property (nonatomic, readwrite, strong) OCACommand *commandB; 22 | @property (nonatomic, readwrite, strong) OCACommand *commandC; 23 | @property (nonatomic, readwrite, strong) NSArray *commands; 24 | 25 | @end 26 | 27 | 28 | 29 | 30 | 31 | @implementation OCAHubTest 32 | 33 | 34 | 35 | 36 | 37 | - (void)setUp { 38 | [super setUp]; 39 | 40 | self.commandA = [OCACommand commandForClass:[NSString class]]; 41 | self.commandB = [OCACommand commandForClass:[NSString class]]; 42 | self.commandC = [OCACommand commandForClass:[NSString class]]; 43 | self.commands = @[ self.commandA, self.commandB, self.commandC ]; 44 | } 45 | 46 | 47 | - (void)test_merge { 48 | OCAHub *hubMerge = [OCAHub merge:OCAVariadic(self.commands)]; 49 | XCTAssertEqualObjects(hubMerge.valueClass, [NSString class], @"Merging hub should know class."); 50 | 51 | NSMutableArray *received = [NSMutableArray array]; 52 | [hubMerge subscribeForClass:[NSString class] handler:^(id value) { 53 | [received addObject:value]; 54 | }]; 55 | 56 | [self.commandA sendValue:@"A"]; 57 | [self.commandB sendValue:@"B"]; 58 | [self.commandC sendValue:@"C"]; 59 | 60 | NSArray *expected = @[ @"A", @"B", @"C" ]; 61 | XCTAssertEqualObjects(received, expected); 62 | } 63 | 64 | 65 | - (void)test_combine { 66 | OCAHub *hubCombine = [OCAHub combine:OCAVariadic(self.commands)]; 67 | XCTAssertEqualObjects(hubCombine.valueClass, [NSArray class], @"Combining hub must produce arrays."); 68 | 69 | NSMutableArray *received = [NSMutableArray array]; 70 | [hubCombine subscribeForClass:[NSArray class] handler:^(id value) { 71 | [received addObject:value]; 72 | }]; 73 | 74 | [self.commandA sendValue:@"A"]; 75 | [self.commandB sendValue:@"B"]; 76 | [self.commandC sendValue:@"C"]; 77 | 78 | NSArray *expected = @[ @[ @"A", NSNull.null, NSNull.null ], 79 | @[ @"A", @"B", NSNull.null ], 80 | @[ @"A", @"B", @"C" ] ]; 81 | XCTAssertEqualObjects(received, expected); 82 | } 83 | 84 | 85 | - (void)test_dependency { 86 | OCAHub *hubDependency = [self.commandA dependOn:self.commandB, self.commandC, nil]; 87 | XCTAssertEqualObjects(hubDependency.valueClass, [NSString class], @"Dependency hub should know class."); 88 | 89 | NSMutableArray *received = [NSMutableArray array]; 90 | [hubDependency subscribeForClass:[NSString class] handler:^(id value) { 91 | [received addObject:value]; 92 | }]; 93 | 94 | [self.commandA sendValue:@"A"]; 95 | [self.commandB sendValue:@"B"]; 96 | [self.commandC sendValue:@"C"]; 97 | 98 | NSArray *expected = @[ @"A", @"A", @"A" ]; 99 | XCTAssertEqualObjects(received, expected); 100 | } 101 | 102 | 103 | //TODO: Test finishing. 104 | 105 | 106 | 107 | 108 | 109 | @end 110 | 111 | 112 | -------------------------------------------------------------------------------- /Tests/OCAInterpolatorTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInterpolatorTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Juraj Homola on 26.2.2014. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAInterpolator.h" 10 | #import "OCAQueue.h" 11 | #import "OCAContext.h" 12 | #import "OCASemaphore.h" 13 | 14 | @interface OCAInterpolatorTest : XCTestCase 15 | 16 | @end 17 | 18 | 19 | @implementation OCAInterpolatorTest 20 | 21 | 22 | - (void)test_correctOrder { 23 | 24 | NSMutableArray *array = [[NSMutableArray alloc] init]; 25 | 26 | OCASemaphore *semaphore = [[OCASemaphore alloc] initWithValue:0]; 27 | [[OCAQueue background] performBlockAndWait:^{ 28 | 29 | [[OCAInterpolator interpolatorWithDuration:1 frequency:10] subscribeForClass:[NSNumber class] handler:^(NSNumber *value) { 30 | [array addObject:value]; 31 | } finish:^(NSError *error) { 32 | [semaphore signal]; 33 | }]; 34 | }]; 35 | 36 | BOOL signaled = [semaphore waitFor:2]; 37 | XCTAssertTrue(signaled); 38 | NSArray *sortedArray = [array sortedArrayUsingSelector:@selector(compare:)]; 39 | XCTAssertTrue(array.count == 11); 40 | XCTAssertEqualObjects(sortedArray, array); 41 | } 42 | 43 | 44 | - (void)test_durationZero { 45 | 46 | NSMutableArray *array = [[NSMutableArray alloc] init]; 47 | 48 | OCASemaphore *semaphore = [[OCASemaphore alloc] initWithValue:0]; 49 | [[OCAQueue background] performBlockAndWait:^{ 50 | 51 | [[OCAInterpolator interpolatorWithDuration:0 frequency:100] subscribeForClass:[NSNumber class] handler:^(NSNumber *value) { 52 | [array addObject:value]; 53 | } finish:^(NSError *error) { 54 | [semaphore signal]; 55 | }]; 56 | }]; 57 | 58 | BOOL signaled = [semaphore waitFor:3]; 59 | XCTAssertTrue(signaled); 60 | XCTAssertTrue(array.count == 1); 61 | 62 | NSArray *expected = @[ @1 ]; 63 | XCTAssertEqualObjects(array, expected); 64 | } 65 | 66 | 67 | - (void)test_frequencyZero { 68 | NSMutableArray *array = [[NSMutableArray alloc] init]; 69 | 70 | OCASemaphore *semaphore = [[OCASemaphore alloc] initWithValue:0]; 71 | [[OCAQueue background] performBlockAndWait:^{ 72 | 73 | [[OCAInterpolator interpolatorWithDuration:2 frequency:0] subscribeForClass:[NSNumber class] handler:^(NSNumber *value) { 74 | [array addObject:value]; 75 | } finish:^(NSError *error) { 76 | [semaphore signal]; 77 | }]; 78 | }]; 79 | 80 | BOOL signaled = [semaphore waitFor:3]; 81 | XCTAssertTrue(signaled); 82 | XCTAssertTrue(array.count == 2); 83 | 84 | NSArray *expected = @[ @0, @1 ]; 85 | XCTAssertEqualObjects(array, expected); 86 | } 87 | 88 | 89 | - (void)test_correctOrderCustom { 90 | NSMutableArray *array = [[NSMutableArray alloc] init]; 91 | 92 | OCASemaphore *semaphore = [[OCASemaphore alloc] initWithValue:0]; 93 | [[OCAQueue background] performBlockAndWait:^{ 94 | 95 | [[OCAInterpolator interpolatorWithDuration:1 frequency:3 fromValue:3.2 toValue:-4.5] 96 | subscribeForClass:[NSNumber class] 97 | handler:^(NSNumber *value) { 98 | [array addObject:value]; 99 | } finish:^(NSError *error) { 100 | [semaphore signal]; 101 | }]; 102 | }]; 103 | 104 | BOOL signaled = [semaphore waitFor:2]; 105 | XCTAssertTrue(signaled); 106 | NSArray *sortedArray = [array sortedArrayUsingDescriptors:@[ [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO] ]]; 107 | XCTAssertTrue(array.count == 4); 108 | XCTAssertEqualObjects(sortedArray, array); 109 | } 110 | 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /Tests/OCAInvokerTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAInvokerTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 3.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAInvoker.h" 10 | #import "OCACommand.h" 11 | #import "OCAInvocationCatcher.h" 12 | 13 | 14 | 15 | 16 | 17 | @interface OCAInvokerTest : XCTestCase 18 | 19 | 20 | @property (atomic, readwrite, strong) NSString *value; 21 | 22 | 23 | @end 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | @implementation OCAInvokerTest 35 | 36 | 37 | 38 | 39 | 40 | - (void)setUp { 41 | [super setUp]; 42 | 43 | self.value = nil; 44 | } 45 | 46 | 47 | - (void)test_simple { 48 | OCACommand *command = [OCACommand commandForClass:nil]; 49 | [command invoke:OCAInvocation(self, setValue:@"ABC")]; 50 | [command sendValue:nil]; 51 | XCTAssertEqualObjects(self.value, @"ABC"); 52 | } 53 | 54 | 55 | - (void)test_placeholders { 56 | OCACommand *command = [OCACommand commandForClass:[NSArray class]]; 57 | 58 | @autoreleasepool { 59 | //! Autorelease pool causes the placeholders to be released, so they must be retained by the Invoker. 60 | [command invoke:OCAInvocation( OCAPH(OCAInvokerTest), setValue: OCAPH(NSString) )]; 61 | } 62 | 63 | [command sendValue:@[ self, @"ABC" ]]; 64 | XCTAssertEqualObjects(self.value, @"ABC"); 65 | } 66 | 67 | 68 | - (void)test_nilTarget { 69 | id coolObject = nil; 70 | XCTAssertNoThrow(OCAInvocation(coolObject, test_nilTarget), "Macro should return nil when the target is nil."); 71 | } 72 | 73 | 74 | 75 | 76 | 77 | @end 78 | 79 | 80 | -------------------------------------------------------------------------------- /Tests/OCAMathTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAMathTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 2.1.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAMath.h" 10 | 11 | 12 | 13 | @interface OCAMathTest : XCTestCase 14 | 15 | @end 16 | 17 | 18 | 19 | 20 | 21 | @implementation OCAMathTest 22 | 23 | 24 | 25 | 26 | 27 | - (void)test_generic { 28 | OCATransformer *t = [OCAMath transform:^OCAReal(OCAReal x) { 29 | return (x * 4 - 5) / 3; 30 | } reverse:^OCAReal(OCAReal x) { 31 | return (x * 3 + 5) / 4; 32 | }]; 33 | 34 | XCTAssertEqualObjects([t transformedValue:@8], @9); 35 | XCTAssertEqualObjects([t reverseTransformedValue:@9], @8); 36 | } 37 | 38 | 39 | - (void)test_function { 40 | OCATransformer *cosine = [OCAMath function:&cos reverse:&acos]; 41 | XCTAssertEqualObjects([cosine transformedValue:@0], @1); 42 | XCTAssertEqualObjects([cosine reverseTransformedValue:@1], @0); 43 | } 44 | 45 | 46 | - (void)test_basicOperations { 47 | OCATransformer *t = [OCATransformer sequence:@[ 48 | [OCAMath add:3], 49 | [OCAMath multiplyBy:4], 50 | [OCAMath divideBy:2], 51 | [OCAMath subtract:5], 52 | ]]; 53 | XCTAssertEqualObjects([t transformedValue:@8], @17); 54 | XCTAssertEqualObjects([t reverseTransformedValue:@17], @8); 55 | } 56 | 57 | 58 | - (void)test_advancedOperations { 59 | OCATransformer *t = [OCATransformer sequence:@[ 60 | [OCAMath powerBy:4], 61 | [OCAMath rootOf:2], 62 | [OCAMath logarithmWithBase:10], 63 | ]]; 64 | XCTAssertEqualObjects([t transformedValue:@10], @2); 65 | XCTAssertEqualObjects([t reverseTransformedValue:@2], @10); 66 | } 67 | 68 | 69 | - (void)test_trigonometry { 70 | OCATransformer *t = [OCATransformer sequence:@[ 71 | [OCAMath toRadians], 72 | [OCAMath sine], 73 | [OCAMath powerBy:2], 74 | ]]; 75 | const OCAReal accuracy = 1e-14; // May need increase for more lossy calculations. 76 | XCTAssertEqualWithAccuracy([[t transformedValue:@45] doubleValue], 0.5, accuracy); 77 | XCTAssertEqualWithAccuracy([[t reverseTransformedValue:@0.5] doubleValue], 45, accuracy); 78 | } 79 | 80 | 81 | 82 | 83 | 84 | @end 85 | 86 | 87 | -------------------------------------------------------------------------------- /Tests/OCAObjectTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAObjectTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright © 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAObject.h" 10 | #import "ObjectiveChain.h" 11 | 12 | 13 | 14 | @interface OCAObjectTest : XCTestCase 15 | 16 | 17 | @property (nonatomic, readwrite, strong) id lazyProperty; 18 | 19 | 20 | @end 21 | 22 | 23 | 24 | 25 | 26 | @implementation OCAObjectTest 27 | 28 | 29 | 30 | 31 | 32 | - (void)setUp { 33 | [super setUp]; 34 | } 35 | 36 | 37 | - (void)tearDown{ 38 | [super tearDown]; 39 | } 40 | 41 | 42 | 43 | 44 | 45 | #pragma mark OCAssert 46 | 47 | 48 | - (void)test_OCAAssert_executeAppendedCode { 49 | #if defined(NS_BLOCK_ASSERTIONS) 50 | BOOL condition = NO; 51 | BOOL executed = NO; 52 | 53 | OCAAssert(condition, @"Condition is not met.") executed = YES; 54 | 55 | XCTAssertTrue(executed, @"Didn't execute appended code."); 56 | #else 57 | XCTFail(@"Foundation assertions are enabled."); 58 | #endif 59 | 60 | } 61 | 62 | 63 | 64 | - (void)test_validateClass_nilAlwaysPasses { 65 | id object = nil; 66 | XCTAssertTrue([OCAObject validateObject:&object ofClass:[NSMetadataQueryAttributeValueTuple class]], @"Nil must pass any class validation."); 67 | XCTAssertTrue([OCAObject validateObject:&object ofClass:nil], @"Nil must pass any class validation."); 68 | object = @"test"; 69 | XCTAssertTrue([OCAObject validateObject:&object ofClass:[NSString class]]); 70 | XCTAssertTrue([OCAObject validateObject:&object ofClass:nil], @"Anything must pass for Nil class validation."); 71 | } 72 | 73 | 74 | - (void)test_findingCommonClass { 75 | NSArray *classes = @[ NSString.class, NSMutableString.class, NSString.class ]; 76 | XCTAssertEqualObjects([OCAObject valueClassForClasses:classes], NSString.class, @"Should be highest of all concrete classes."); 77 | } 78 | 79 | 80 | - (void)test_deallocation { 81 | __block BOOL passed = NO; 82 | NSBlockOperation *something = [[NSBlockOperation alloc] init]; 83 | 84 | [something.decomposer addOwnedObject:self cleanup:^(__unsafe_unretained NSBlockOperation *owner){ 85 | [owner.decomposer removeOwnedObject:self]; // This line should not crash. 86 | 87 | passed = YES; 88 | }]; 89 | something = nil; // Should dealloc. 90 | 91 | XCTAssertTrue(passed, @"Block associated with decomposer should be invoked when owner is deallocated."); 92 | } 93 | 94 | 95 | 96 | 97 | 98 | @end 99 | 100 | 101 | -------------------------------------------------------------------------------- /Tests/OCASwitchTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCASwitchTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 4.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCASwitch.h" 10 | #import "OCACommand.h" 11 | #import "OCASubscriber.h" 12 | #import "OCAPredicate.h" 13 | 14 | 15 | 16 | 17 | 18 | @interface OCASwitchTest : XCTestCase 19 | 20 | @end 21 | 22 | 23 | 24 | 25 | 26 | @implementation OCASwitchTest 27 | 28 | 29 | 30 | 31 | 32 | - (void)test_ { 33 | __block NSNumber *result = nil; 34 | OCACommand *command = [OCACommand commandForClass:[NSString class]]; 35 | [command switchIf:[OCAPredicate beginsWith:@"A"] 36 | then:[OCASubscriber subscribe: 37 | ^{ 38 | result = @YES; 39 | }] 40 | else:[OCASubscriber subscribe: 41 | ^{ 42 | result = @NO; 43 | }]]; 44 | [command sendValue:@"Aloha"]; 45 | XCTAssertTrue(result.boolValue); 46 | 47 | [command sendValue:@"Hello"]; 48 | XCTAssertFalse(result.boolValue); 49 | } 50 | 51 | 52 | 53 | 54 | 55 | @end 56 | 57 | 58 | -------------------------------------------------------------------------------- /Tests/OCAThrottleTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAThrottleTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 2.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAThrottle.h" 10 | #import "OCACommand.h" 11 | #import "OCASubscriber.h" 12 | #import "OCAQueue.h" 13 | #import "OCASemaphore.h" 14 | 15 | 16 | 17 | 18 | 19 | @interface OCAThrottleTest : XCTestCase 20 | 21 | @end 22 | 23 | 24 | 25 | 26 | 27 | @implementation OCAThrottleTest 28 | 29 | 30 | 31 | 32 | - (void)test_nonContinuousThrottle { 33 | OCACommand *command = [OCACommand commandForClass:[NSNumber class]]; 34 | 35 | NSMutableArray *received = [[NSMutableArray alloc] init]; 36 | [[command throttle:0.05] 37 | subscribeForClass:[NSNumber class] handler:^(NSNumber *value) { 38 | @synchronized(received) { 39 | [received addObject:value]; 40 | } 41 | }]; 42 | OCASemaphore *semaphore = [[OCASemaphore alloc] initWithValue:0]; 43 | 44 | [[OCAQueue background] performBlockAndWait:^{ 45 | for (NSUInteger index = 0; index <= 100; index++) { 46 | [command sendValue:@(index)]; 47 | [NSThread sleepForTimeInterval:0.01]; 48 | } 49 | [[OCAQueue background] performAfter:0.1 block:^{ 50 | [semaphore signal]; 51 | }]; 52 | }]; 53 | 54 | BOOL signaled = [semaphore waitFor:2]; 55 | XCTAssertTrue(signaled, @"Task didn't finish in time."); 56 | 57 | NSArray *expected = @[ @100 ]; 58 | XCTAssertEqualObjects(received, expected, @"Should receive only last value."); 59 | } 60 | 61 | 62 | - (void)test_continuousThrottle { 63 | OCACommand *command = [OCACommand commandForClass:[NSNumber class]]; 64 | 65 | NSMutableArray *received = [[NSMutableArray alloc] init]; 66 | [[command throttleContinuous:0.1] 67 | subscribeForClass:[NSNumber class] handler:^(NSNumber *value) { 68 | @synchronized(received) { 69 | [received addObject:value]; 70 | } 71 | }]; 72 | OCASemaphore *semaphore = [[OCASemaphore alloc] initWithValue:0]; 73 | 74 | [[OCAQueue background] performBlockAndWait:^{ 75 | for (NSUInteger index = 0; index <= 100; index++) { 76 | [command sendValue:@(index)]; 77 | [NSThread sleepForTimeInterval:0.01 + 0.0001]; // Give him some more time to have exactly 10th values. 78 | } 79 | [[OCAQueue background] performAfter:0.1 block:^{ 80 | [semaphore signal]; 81 | }]; 82 | }]; 83 | 84 | BOOL signaled = [semaphore waitFor:2]; 85 | XCTAssertTrue(signaled, @"Task didn't finish in time."); 86 | 87 | NSArray *expected = @[ @0, @10, @20, @30, @40, @50, @60, @70, @80, @90, @100 ]; 88 | XCTAssertEqualObjects(received, expected, @"Should receive a value every 0.1s, so every 10th."); 89 | } 90 | 91 | 92 | 93 | 94 | @end 95 | 96 | 97 | -------------------------------------------------------------------------------- /Tests/OCAVariadicTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAVariadicTest.m 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 1.2.14. 6 | // Copyright (c) 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import "OCAVariadic.h" 10 | 11 | 12 | 13 | 14 | @interface OCAVariadicTest : XCTestCase 15 | 16 | @end 17 | 18 | 19 | 20 | 21 | 22 | @implementation OCAVariadicTest 23 | 24 | 25 | 26 | 27 | 28 | - (void)test_stringFormatting { 29 | NSString *result = [self format:@"A %@ %@", @"B", @"C"]; 30 | XCTAssertEqualObjects(result, @"A B C"); 31 | } 32 | 33 | 34 | - (NSString *)format:(NSString *)format, ... NS_FORMAT_ARGUMENT(1) { 35 | return OCAStringFromFormat(format); 36 | } 37 | 38 | 39 | 40 | 41 | 42 | - (void)test_variadic_simple { 43 | NSArray *result = [self variadic:@"A", @"B", nil]; 44 | NSArray *expected = @[ @"A", @"B" ]; 45 | XCTAssertEqualObjects(result, expected); 46 | } 47 | 48 | 49 | - (void)test_variadic_array { 50 | NSArray *expected = @[ @"A", @"B" ]; 51 | NSArray *result = [self variadic:OCAVariadic(expected)]; 52 | XCTAssertEqualObjects(result, expected); 53 | } 54 | 55 | 56 | - (void)test_variadic_mixed { 57 | NSArray *tail = @[ @"C", @"D" ]; 58 | NSArray *result = [self variadic:@"A", @"B", OCAVariadic(tail)]; 59 | NSArray *expected = @[ @"A", @"B", @"C", @"D" ]; 60 | XCTAssertEqualObjects(result, expected); 61 | } 62 | 63 | 64 | - (NSArray *)variadic:(NSString *)string, ... NS_REQUIRES_NIL_TERMINATION { 65 | return OCAArrayFromVariadicArguments(string); 66 | } 67 | 68 | 69 | 70 | 71 | 72 | @end 73 | 74 | 75 | -------------------------------------------------------------------------------- /Tests/Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix.pch 3 | // Objective-Chain 4 | // 5 | // Created by Martin Kiss on 30.12.13. 6 | // Copyright 2014 Martin Kiss. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | --------------------------------------------------------------------------------