├── .travis.yml ├── Assets └── logo.sketch │ ├── Data │ ├── QuickLook │ ├── Preview.png │ └── Thumbnail.png │ ├── fonts │ ├── metadata │ └── version ├── CHANGELOG.md ├── Classes ├── NSArray+ObjectiveSugar.h ├── NSArray+ObjectiveSugar.m ├── NSDictionary+ObjectiveSugar.h ├── NSDictionary+ObjectiveSugar.m ├── NSMutableArray+ObjectiveSugar.h ├── NSMutableArray+ObjectiveSugar.m ├── NSNumber+ObjectiveSugar.h ├── NSNumber+ObjectiveSugar.m ├── NSSet+ObjectiveSugar.h ├── NSSet+ObjectiveSugar.m ├── NSString+ObjectiveSugar.h ├── NSString+ObjectiveSugar.m └── ObjectiveSugar.h ├── Example ├── .gitignore ├── Makefile ├── ObjectiveSugar.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── ObjectiveSugar.xcscheme ├── ObjectiveSugar.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── ObjectiveSugar.xccheckout ├── ObjectiveSugar │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── ObjectiveSugar-Info.plist │ ├── ObjectiveSugar-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── ObjectiveSugarTests │ ├── CAdditionsTests.m │ ├── NSArrayTests.m │ ├── NSDictionaryTests.m │ ├── NSMutableArrayTests.m │ ├── NSNumberTests.m │ ├── NSSetTests.m │ ├── NSStringTests.m │ ├── ObjectiveSugarTests-Info.plist │ └── en.lproj │ │ └── InfoPlist.strings ├── Podfile └── Podfile.lock ├── LICENSE ├── ObjectiveSugar.podspec └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | before_install: 4 | - cd Example 5 | 6 | install: make install 7 | 8 | script: make ci 9 | 10 | -------------------------------------------------------------------------------- /Assets/logo.sketch/Data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supermarin/ObjectiveSugar/3d1c39adb701938ce2e19f8e5a789c72450f18ed/Assets/logo.sketch/Data -------------------------------------------------------------------------------- /Assets/logo.sketch/QuickLook/Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supermarin/ObjectiveSugar/3d1c39adb701938ce2e19f8e5a789c72450f18ed/Assets/logo.sketch/QuickLook/Preview.png -------------------------------------------------------------------------------- /Assets/logo.sketch/QuickLook/Thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supermarin/ObjectiveSugar/3d1c39adb701938ce2e19f8e5a789c72450f18ed/Assets/logo.sketch/QuickLook/Thumbnail.png -------------------------------------------------------------------------------- /Assets/logo.sketch/fonts: -------------------------------------------------------------------------------- 1 | HelveticaNeue-UltraLight -------------------------------------------------------------------------------- /Assets/logo.sketch/metadata: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app 6 | com.bohemiancoding.sketch 7 | build 8 | 5302 9 | commit 10 | 9460a4bc62af5e9ba50dd4143578fd9401710ce5 11 | version 12 | 18 13 | 14 | 15 | -------------------------------------------------------------------------------- /Assets/logo.sketch/version: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # master 2 | ##### Enhancements 3 | - added `-reduce` method on NSArray and NSSet 4 | 5 | ##### Bug Fixes 6 | 7 | - NSMutableArray -keepIf was mutating the collection while iterating 8 | - !!BREAKING `-map` on NSArray now puts NSNulls if the block returns nil 9 | - !!BREAKING `-map` on NSSet doesn't blow up if the block returns nil. 10 | 11 | 12 | # 1.1.1 13 | 14 | ##### Bug Fixes 15 | 16 | - Array range accesors with backwards range (e.g. array[@"0..-2"]) 17 | 18 | # 1.1.0 19 | 20 | ##### Enhancements 21 | 22 | - `until` keyword, and improved `unless` (it allows you to pass args) | 23 | [C0deH4cker #61](https://github.com/supermarin/ObjectiveSugar/pull/61) 24 | - Force the library to load under ARC | 25 | [mickeyreiss #62](https://github.com/supermarin/ObjectiveSugar/pull/62) 26 | 27 | # 1.0.0 28 | 29 | * The library is stable, warning-less and production-ready 30 | 31 | * Thanks everybody for contributing and all the great PRs. 32 | Since the library is stable enough and we almost never had to fix things, 33 | it's time to release 1.0. 34 | 35 | * Documentation could be better, and we encourage you to write some. 36 | Thanks [@orta](https://github.com/orta) for initiating that. 37 | 38 | -------------------------------------------------------------------------------- /Classes/NSArray+ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+ObjectiveSugar.h 3 | // Objective Sugar 4 | // 5 | // Created by Marin Usalj on 5/7/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | // For an overview see http://cocoadocs.org/docsets/ObjectiveSugar/ 10 | 11 | #import 12 | 13 | @interface NSArray (ObjectiveSugar) 14 | 15 | /** 16 | The first item in the array, or nil. 17 | 18 | @return The first item in the array, or nil. 19 | */ 20 | - (id)first DEPRECATED_MSG_ATTRIBUTE("Please use -firstObject instead"); 21 | 22 | /** 23 | The last item in the array, or nil. 24 | 25 | @return The last item in the array, or nil. 26 | */ 27 | - (id)last DEPRECATED_MSG_ATTRIBUTE("Please use -lastObject instead"); 28 | 29 | /** 30 | A random element in the array, or nil. 31 | 32 | @return A random element in the array, or nil. 33 | */ 34 | - (id)sample; 35 | 36 | /// Alias for -sample 37 | - (id)anyObject; 38 | 39 | 40 | /** 41 | Allow subscripting to fetch elements within the specified range 42 | 43 | @param key An NSString or NSValue wrapping an NSRange. It's intended to behave like Ruby's array range accessors. 44 | 45 | Given array of 10 elements, e.g. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], you can perform these operations: 46 | array[@"1..3"] will give you [2, 3, 4] 47 | array[@"1...3"] will give you [2, 3] (last value excluded) 48 | array[@"1,3"] implies NSRange(location: 1, length: 3), and gives you [2, 3, 4] 49 | 50 | 51 | @return An array with elements within the specified range 52 | */ 53 | - (id)objectForKeyedSubscript:(id )key; 54 | 55 | 56 | /** 57 | A simpler alias for `enumerateObjectsUsingBlock` 58 | 59 | @param block A block with the object in its arguments. 60 | */ 61 | - (void)each:(void (^)(id object))block; 62 | 63 | /** 64 | A simpler alias for `enumerateObjectsUsingBlock` which also passes in an index 65 | 66 | @param block A block with the object in its arguments. 67 | */ 68 | - (void)eachWithIndex:(void (^)(id object, NSUInteger index))block; 69 | 70 | /** 71 | A simpler alias for `enumerateObjectsWithOptions:usingBlock:` 72 | 73 | @param block A block with the object in its arguments. 74 | @param options Enumerating options. 75 | */ 76 | 77 | - (void)each:(void (^)(id object))block options:(NSEnumerationOptions)options; 78 | 79 | /** 80 | A simpler alias for `enumerateObjectsWithOptions:usingBlock:` which also passes in an index 81 | 82 | @param block A block with the object in its arguments. 83 | @param options Enumerating options. 84 | */ 85 | 86 | - (void)eachWithIndex:(void (^)(id object, NSUInteger index))block options:(NSEnumerationOptions)options; 87 | 88 | 89 | /** 90 | An alias for `containsObject` 91 | 92 | @param object An object that the array may or may not contain. 93 | */ 94 | - (BOOL)includes:(id)object; 95 | 96 | /** 97 | Take the first `numberOfElements` out of the array, or the maximum amount of 98 | elements if it is less. 99 | 100 | @param numberOfElements Number of elements to take from array 101 | @return An array of elements 102 | */ 103 | - (NSArray *)take:(NSUInteger)numberOfElements; 104 | 105 | /** 106 | Passes elements to the `block` until the block returns NO, 107 | then stops iterating and returns an array of all prior elements. 108 | 109 | @param block A block that returns YES/NO 110 | @return An array of elements 111 | */ 112 | - (NSArray *)takeWhile:(BOOL (^)(id object))block; 113 | 114 | /** 115 | Iterate through the current array running the block on each object and 116 | returning an array of the changed objects. 117 | 118 | @param block A block that passes in each object and returns a modified object 119 | @return An array of modified elements 120 | */ 121 | - (NSArray *)map:(id (^)(id object))block; 122 | 123 | /** 124 | Iterate through current array asking whether to keep each element. 125 | 126 | @param block A block that returns YES/NO for whether the object should stay 127 | @return An array of elements selected 128 | */ 129 | - (NSArray *)select:(BOOL (^)(id object))block; 130 | 131 | /** 132 | Iterate through current array returning the first element meeting a criteria. 133 | 134 | @param block A block that returns YES/NO 135 | @return The first matching element 136 | */ 137 | - (id)detect:(BOOL (^)(id object))block; 138 | 139 | 140 | /** 141 | Alias for `detect`. Iterate through current array returning the first element 142 | meeting a criteria. 143 | 144 | @param block A block that returns YES/NO 145 | @return The first matching element 146 | */ 147 | - (id)find:(BOOL (^)(id object))block; 148 | 149 | /** 150 | Iterate through current array asking whether to remove each element. 151 | 152 | @param block A block that returns YES/NO for whether the object should be removed 153 | @return An array of elements not rejected 154 | */ 155 | - (NSArray *)reject:(BOOL (^)(id object))block; 156 | 157 | /** 158 | Recurse through self checking for NSArrays and extract all elements into one single array 159 | 160 | @return An array of all held arrays merged 161 | */ 162 | - (NSArray *)flatten; 163 | 164 | /** 165 | Remove all the nulls from array 166 | 167 | @return A copy of the given array without NSNulls 168 | */ 169 | - (NSArray *)compact; 170 | 171 | /** 172 | Alias for `componentsJoinedByString` with a default of no seperator 173 | 174 | @return A string of all objects joined with an empty string 175 | */ 176 | - (NSString *)join; 177 | 178 | /** 179 | Alias for `componentsJoinedByString` 180 | 181 | @return A string of all objects joined with the `seperator` string 182 | */ 183 | - (NSString *)join:(NSString *)separator; 184 | 185 | /** 186 | Run the default comparator on each object in the array 187 | 188 | @return A sorted copy of the array 189 | */ 190 | - (NSArray *)sort; 191 | 192 | /** 193 | Sorts the array using the the default comparator on the given key 194 | 195 | @return A sorted copy of the array 196 | */ 197 | - (NSArray *)sortBy:(NSString *)key; 198 | 199 | /** 200 | Alias for reverseObjectEnumerator.allObjects 201 | 202 | Returns a reversed array 203 | */ 204 | - (NSArray *)reverse; 205 | 206 | /** 207 | Return all the objects that are in both self and `array`. 208 | Alias for Ruby's & operator 209 | 210 | @return An array of objects common to both arrays 211 | */ 212 | - (NSArray *)intersectionWithArray:(NSArray *)array; 213 | 214 | /** 215 | Return all the objects that in both self and `array` combined. 216 | Alias for Ruby's | operator 217 | 218 | @return An array of the two arrays combined 219 | */ 220 | 221 | - (NSArray *)unionWithArray:(NSArray *)array; 222 | 223 | /** 224 | Return all the objects in self that are not in `array`. 225 | Alias for Ruby's - operator 226 | 227 | @return An array of the self without objects in `array` 228 | */ 229 | 230 | - (NSArray *)relativeComplement:(NSArray *)array; 231 | 232 | /** 233 | Return all the objects that are unique to each array individually 234 | Alias for Ruby's ^ operator. Equivalent of a - b | b - a 235 | 236 | @return An array of elements which are in either of the arrays and not in their intersection. 237 | */ 238 | - (NSArray *)symmetricDifference:(NSArray *)array; 239 | 240 | /** 241 | Return a single value from an array by iterating through the elements and transforming a running total. 242 | 243 | @return A single value that is the end result of apply the block function to each element successively. 244 | **/ 245 | - (id)reduce:(id (^)(id accumulator, id object))block; 246 | 247 | /** 248 | Same as -reduce, with initial value provided by yourself 249 | **/ 250 | - (id)reduce:(id)initial withBlock:(id (^)(id accumulator, id object))block; 251 | 252 | /** 253 | Produces a duplicate-free version of the array 254 | 255 | @return a new array with all unique elements 256 | **/ 257 | - (NSArray *)unique; 258 | 259 | @end 260 | 261 | -------------------------------------------------------------------------------- /Classes/NSArray+ObjectiveSugar.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+ObjectiveSugar.m 3 | // WidgetPush 4 | // 5 | // Created by Marin Usalj on 5/7/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "NSArray+ObjectiveSugar.h" 10 | #import "NSMutableArray+ObjectiveSugar.h" 11 | #import "NSString+ObjectiveSugar.h" 12 | 13 | static NSString * const OSMinusString = @"-"; 14 | 15 | @implementation NSArray (ObjectiveSugar) 16 | 17 | - (id)sample { 18 | if (self.count == 0) return nil; 19 | 20 | NSUInteger index = arc4random_uniform((u_int32_t)self.count); 21 | return self[index]; 22 | } 23 | 24 | - (id)objectForKeyedSubscript:(id)key { 25 | if ([key isKindOfClass:[NSString class]]) 26 | return [self subarrayWithRange:[self rangeFromString:key]]; 27 | 28 | else if ([key isKindOfClass:[NSValue class]]) 29 | return [self subarrayWithRange:[key rangeValue]]; 30 | 31 | else 32 | [NSException raise:NSInvalidArgumentException format:@"expected NSString or NSValue argument, got %@ instead", [key class]]; 33 | 34 | return nil; 35 | } 36 | 37 | - (NSRange)rangeFromString:(NSString *)string { 38 | NSRange range = NSRangeFromString(string); 39 | 40 | if ([string containsString:@"..."]) { 41 | range.length = isBackwardsRange(string) ? (self.count - 2) - range.length : range.length - range.location; 42 | 43 | } else if ([string containsString:@".."]) { 44 | range.length = isBackwardsRange(string) ? (self.count - 1) - range.length : range.length - range.location + 1; 45 | } 46 | 47 | return range; 48 | } 49 | 50 | - (void)each:(void (^)(id object))block { 51 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 52 | block(obj); 53 | }]; 54 | } 55 | 56 | - (void)eachWithIndex:(void (^)(id object, NSUInteger index))block { 57 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 58 | block(obj, idx); 59 | }]; 60 | } 61 | 62 | - (void)each:(void (^)(id object))block options:(NSEnumerationOptions)options { 63 | [self enumerateObjectsWithOptions:options usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 64 | block(obj); 65 | }]; 66 | } 67 | 68 | - (void)eachWithIndex:(void (^)(id object, NSUInteger index))block options:(NSEnumerationOptions)options { 69 | [self enumerateObjectsWithOptions:options usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 70 | block(obj, idx); 71 | }]; 72 | } 73 | 74 | - (BOOL)includes:(id)object { 75 | return [self containsObject:object]; 76 | } 77 | 78 | - (NSArray *)take:(NSUInteger)numberOfElements { 79 | return [self subarrayWithRange:NSMakeRange(0, MIN(numberOfElements, [self count]))]; 80 | } 81 | 82 | - (NSArray *)takeWhile:(BOOL (^)(id object))block { 83 | NSMutableArray *array = [NSMutableArray array]; 84 | 85 | for (id arrayObject in self) { 86 | if (block(arrayObject)) 87 | [array addObject:arrayObject]; 88 | 89 | else break; 90 | } 91 | 92 | return array; 93 | } 94 | 95 | - (NSArray *)map:(id (^)(id object))block { 96 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count]; 97 | 98 | for (id object in self) { 99 | [array addObject:block(object) ?: [NSNull null]]; 100 | } 101 | 102 | return array; 103 | } 104 | 105 | - (NSArray *)select:(BOOL (^)(id object))block { 106 | return [self filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 107 | return block(evaluatedObject); 108 | }]]; 109 | } 110 | 111 | - (NSArray *)reject:(BOOL (^)(id object))block { 112 | return [self filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 113 | return !block(evaluatedObject); 114 | }]]; 115 | } 116 | 117 | - (id)detect:(BOOL (^)(id object))block { 118 | for (id object in self) { 119 | if (block(object)) 120 | return object; 121 | } 122 | return nil; 123 | } 124 | 125 | - (id)find:(BOOL (^)(id object))block { 126 | return [self detect:block]; 127 | } 128 | 129 | - (NSArray *)flatten { 130 | NSMutableArray *array = [NSMutableArray array]; 131 | 132 | for (id object in self) { 133 | if ([object isKindOfClass:NSArray.class]) { 134 | [array concat:[object flatten]]; 135 | } else { 136 | [array addObject:object]; 137 | } 138 | } 139 | 140 | return array; 141 | } 142 | 143 | - (NSArray *)compact { 144 | return [self select:^BOOL(id object) { 145 | return object != [NSNull null]; 146 | }]; 147 | } 148 | 149 | - (NSString *)join { 150 | return [self componentsJoinedByString:@""]; 151 | } 152 | 153 | - (NSString *)join:(NSString *)separator { 154 | return [self componentsJoinedByString:separator]; 155 | } 156 | 157 | - (NSArray *)sort { 158 | return [self sortedArrayUsingSelector:@selector(compare:)]; 159 | } 160 | 161 | - (NSArray *)sortBy:(NSString*)key; { 162 | NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:YES]; 163 | return [self sortedArrayUsingDescriptors:@[descriptor]]; 164 | } 165 | 166 | - (NSArray *)reverse { 167 | return self.reverseObjectEnumerator.allObjects; 168 | } 169 | 170 | - (id)reduce:(id (^)(id accumulator, id object))block { 171 | return [self reduce:nil withBlock:block]; 172 | } 173 | 174 | - (id)reduce:(id)initial withBlock:(id (^)(id accumulator, id object))block { 175 | id accumulator = initial; 176 | 177 | for(id object in self) 178 | accumulator = accumulator ? block(accumulator, object) : object; 179 | 180 | return accumulator; 181 | } 182 | 183 | - (NSArray *)unique 184 | { 185 | return [[NSOrderedSet orderedSetWithArray:self] array]; 186 | } 187 | 188 | #pragma mark - Set operations 189 | 190 | - (NSArray *)intersectionWithArray:(NSArray *)array { 191 | NSPredicate *intersectPredicate = [NSPredicate predicateWithFormat:@"SELF IN %@", array]; 192 | return [self filteredArrayUsingPredicate:intersectPredicate]; 193 | } 194 | 195 | - (NSArray *)unionWithArray:(NSArray *)array { 196 | NSArray *complement = [self relativeComplement:array]; 197 | return [complement arrayByAddingObjectsFromArray:array]; 198 | } 199 | 200 | - (NSArray *)relativeComplement:(NSArray *)array { 201 | NSPredicate *relativeComplementPredicate = [NSPredicate predicateWithFormat:@"NOT SELF IN %@", array]; 202 | return [self filteredArrayUsingPredicate:relativeComplementPredicate]; 203 | } 204 | 205 | - (NSArray *)symmetricDifference:(NSArray *)array { 206 | NSArray *aSubtractB = [self relativeComplement:array]; 207 | NSArray *bSubtractA = [array relativeComplement:self]; 208 | return [aSubtractB unionWithArray:bSubtractA]; 209 | } 210 | 211 | #pragma mark - Private 212 | 213 | static inline BOOL isBackwardsRange(NSString *rangeString) { 214 | return [rangeString containsString:OSMinusString]; 215 | } 216 | 217 | #pragma mark - Aliases 218 | 219 | - (id)anyObject { 220 | return [self sample]; 221 | } 222 | 223 | - (id)first DEPRECATED_ATTRIBUTE { 224 | return [self firstObject]; 225 | } 226 | 227 | - (id)last DEPRECATED_ATTRIBUTE { 228 | return [self lastObject]; 229 | } 230 | 231 | @end 232 | 233 | -------------------------------------------------------------------------------- /Classes/NSDictionary+ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+ObjectiveSugar.h 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary (ObjectiveSugar) 12 | 13 | - (void)each:(void (^)(id key, id value))block; 14 | - (void)eachKey:(void (^)(id key))block; 15 | - (void)eachValue:(void (^)(id value))block; 16 | - (NSArray *)map:(id (^)(id key, id value))block; 17 | - (BOOL)hasKey:(id)key; 18 | - (NSDictionary *)pick:(NSArray *)keys; 19 | - (NSDictionary *)omit:(NSArray *)keys; 20 | - (NSDictionary *)merge:(NSDictionary *)dictionary; 21 | - (NSDictionary *)merge:(NSDictionary *)dictionary block:(id(^)(id key, id oldVal, id newVal))block; 22 | - (NSDictionary *)invert; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Classes/NSDictionary+ObjectiveSugar.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+ObjectiveSugar.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+ObjectiveSugar.h" 10 | 11 | #import "NSArray+ObjectiveSugar.h" 12 | 13 | @implementation NSDictionary (Rubyfy) 14 | 15 | - (void)each:(void (^)(id k, id v))block { 16 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 17 | block(key, obj); 18 | }]; 19 | } 20 | 21 | - (void)eachKey:(void (^)(id k))block { 22 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 23 | block(key); 24 | }]; 25 | } 26 | 27 | - (void)eachValue:(void (^)(id v))block { 28 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 29 | block(obj); 30 | }]; 31 | } 32 | 33 | - (NSArray *)map:(id (^)(id key, id value))block { 34 | NSMutableArray *array = [NSMutableArray array]; 35 | 36 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 37 | id object = block(key, obj); 38 | if (object) { 39 | [array addObject:object]; 40 | } 41 | }]; 42 | 43 | return array; 44 | } 45 | 46 | - (BOOL)hasKey:(id)key { 47 | return !!self[key]; 48 | } 49 | 50 | - (NSDictionary *)pick:(NSArray *)keys { 51 | NSMutableDictionary *picked = [[NSMutableDictionary alloc] initWithCapacity:keys.count]; 52 | 53 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 54 | if ([keys containsObject:key]) { 55 | picked[key] = obj; 56 | } 57 | }]; 58 | 59 | return picked; 60 | } 61 | 62 | - (NSDictionary *)omit:(NSArray *)keys { 63 | NSMutableDictionary *omitted = [[NSMutableDictionary alloc] initWithCapacity:([self allKeys].count - keys.count)]; 64 | 65 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 66 | if (![keys containsObject:key]) { 67 | omitted[key] = obj; 68 | } 69 | }]; 70 | 71 | return omitted; 72 | } 73 | 74 | - (NSDictionary *)merge:(NSDictionary *)dictionary { 75 | NSMutableDictionary *merged = [NSMutableDictionary dictionaryWithDictionary:self]; 76 | [merged addEntriesFromDictionary:dictionary]; 77 | return merged; 78 | } 79 | 80 | - (NSDictionary *)merge:(NSDictionary *)dictionary block:(id(^)(id key, id oldVal, id newVal))block { 81 | NSMutableDictionary *merged = [NSMutableDictionary dictionary]; 82 | [[[self allKeys] relativeComplement:[dictionary allKeys]] each:^(id key) { 83 | merged[key] = self[key]; 84 | }]; 85 | 86 | [[[dictionary allKeys] relativeComplement:[self allKeys]] each:^(id key) { 87 | merged[key] = dictionary[key]; 88 | }]; 89 | 90 | [[[self allKeys] intersectionWithArray:[dictionary allKeys]] each:^(id key) { 91 | merged[key] = block(key, self[key], dictionary[key]); 92 | }]; 93 | return merged; 94 | } 95 | 96 | - (NSDictionary *)invert { 97 | NSMutableDictionary *inverted = [NSMutableDictionary dictionary]; 98 | for (id key in [self allKeys]) { 99 | inverted[self[key]] = key; 100 | } 101 | return inverted; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Classes/NSMutableArray+ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+ObjectiveSugar.h 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableArray (ObjectiveSugar) 12 | 13 | /// Alias for -addObject. Appends the given object at the end 14 | - (void)push:(id)object; 15 | 16 | /** 17 | Removes the last item of the array, and returns that item 18 | Note: This method changes the length of the array! 19 | 20 | @return First array item or nil. 21 | */ 22 | - (id)pop; 23 | 24 | 25 | /** 26 | Removes the last n items of the array, and returns that item 27 | Note: This method changes the length of the array! 28 | 29 | @return First array item or nil. 30 | */ 31 | - (NSArray *)pop:(NSUInteger)numberOfElements; 32 | - (void)concat:(NSArray *)array; 33 | 34 | 35 | /** 36 | Removes the first item of the array, and returns that item 37 | Note: This method changes the length of the array! 38 | 39 | @return First array item or nil. 40 | */ 41 | - (id)shift; 42 | 43 | 44 | /** 45 | Removes N first items of the array, and returns that items 46 | Note: This method changes the length of the array! 47 | 48 | @return Array of first N items or empty array. 49 | */ 50 | - (NSArray *)shift:(NSUInteger)numberOfElements; 51 | 52 | 53 | /** 54 | Deletes every element of the array for which the given block evaluates to NO. 55 | 56 | @param block block that returns YES/NO 57 | @return An array of elements 58 | */ 59 | - (NSArray *)keepIf:(BOOL (^)(id object))block; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Classes/NSMutableArray+ObjectiveSugar.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+ObjectiveSugar.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+ObjectiveSugar.h" 10 | #import "NSArray+ObjectiveSugar.h" 11 | 12 | @implementation NSMutableArray (ObjectiveSugar) 13 | 14 | - (void)push:(id)object { 15 | [self addObject:object]; 16 | } 17 | 18 | - (id)pop { 19 | id object = [self lastObject]; 20 | [self removeLastObject]; 21 | 22 | return object; 23 | } 24 | 25 | - (NSArray *)pop:(NSUInteger)numberOfElements { 26 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:numberOfElements]; 27 | 28 | for (NSUInteger i = 0; i < numberOfElements; i++) 29 | [array insertObject:[self pop] atIndex:0]; 30 | 31 | return array; 32 | } 33 | 34 | - (void)concat:(NSArray *)array { 35 | [self addObjectsFromArray:array]; 36 | } 37 | 38 | - (id)shift { 39 | NSArray *result = [self shift:1]; 40 | return [result firstObject]; 41 | } 42 | 43 | - (NSArray *)shift:(NSUInteger)numberOfElements { 44 | NSUInteger shiftLength = MIN(numberOfElements, [self count]); 45 | 46 | NSRange range = NSMakeRange(0, shiftLength); 47 | NSArray *result = [self subarrayWithRange:range]; 48 | [self removeObjectsInRange:range]; 49 | 50 | return result; 51 | } 52 | 53 | - (NSArray *)keepIf:(BOOL (^)(id object))block { 54 | [self filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 55 | return block(evaluatedObject); 56 | }]]; 57 | return self; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Classes/NSNumber+ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+Rubyfy.h 3 | // Domainchy 4 | // 5 | // Created by Marin Usalj on 11/15/12. 6 | // Copyright (c) 2012 supermar.in | @supermarin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSNumber (ObjectiveSugar) 12 | 13 | - (void)times:(void(^)(void))block; 14 | - (void)timesWithIndex:(void(^)(NSUInteger index))block; 15 | 16 | - (void)upto:(int)number do:(void(^)(NSInteger number))block; 17 | - (void)downto:(int)number do:(void(^)(NSInteger number))block; 18 | 19 | // Numeric inflections 20 | - (NSNumber *)seconds; 21 | - (NSNumber *)minutes; 22 | - (NSNumber *)hours; 23 | - (NSNumber *)days; 24 | - (NSNumber *)weeks; 25 | - (NSNumber *)fortnights; 26 | - (NSNumber *)months; 27 | - (NSNumber *)years; 28 | 29 | // There are singular aliases for the above methods 30 | - (NSNumber *)second; 31 | - (NSNumber *)minute; 32 | - (NSNumber *)hour; 33 | - (NSNumber *)day; 34 | - (NSNumber *)week; 35 | - (NSNumber *)fortnight; 36 | - (NSNumber *)month; 37 | - (NSNumber *)year; 38 | 39 | - (NSDate *)ago; 40 | - (NSDate *)ago:(NSDate *)time; 41 | - (NSDate *)since:(NSDate *)time; 42 | - (NSDate *)until:(NSDate *)time; 43 | - (NSDate *)fromNow; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Classes/NSNumber+ObjectiveSugar.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+Rubyfy.m 3 | // Domainchy 4 | // 5 | // Created by Marin Usalj on 11/15/12. 6 | // Copyright (c) 2012 supermar.in | @supermarin. All rights reserved. 7 | // 8 | 9 | #import "NSNumber+ObjectiveSugar.h" 10 | 11 | @implementation NSNumber (ObjectiveSugar) 12 | 13 | - (void)times:(void (^)(void))block { 14 | for (int i = 0; i < self.integerValue; i++) 15 | block(); 16 | } 17 | 18 | - (void)timesWithIndex:(void (^)(NSUInteger))block { 19 | for (NSUInteger i = 0; i < self.unsignedIntegerValue; i++) 20 | block(i); 21 | } 22 | 23 | - (void)upto:(int)number do:(void (^)(NSInteger))block { 24 | for (NSInteger i = self.integerValue; i <= number; i++) 25 | block(i); 26 | } 27 | 28 | - (void)downto:(int)number do:(void (^)(NSInteger))block { 29 | for (NSInteger i = self.integerValue; i >= number; i--) 30 | block(i); 31 | } 32 | 33 | - (NSNumber *)second { 34 | return self.seconds; 35 | } 36 | 37 | - (NSNumber *)seconds { 38 | return self; 39 | } 40 | 41 | - (NSNumber *)minute { 42 | return self.minutes; 43 | } 44 | 45 | - (NSNumber *)minutes { 46 | return @(self.floatValue * 60); 47 | } 48 | 49 | - (NSNumber *)hour { 50 | return self.hours; 51 | } 52 | 53 | - (NSNumber *)hours { 54 | return @(self.floatValue * [@60 minutes].integerValue); 55 | } 56 | 57 | - (NSNumber *)day { 58 | return self.days; 59 | } 60 | 61 | - (NSNumber *)days { 62 | return @(self.floatValue * [@24 hours].integerValue); 63 | } 64 | 65 | - (NSNumber *)week { 66 | return self.weeks; 67 | } 68 | 69 | - (NSNumber *)weeks { 70 | return @(self.floatValue * [@7 days].integerValue); 71 | } 72 | 73 | - (NSNumber *)fortnight { 74 | return self.fortnights; 75 | } 76 | 77 | - (NSNumber *)fortnights { 78 | return @(self.floatValue * [@2 weeks].integerValue); 79 | } 80 | 81 | - (NSNumber *)month { 82 | return self.months; 83 | } 84 | 85 | - (NSNumber *)months { 86 | return @(self.floatValue * [@30 days].integerValue); 87 | } 88 | 89 | - (NSNumber *)year { 90 | return self.years; 91 | } 92 | 93 | - (NSNumber *)years { 94 | return @(self.floatValue * [@(365.25) days].integerValue); 95 | } 96 | 97 | - (NSDate *)ago { 98 | return [self ago:[NSDate date]]; 99 | } 100 | 101 | - (NSDate *)ago:(NSDate *)time { 102 | return [NSDate dateWithTimeIntervalSince1970:([time timeIntervalSince1970] - self.floatValue)]; 103 | } 104 | 105 | - (NSDate *)since:(NSDate *)time { 106 | return [NSDate dateWithTimeIntervalSince1970:([time timeIntervalSince1970] + self.floatValue)]; 107 | } 108 | 109 | - (NSDate *)until:(NSDate *)time { 110 | return [self ago:time]; 111 | } 112 | 113 | - (NSDate *)fromNow { 114 | return [self since:[NSDate date]]; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /Classes/NSSet+ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+Accessors.h 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSSet (Accessors) 12 | 13 | /// Returns the first object of a given set. 14 | /// Note that sets are unordered, so this method won't always return the same thing 15 | @property(readonly) id firstObject; 16 | 17 | /// Returns the last object of a given set. 18 | /// Note that sets are unordered, so this method won't always return the same thing 19 | @property(readonly) id lastObject; 20 | 21 | 22 | /// Alias for -anyObject. Returns a random object from a given set 23 | @property(readonly) id sample; 24 | 25 | /// Alias for -anyObject. Returns a random object from a given set 26 | - (void)each:(void (^)(id object))block; 27 | - (void)eachWithIndex:(void (^)(id object, NSUInteger index))block; 28 | 29 | /// Filters the given set using provided block, and returns an array copy 30 | - (NSArray *)select:(BOOL (^)(id object))block; 31 | 32 | /// Keeps the objects passing the given block, and returns an array copy 33 | - (NSArray *)reject:(BOOL (^)(id object))block; 34 | 35 | /// Returns a sorted array copy of the given set 36 | - (NSArray *)sort; 37 | 38 | /** 39 | * Maps the given NSSet to NSArray. 40 | * NOTE: If the block returns `-nil`, values are ignored. 41 | * This is intentional to keep the behavior of `-valueForKeyPath`. 42 | * This is different from NSArray `-map`, which puts NSNulls in the given case. 43 | * If you want to preserve the count and use NSNulls, use `set.allObjects -map:` 44 | * 45 | * @param block - a transform block 46 | * 47 | * @return An NSArray copy of the transformed set. 48 | */ 49 | - (NSArray *)map:(id (^)(id object))block; 50 | 51 | /** 52 | Return a single value from an array by iterating through the elements and transforming a running total. 53 | 54 | @return A single value that is the end result of apply the block function to each element successively. 55 | **/ 56 | - (id)reduce:(id (^)(id accumulator, id object))block; 57 | 58 | /** 59 | Same as -reduce, with initial value provided by yourself 60 | **/ 61 | - (id)reduce:(id)initial withBlock:(id (^)(id accumulator, id object))block; 62 | 63 | 64 | #pragma mark - Deprecations 65 | 66 | @property(readonly) id first DEPRECATED_MSG_ATTRIBUTE("Please use -firstObject"); 67 | @property(readonly) id last DEPRECATED_MSG_ATTRIBUTE("Please use -lastObject"); 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Classes/NSSet+ObjectiveSugar.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSet+ObjectiveSugar.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "NSSet+ObjectiveSugar.h" 10 | #import "NSArray+ObjectiveSugar.h" 11 | 12 | @implementation NSSet (ObjectiveSugar) 13 | 14 | - (id)firstObject { 15 | NSArray *allObjects = self.allObjects; 16 | 17 | if (allObjects.count > 0) 18 | return allObjects[0]; 19 | return nil; 20 | } 21 | 22 | - (id)lastObject { 23 | return self.allObjects.lastObject; 24 | } 25 | 26 | - (id)sample { 27 | return [self anyObject]; 28 | } 29 | 30 | - (void)each:(void (^)(id))block { 31 | [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { 32 | block(obj); 33 | }]; 34 | } 35 | 36 | - (void)eachWithIndex:(void (^)(id, int))block { 37 | __block int counter = 0; 38 | [self enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { 39 | block(obj, counter); 40 | counter ++; 41 | }]; 42 | } 43 | 44 | - (NSArray *)map:(id (^)(id object))block { 45 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count]; 46 | 47 | 48 | for (id object in self) { 49 | id mappedObject = block(object); 50 | if(mappedObject) { 51 | [array addObject:mappedObject]; 52 | } 53 | } 54 | 55 | return array; 56 | } 57 | 58 | - (NSArray *)select:(BOOL (^)(id object))block { 59 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count]; 60 | 61 | for (id object in self) { 62 | if (block(object)) { 63 | [array addObject:object]; 64 | } 65 | } 66 | 67 | return array; 68 | } 69 | 70 | - (NSArray *)reject:(BOOL (^)(id object))block { 71 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count]; 72 | 73 | for (id object in self) { 74 | if (block(object) == NO) { 75 | [array addObject:object]; 76 | } 77 | } 78 | 79 | return array; 80 | } 81 | 82 | - (NSArray *)sort { 83 | NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES]; 84 | return [self sortedArrayUsingDescriptors:@[sortDescriptor]]; 85 | } 86 | 87 | - (id)reduce:(id(^)(id accumulator, id object))block { 88 | return [self reduce:nil withBlock:block]; 89 | } 90 | 91 | - (id)reduce:(id)initial withBlock:(id(^)(id accumulator, id object))block { 92 | id accumulator = initial; 93 | 94 | for(id object in self) 95 | accumulator = accumulator ? block(accumulator, object) : object; 96 | 97 | return accumulator; 98 | } 99 | 100 | 101 | #pragma mark - Deprecations 102 | 103 | - (id)first DEPRECATED_ATTRIBUTE { 104 | return [self firstObject]; 105 | } 106 | 107 | - (id)last DEPRECATED_ATTRIBUTE { 108 | return [self lastObject]; 109 | } 110 | 111 | @end 112 | 113 | -------------------------------------------------------------------------------- /Classes/NSString+ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+ObjectiveSugar.h 3 | // SampleProject 4 | // 5 | // Created by Neil on 05/12/2012. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NSString *NSStringWithFormat(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); 12 | 13 | @interface NSString(ObjectiveSugar) 14 | 15 | /** 16 | Returns an array containing substrings from the receiver that have been divided by a whitespace delimiter 17 | 18 | @return An array containing substrings that have been divided by a whitespace delimiter 19 | */ 20 | - (NSArray *)split; 21 | 22 | 23 | /** 24 | Returns an array containing substrings from the receiver that have been divided by a given delimiter 25 | 26 | @param delimiter The delimiter string 27 | @return An array containing substrings that have been divided by delimiter 28 | */ 29 | - (NSArray *)split:(NSString *)delimiter; 30 | 31 | 32 | /** 33 | Returns a new string made by converting a snake_case_string to CamelCaseString 34 | 35 | @return A string made by converting a snake_case_string to CamelCaseString 36 | */ 37 | - (NSString *)camelCase; 38 | 39 | 40 | /** 41 | Returns a new string made by converting a snake_case_string to lowerCamelCaseString 42 | 43 | @return A string made by converting a snake_case_string to CamelCaseString 44 | */ 45 | - (NSString *)lowerCamelCase; 46 | 47 | 48 | /** 49 | Returns a Boolean value that indicates whether a given string is a substring of the receiver 50 | 51 | @return YES if 'string' is a substring of the receiver, otherwise NO 52 | */ 53 | - (BOOL)containsString:(NSString *)string; 54 | 55 | 56 | /** 57 | Returns a new string made by removing whitespaces and newlines from both ends of the receiver 58 | 59 | @return A string without trailing or leading whitespaces and newlines 60 | */ 61 | - (NSString *)strip; 62 | 63 | /** 64 | Returns a new string that matches the passed in pattern 65 | 66 | @return A String matching the regex or nil if no match is found 67 | */ 68 | - (NSString *)match:(NSString *)pattern; 69 | 70 | @end 71 | 72 | -------------------------------------------------------------------------------- /Classes/NSString+ObjectiveSugar.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+ObjectiveSugar.m 3 | // SampleProject 4 | // 5 | // Created by Neil on 05/12/2012. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "NSString+ObjectiveSugar.h" 10 | #import "NSArray+ObjectiveSugar.h" 11 | 12 | static NSString *const UNDERSCORE = @"_"; 13 | static NSString *const SPACE = @" "; 14 | static NSString *const EMPTY_STRING = @""; 15 | 16 | NSString *NSStringWithFormat(NSString *formatString, ...) { 17 | va_list args; 18 | va_start(args, formatString); 19 | 20 | NSString *string = [[NSString alloc] initWithFormat:formatString arguments:args]; 21 | 22 | va_end(args); 23 | 24 | #if defined(__has_feature) && __has_feature(objc_arc) 25 | return string; 26 | #else 27 | return [string autorelease]; 28 | #endif 29 | } 30 | 31 | 32 | @implementation NSString(Additions) 33 | 34 | - (NSArray *)split { 35 | NSArray *result = [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 36 | return [result select:^BOOL(NSString *string) { 37 | return string.length > 0; 38 | }]; 39 | } 40 | 41 | - (NSArray *)split:(NSString *)delimiter { 42 | return [self componentsSeparatedByString:delimiter]; 43 | } 44 | 45 | - (NSString *)camelCase { 46 | NSString *spaced = [self stringByReplacingOccurrencesOfString:UNDERSCORE withString:SPACE]; 47 | NSString *capitalized = [spaced capitalizedString]; 48 | 49 | return [capitalized stringByReplacingOccurrencesOfString:SPACE withString:EMPTY_STRING]; 50 | } 51 | 52 | - (NSString *)lowerCamelCase { 53 | NSString *upperCamelCase = [self camelCase]; 54 | NSString *firstLetter = [upperCamelCase substringToIndex:1]; 55 | return [upperCamelCase stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstLetter.lowercaseString]; 56 | } 57 | 58 | - (BOOL)containsString:(NSString *) string { 59 | NSRange range = [self rangeOfString:string options:NSCaseInsensitiveSearch]; 60 | return range.location != NSNotFound; 61 | } 62 | 63 | - (NSString *)strip { 64 | return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 65 | } 66 | 67 | - (NSString *)match:(NSString *)pattern { 68 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil]; 69 | NSTextCheckingResult *match = [regex firstMatchInString:self options:0 range:NSMakeRange(0, self.length)]; 70 | 71 | return (match != nil) ? [self substringWithRange:[match range]] : nil; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /Classes/ObjectiveSugar.h: -------------------------------------------------------------------------------- 1 | // C SUGAR 2 | #define unless(condition...) if(!(condition)) 3 | #define until(condition...) while(!(condition)) 4 | 5 | // OBJC SUGAR 6 | #import "NSNumber+ObjectiveSugar.h" 7 | #import "NSArray+ObjectiveSugar.h" 8 | #import "NSMutableArray+ObjectiveSugar.h" 9 | #import "NSDictionary+ObjectiveSugar.h" 10 | #import "NSSet+ObjectiveSugar.h" 11 | #import "NSString+ObjectiveSugar.h" 12 | -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | !default.xcworkspace 12 | xcuserdata 13 | *.xccheckout 14 | profile 15 | *.moved-aside 16 | .idea 17 | Pods 18 | 19 | *.log 20 | 21 | -------------------------------------------------------------------------------- /Example/Makefile: -------------------------------------------------------------------------------- 1 | project_name = ObjectiveSugar 2 | workspace = $(project_name).xcworkspace 3 | xcodebuild = xcodebuild -workspace $(workspace) -scheme $(project_name) -sdk iphonesimulator 4 | 5 | clean: 6 | $(xcodebuild) clean 7 | 8 | test: 9 | $(xcodebuild) test | tee xcodebuild.log 10 | 11 | install: 12 | gem install cocoapods --no-ri --no-rdoc 13 | gem install xcpretty --no-ri --no-rdoc 14 | pod install 15 | 16 | ci: 17 | $(xcodebuild) test | xcpretty -c 18 | 19 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | archiveVersion 6 | 1 7 | classes 8 | 9 | objectVersion 10 | 46 11 | objects 12 | 13 | 0D82F54D4F82F5960EA2CBA9 14 | 15 | includeInIndex 16 | 1 17 | isa 18 | PBXFileReference 19 | lastKnownFileType 20 | text.xcconfig 21 | name 22 | Pods-ObjectiveSugarTests.release.xcconfig 23 | path 24 | Pods/Target Support Files/Pods-ObjectiveSugarTests/Pods-ObjectiveSugarTests.release.xcconfig 25 | sourceTree 26 | <group> 27 | 28 | 11566D315A504F04BFD15074 29 | 30 | fileRef 31 | 32DEFD3FCB544122AD940C8F 32 | isa 33 | PBXBuildFile 34 | 35 | 13656AD991424BF58F43F5B7 36 | 37 | buildActionMask 38 | 2147483647 39 | files 40 | 41 | inputPaths 42 | 43 | isa 44 | PBXShellScriptBuildPhase 45 | name 46 | Copy Pods Resources 47 | outputPaths 48 | 49 | runOnlyForDeploymentPostprocessing 50 | 0 51 | shellPath 52 | /bin/sh 53 | shellScript 54 | "${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh" 55 | 56 | 57 | 174A1F9A81B44DC6B8B6DFD9 58 | 59 | buildActionMask 60 | 2147483647 61 | files 62 | 63 | inputPaths 64 | 65 | isa 66 | PBXShellScriptBuildPhase 67 | name 68 | Copy Pods Resources 69 | outputPaths 70 | 71 | runOnlyForDeploymentPostprocessing 72 | 0 73 | shellPath 74 | /bin/sh 75 | shellScript 76 | "${SRCROOT}/Pods/Target Support Files/Pods-ObjectiveSugarTests/Pods-ObjectiveSugarTests-resources.sh" 77 | 78 | 79 | 32DEFD3FCB544122AD940C8F 80 | 81 | explicitFileType 82 | archive.ar 83 | includeInIndex 84 | 0 85 | isa 86 | PBXFileReference 87 | path 88 | libPods.a 89 | sourceTree 90 | BUILT_PRODUCTS_DIR 91 | 92 | 468DABF301EC4EC1A00CC4C2 93 | 94 | buildActionMask 95 | 2147483647 96 | files 97 | 98 | inputPaths 99 | 100 | isa 101 | PBXShellScriptBuildPhase 102 | name 103 | Check Pods Manifest.lock 104 | outputPaths 105 | 106 | runOnlyForDeploymentPostprocessing 107 | 0 108 | shellPath 109 | /bin/sh 110 | shellScript 111 | diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null 112 | if [[ $? != 0 ]] ; then 113 | cat << EOM 114 | error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. 115 | EOM 116 | exit 1 117 | fi 118 | 119 | 120 | 893C6A18A162B9FB06935ED4 121 | 122 | includeInIndex 123 | 1 124 | isa 125 | PBXFileReference 126 | lastKnownFileType 127 | text.xcconfig 128 | name 129 | Pods-ObjectiveSugarTests.debug.xcconfig 130 | path 131 | Pods/Target Support Files/Pods-ObjectiveSugarTests/Pods-ObjectiveSugarTests.debug.xcconfig 132 | sourceTree 133 | <group> 134 | 135 | 89690120173C0E4B00B403D2 136 | 137 | children 138 | 139 | 89690132173C0E4C00B403D2 140 | 89690150173C0E4C00B403D2 141 | 8969012B173C0E4C00B403D2 142 | 8969012A173C0E4C00B403D2 143 | 9ADDC0C494D895C067A28C9A 144 | 145 | isa 146 | PBXGroup 147 | sourceTree 148 | <group> 149 | 150 | 89690121173C0E4B00B403D2 151 | 152 | attributes 153 | 154 | LastUpgradeCheck 155 | 0460 156 | ORGANIZATIONNAME 157 | supermar.in 158 | 159 | buildConfigurationList 160 | 89690124173C0E4B00B403D2 161 | compatibilityVersion 162 | Xcode 3.2 163 | developmentRegion 164 | English 165 | hasScannedForEncodings 166 | 0 167 | isa 168 | PBXProject 169 | knownRegions 170 | 171 | en 172 | 173 | mainGroup 174 | 89690120173C0E4B00B403D2 175 | productRefGroup 176 | 8969012A173C0E4C00B403D2 177 | projectDirPath 178 | 179 | projectReferences 180 | 181 | projectRoot 182 | 183 | targets 184 | 185 | 89690128173C0E4C00B403D2 186 | 89690148173C0E4C00B403D2 187 | 188 | 189 | 89690124173C0E4B00B403D2 190 | 191 | buildConfigurations 192 | 193 | 89690159173C0E4C00B403D2 194 | 8969015A173C0E4C00B403D2 195 | 196 | defaultConfigurationIsVisible 197 | 0 198 | defaultConfigurationName 199 | Release 200 | isa 201 | XCConfigurationList 202 | 203 | 89690125173C0E4C00B403D2 204 | 205 | buildActionMask 206 | 2147483647 207 | files 208 | 209 | 89690139173C0E4C00B403D2 210 | 8969013D173C0E4C00B403D2 211 | 212 | isa 213 | PBXSourcesBuildPhase 214 | runOnlyForDeploymentPostprocessing 215 | 0 216 | 217 | 89690126173C0E4C00B403D2 218 | 219 | buildActionMask 220 | 2147483647 221 | files 222 | 223 | 8969012D173C0E4C00B403D2 224 | 8969012F173C0E4C00B403D2 225 | 89690131173C0E4C00B403D2 226 | 11566D315A504F04BFD15074 227 | 228 | isa 229 | PBXFrameworksBuildPhase 230 | runOnlyForDeploymentPostprocessing 231 | 0 232 | 233 | 89690127173C0E4C00B403D2 234 | 235 | buildActionMask 236 | 2147483647 237 | files 238 | 239 | 89690137173C0E4C00B403D2 240 | 8969013F173C0E4C00B403D2 241 | 89690141173C0E4C00B403D2 242 | 89690143173C0E4C00B403D2 243 | 244 | isa 245 | PBXResourcesBuildPhase 246 | runOnlyForDeploymentPostprocessing 247 | 0 248 | 249 | 89690128173C0E4C00B403D2 250 | 251 | buildConfigurationList 252 | 8969015B173C0E4C00B403D2 253 | buildPhases 254 | 255 | 468DABF301EC4EC1A00CC4C2 256 | 89690125173C0E4C00B403D2 257 | 89690126173C0E4C00B403D2 258 | 89690127173C0E4C00B403D2 259 | 13656AD991424BF58F43F5B7 260 | 261 | buildRules 262 | 263 | dependencies 264 | 265 | isa 266 | PBXNativeTarget 267 | name 268 | ObjectiveSugar 269 | productName 270 | ObjectiveSugar 271 | productReference 272 | 89690129173C0E4C00B403D2 273 | productType 274 | com.apple.product-type.application 275 | 276 | 89690129173C0E4C00B403D2 277 | 278 | explicitFileType 279 | wrapper.application 280 | includeInIndex 281 | 0 282 | isa 283 | PBXFileReference 284 | path 285 | ObjectiveSugar.app 286 | sourceTree 287 | BUILT_PRODUCTS_DIR 288 | 289 | 8969012A173C0E4C00B403D2 290 | 291 | children 292 | 293 | 89690129173C0E4C00B403D2 294 | 89690149173C0E4C00B403D2 295 | 296 | isa 297 | PBXGroup 298 | name 299 | Products 300 | sourceTree 301 | <group> 302 | 303 | 8969012B173C0E4C00B403D2 304 | 305 | children 306 | 307 | 8969012C173C0E4C00B403D2 308 | 8969012E173C0E4C00B403D2 309 | 89690130173C0E4C00B403D2 310 | 32DEFD3FCB544122AD940C8F 311 | D3D0B2406C7A45249244F020 312 | 313 | isa 314 | PBXGroup 315 | name 316 | Frameworks 317 | sourceTree 318 | <group> 319 | 320 | 8969012C173C0E4C00B403D2 321 | 322 | isa 323 | PBXFileReference 324 | lastKnownFileType 325 | wrapper.framework 326 | name 327 | UIKit.framework 328 | path 329 | System/Library/Frameworks/UIKit.framework 330 | sourceTree 331 | SDKROOT 332 | 333 | 8969012D173C0E4C00B403D2 334 | 335 | fileRef 336 | 8969012C173C0E4C00B403D2 337 | isa 338 | PBXBuildFile 339 | 340 | 8969012E173C0E4C00B403D2 341 | 342 | isa 343 | PBXFileReference 344 | lastKnownFileType 345 | wrapper.framework 346 | name 347 | Foundation.framework 348 | path 349 | System/Library/Frameworks/Foundation.framework 350 | sourceTree 351 | SDKROOT 352 | 353 | 8969012F173C0E4C00B403D2 354 | 355 | fileRef 356 | 8969012E173C0E4C00B403D2 357 | isa 358 | PBXBuildFile 359 | 360 | 89690130173C0E4C00B403D2 361 | 362 | isa 363 | PBXFileReference 364 | lastKnownFileType 365 | wrapper.framework 366 | name 367 | CoreGraphics.framework 368 | path 369 | System/Library/Frameworks/CoreGraphics.framework 370 | sourceTree 371 | SDKROOT 372 | 373 | 89690131173C0E4C00B403D2 374 | 375 | fileRef 376 | 89690130173C0E4C00B403D2 377 | isa 378 | PBXBuildFile 379 | 380 | 89690132173C0E4C00B403D2 381 | 382 | children 383 | 384 | 8969013B173C0E4C00B403D2 385 | 8969013C173C0E4C00B403D2 386 | 89690133173C0E4C00B403D2 387 | 388 | isa 389 | PBXGroup 390 | path 391 | ObjectiveSugar 392 | sourceTree 393 | <group> 394 | 395 | 89690133173C0E4C00B403D2 396 | 397 | children 398 | 399 | 89690134173C0E4C00B403D2 400 | 89690135173C0E4C00B403D2 401 | 89690138173C0E4C00B403D2 402 | 8969013A173C0E4C00B403D2 403 | 8969013E173C0E4C00B403D2 404 | 89690140173C0E4C00B403D2 405 | 89690142173C0E4C00B403D2 406 | 407 | isa 408 | PBXGroup 409 | name 410 | Supporting Files 411 | sourceTree 412 | <group> 413 | 414 | 89690134173C0E4C00B403D2 415 | 416 | isa 417 | PBXFileReference 418 | lastKnownFileType 419 | text.plist.xml 420 | path 421 | ObjectiveSugar-Info.plist 422 | sourceTree 423 | <group> 424 | 425 | 89690135173C0E4C00B403D2 426 | 427 | children 428 | 429 | 89690136173C0E4C00B403D2 430 | 431 | isa 432 | PBXVariantGroup 433 | name 434 | InfoPlist.strings 435 | sourceTree 436 | <group> 437 | 438 | 89690136173C0E4C00B403D2 439 | 440 | isa 441 | PBXFileReference 442 | lastKnownFileType 443 | text.plist.strings 444 | name 445 | en 446 | path 447 | en.lproj/InfoPlist.strings 448 | sourceTree 449 | <group> 450 | 451 | 89690137173C0E4C00B403D2 452 | 453 | fileRef 454 | 89690135173C0E4C00B403D2 455 | isa 456 | PBXBuildFile 457 | 458 | 89690138173C0E4C00B403D2 459 | 460 | isa 461 | PBXFileReference 462 | lastKnownFileType 463 | sourcecode.c.objc 464 | path 465 | main.m 466 | sourceTree 467 | <group> 468 | 469 | 89690139173C0E4C00B403D2 470 | 471 | fileRef 472 | 89690138173C0E4C00B403D2 473 | isa 474 | PBXBuildFile 475 | 476 | 8969013A173C0E4C00B403D2 477 | 478 | isa 479 | PBXFileReference 480 | lastKnownFileType 481 | sourcecode.c.h 482 | path 483 | ObjectiveSugar-Prefix.pch 484 | sourceTree 485 | <group> 486 | 487 | 8969013B173C0E4C00B403D2 488 | 489 | isa 490 | PBXFileReference 491 | lastKnownFileType 492 | sourcecode.c.h 493 | path 494 | AppDelegate.h 495 | sourceTree 496 | <group> 497 | 498 | 8969013C173C0E4C00B403D2 499 | 500 | isa 501 | PBXFileReference 502 | lastKnownFileType 503 | sourcecode.c.objc 504 | path 505 | AppDelegate.m 506 | sourceTree 507 | <group> 508 | 509 | 8969013D173C0E4C00B403D2 510 | 511 | fileRef 512 | 8969013C173C0E4C00B403D2 513 | isa 514 | PBXBuildFile 515 | 516 | 8969013E173C0E4C00B403D2 517 | 518 | isa 519 | PBXFileReference 520 | lastKnownFileType 521 | image.png 522 | path 523 | Default.png 524 | sourceTree 525 | <group> 526 | 527 | 8969013F173C0E4C00B403D2 528 | 529 | fileRef 530 | 8969013E173C0E4C00B403D2 531 | isa 532 | PBXBuildFile 533 | 534 | 89690140173C0E4C00B403D2 535 | 536 | isa 537 | PBXFileReference 538 | lastKnownFileType 539 | image.png 540 | path 541 | Default@2x.png 542 | sourceTree 543 | <group> 544 | 545 | 89690141173C0E4C00B403D2 546 | 547 | fileRef 548 | 89690140173C0E4C00B403D2 549 | isa 550 | PBXBuildFile 551 | 552 | 89690142173C0E4C00B403D2 553 | 554 | isa 555 | PBXFileReference 556 | lastKnownFileType 557 | image.png 558 | path 559 | Default-568h@2x.png 560 | sourceTree 561 | <group> 562 | 563 | 89690143173C0E4C00B403D2 564 | 565 | fileRef 566 | 89690142173C0E4C00B403D2 567 | isa 568 | PBXBuildFile 569 | 570 | 89690144173C0E4C00B403D2 571 | 572 | buildActionMask 573 | 2147483647 574 | files 575 | 576 | 89690168173C16A100B403D2 577 | 89690169173C16A100B403D2 578 | 8969016A173C16A100B403D2 579 | 8969016B173C16A100B403D2 580 | 8969016C173C16A100B403D2 581 | 8969016D173C16A100B403D2 582 | 8969016E173C16A100B403D2 583 | 584 | isa 585 | PBXSourcesBuildPhase 586 | runOnlyForDeploymentPostprocessing 587 | 0 588 | 589 | 89690145173C0E4C00B403D2 590 | 591 | buildActionMask 592 | 2147483647 593 | files 594 | 595 | 8969014C173C0E4C00B403D2 596 | 8969014D173C0E4C00B403D2 597 | A06180063BF14EB68414F45F 598 | 599 | isa 600 | PBXFrameworksBuildPhase 601 | runOnlyForDeploymentPostprocessing 602 | 0 603 | 604 | 89690146173C0E4C00B403D2 605 | 606 | buildActionMask 607 | 2147483647 608 | files 609 | 610 | 89690155173C0E4C00B403D2 611 | 612 | isa 613 | PBXResourcesBuildPhase 614 | runOnlyForDeploymentPostprocessing 615 | 0 616 | 617 | 89690147173C0E4C00B403D2 618 | 619 | buildActionMask 620 | 2147483647 621 | files 622 | 623 | inputPaths 624 | 625 | isa 626 | PBXShellScriptBuildPhase 627 | outputPaths 628 | 629 | runOnlyForDeploymentPostprocessing 630 | 0 631 | shellPath 632 | /bin/sh 633 | shellScript 634 | # Run the unit tests in this test bundle. 635 | "${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests" 636 | 637 | 638 | 89690148173C0E4C00B403D2 639 | 640 | buildConfigurationList 641 | 8969015E173C0E4C00B403D2 642 | buildPhases 643 | 644 | BC624B20376C4C728DE6A392 645 | 89690144173C0E4C00B403D2 646 | 89690145173C0E4C00B403D2 647 | 89690146173C0E4C00B403D2 648 | 89690147173C0E4C00B403D2 649 | 174A1F9A81B44DC6B8B6DFD9 650 | 651 | buildRules 652 | 653 | dependencies 654 | 655 | 8969014F173C0E4C00B403D2 656 | 657 | isa 658 | PBXNativeTarget 659 | name 660 | ObjectiveSugarTests 661 | productName 662 | ObjectiveSugarTests 663 | productReference 664 | 89690149173C0E4C00B403D2 665 | productType 666 | com.apple.product-type.bundle.unit-test 667 | 668 | 89690149173C0E4C00B403D2 669 | 670 | explicitFileType 671 | wrapper.cfbundle 672 | includeInIndex 673 | 0 674 | isa 675 | PBXFileReference 676 | path 677 | ObjectiveSugarTests.octest 678 | sourceTree 679 | BUILT_PRODUCTS_DIR 680 | 681 | 8969014C173C0E4C00B403D2 682 | 683 | fileRef 684 | 8969012C173C0E4C00B403D2 685 | isa 686 | PBXBuildFile 687 | 688 | 8969014D173C0E4C00B403D2 689 | 690 | fileRef 691 | 8969012E173C0E4C00B403D2 692 | isa 693 | PBXBuildFile 694 | 695 | 8969014E173C0E4C00B403D2 696 | 697 | containerPortal 698 | 89690121173C0E4B00B403D2 699 | isa 700 | PBXContainerItemProxy 701 | proxyType 702 | 1 703 | remoteGlobalIDString 704 | 89690128173C0E4C00B403D2 705 | remoteInfo 706 | ObjectiveSugar 707 | 708 | 8969014F173C0E4C00B403D2 709 | 710 | isa 711 | PBXTargetDependency 712 | target 713 | 89690128173C0E4C00B403D2 714 | targetProxy 715 | 8969014E173C0E4C00B403D2 716 | 717 | 89690150173C0E4C00B403D2 718 | 719 | children 720 | 721 | 89690161173C16A100B403D2 722 | 89690162173C16A100B403D2 723 | 89690163173C16A100B403D2 724 | 89690164173C16A100B403D2 725 | 89690165173C16A100B403D2 726 | 89690166173C16A100B403D2 727 | 89690167173C16A100B403D2 728 | 89690151173C0E4C00B403D2 729 | 730 | isa 731 | PBXGroup 732 | path 733 | ObjectiveSugarTests 734 | sourceTree 735 | <group> 736 | 737 | 89690151173C0E4C00B403D2 738 | 739 | children 740 | 741 | 89690152173C0E4C00B403D2 742 | 89690153173C0E4C00B403D2 743 | 744 | isa 745 | PBXGroup 746 | name 747 | Supporting Files 748 | sourceTree 749 | <group> 750 | 751 | 89690152173C0E4C00B403D2 752 | 753 | isa 754 | PBXFileReference 755 | lastKnownFileType 756 | text.plist.xml 757 | path 758 | ObjectiveSugarTests-Info.plist 759 | sourceTree 760 | <group> 761 | 762 | 89690153173C0E4C00B403D2 763 | 764 | children 765 | 766 | 89690154173C0E4C00B403D2 767 | 768 | isa 769 | PBXVariantGroup 770 | name 771 | InfoPlist.strings 772 | sourceTree 773 | <group> 774 | 775 | 89690154173C0E4C00B403D2 776 | 777 | isa 778 | PBXFileReference 779 | lastKnownFileType 780 | text.plist.strings 781 | name 782 | en 783 | path 784 | en.lproj/InfoPlist.strings 785 | sourceTree 786 | <group> 787 | 788 | 89690155173C0E4C00B403D2 789 | 790 | fileRef 791 | 89690153173C0E4C00B403D2 792 | isa 793 | PBXBuildFile 794 | 795 | 89690159173C0E4C00B403D2 796 | 797 | buildSettings 798 | 799 | ALWAYS_SEARCH_USER_PATHS 800 | NO 801 | CLANG_CXX_LANGUAGE_STANDARD 802 | gnu++0x 803 | CLANG_CXX_LIBRARY 804 | libc++ 805 | CLANG_ENABLE_OBJC_ARC 806 | YES 807 | CLANG_WARN_CONSTANT_CONVERSION 808 | YES 809 | CLANG_WARN_EMPTY_BODY 810 | YES 811 | CLANG_WARN_ENUM_CONVERSION 812 | YES 813 | CLANG_WARN_INT_CONVERSION 814 | YES 815 | CLANG_WARN__DUPLICATE_METHOD_MATCH 816 | YES 817 | CODE_SIGN_IDENTITY[sdk=iphoneos*] 818 | iPhone Developer 819 | COPY_PHASE_STRIP 820 | NO 821 | GCC_C_LANGUAGE_STANDARD 822 | gnu99 823 | GCC_DYNAMIC_NO_PIC 824 | NO 825 | GCC_OPTIMIZATION_LEVEL 826 | 0 827 | GCC_PREPROCESSOR_DEFINITIONS 828 | 829 | DEBUG=1 830 | $(inherited) 831 | 832 | GCC_SYMBOLS_PRIVATE_EXTERN 833 | NO 834 | GCC_WARN_ABOUT_RETURN_TYPE 835 | YES 836 | GCC_WARN_UNINITIALIZED_AUTOS 837 | YES 838 | GCC_WARN_UNUSED_VARIABLE 839 | YES 840 | IPHONEOS_DEPLOYMENT_TARGET 841 | 6.1 842 | ONLY_ACTIVE_ARCH 843 | YES 844 | SDKROOT 845 | iphoneos 846 | 847 | isa 848 | XCBuildConfiguration 849 | name 850 | Debug 851 | 852 | 8969015A173C0E4C00B403D2 853 | 854 | buildSettings 855 | 856 | ALWAYS_SEARCH_USER_PATHS 857 | NO 858 | CLANG_CXX_LANGUAGE_STANDARD 859 | gnu++0x 860 | CLANG_CXX_LIBRARY 861 | libc++ 862 | CLANG_ENABLE_OBJC_ARC 863 | YES 864 | CLANG_WARN_CONSTANT_CONVERSION 865 | YES 866 | CLANG_WARN_EMPTY_BODY 867 | YES 868 | CLANG_WARN_ENUM_CONVERSION 869 | YES 870 | CLANG_WARN_INT_CONVERSION 871 | YES 872 | CLANG_WARN__DUPLICATE_METHOD_MATCH 873 | YES 874 | CODE_SIGN_IDENTITY[sdk=iphoneos*] 875 | iPhone Developer 876 | COPY_PHASE_STRIP 877 | YES 878 | GCC_C_LANGUAGE_STANDARD 879 | gnu99 880 | GCC_WARN_ABOUT_RETURN_TYPE 881 | YES 882 | GCC_WARN_UNINITIALIZED_AUTOS 883 | YES 884 | GCC_WARN_UNUSED_VARIABLE 885 | YES 886 | IPHONEOS_DEPLOYMENT_TARGET 887 | 6.1 888 | OTHER_CFLAGS 889 | -DNS_BLOCK_ASSERTIONS=1 890 | SDKROOT 891 | iphoneos 892 | VALIDATE_PRODUCT 893 | YES 894 | 895 | isa 896 | XCBuildConfiguration 897 | name 898 | Release 899 | 900 | 8969015B173C0E4C00B403D2 901 | 902 | buildConfigurations 903 | 904 | 8969015C173C0E4C00B403D2 905 | 8969015D173C0E4C00B403D2 906 | 907 | defaultConfigurationIsVisible 908 | 0 909 | defaultConfigurationName 910 | Release 911 | isa 912 | XCConfigurationList 913 | 914 | 8969015C173C0E4C00B403D2 915 | 916 | baseConfigurationReference 917 | 94C453035CB8232DD1A5513E 918 | buildSettings 919 | 920 | CODE_SIGN_IDENTITY 921 | iPhone Developer 922 | GCC_PRECOMPILE_PREFIX_HEADER 923 | YES 924 | GCC_PREFIX_HEADER 925 | ObjectiveSugar/ObjectiveSugar-Prefix.pch 926 | INFOPLIST_FILE 927 | ObjectiveSugar/ObjectiveSugar-Info.plist 928 | IPHONEOS_DEPLOYMENT_TARGET 929 | 4.3 930 | PRODUCT_NAME 931 | $(TARGET_NAME) 932 | WRAPPER_EXTENSION 933 | app 934 | 935 | isa 936 | XCBuildConfiguration 937 | name 938 | Debug 939 | 940 | 8969015D173C0E4C00B403D2 941 | 942 | baseConfigurationReference 943 | 9FAA69FB133006101A9A7843 944 | buildSettings 945 | 946 | CODE_SIGN_IDENTITY 947 | iPhone Developer 948 | GCC_PRECOMPILE_PREFIX_HEADER 949 | YES 950 | GCC_PREFIX_HEADER 951 | ObjectiveSugar/ObjectiveSugar-Prefix.pch 952 | INFOPLIST_FILE 953 | ObjectiveSugar/ObjectiveSugar-Info.plist 954 | IPHONEOS_DEPLOYMENT_TARGET 955 | 4.3 956 | PRODUCT_NAME 957 | $(TARGET_NAME) 958 | WRAPPER_EXTENSION 959 | app 960 | 961 | isa 962 | XCBuildConfiguration 963 | name 964 | Release 965 | 966 | 8969015E173C0E4C00B403D2 967 | 968 | buildConfigurations 969 | 970 | 8969015F173C0E4C00B403D2 971 | 89690160173C0E4C00B403D2 972 | 973 | defaultConfigurationIsVisible 974 | 0 975 | defaultConfigurationName 976 | Release 977 | isa 978 | XCConfigurationList 979 | 980 | 8969015F173C0E4C00B403D2 981 | 982 | baseConfigurationReference 983 | 893C6A18A162B9FB06935ED4 984 | buildSettings 985 | 986 | BUNDLE_LOADER 987 | $(BUILT_PRODUCTS_DIR)/ObjectiveSugar.app/ObjectiveSugar 988 | FRAMEWORK_SEARCH_PATHS 989 | 990 | $(inherited) 991 | "$(SDKROOT)/Developer/Library/Frameworks" 992 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks" 993 | 994 | GCC_PRECOMPILE_PREFIX_HEADER 995 | YES 996 | GCC_PREFIX_HEADER 997 | ObjectiveSugar/ObjectiveSugar-Prefix.pch 998 | INFOPLIST_FILE 999 | ObjectiveSugarTests/ObjectiveSugarTests-Info.plist 1000 | PRODUCT_NAME 1001 | $(TARGET_NAME) 1002 | TEST_HOST 1003 | $(BUNDLE_LOADER) 1004 | 1005 | isa 1006 | XCBuildConfiguration 1007 | name 1008 | Debug 1009 | 1010 | 89690160173C0E4C00B403D2 1011 | 1012 | baseConfigurationReference 1013 | 0D82F54D4F82F5960EA2CBA9 1014 | buildSettings 1015 | 1016 | BUNDLE_LOADER 1017 | $(BUILT_PRODUCTS_DIR)/ObjectiveSugar.app/ObjectiveSugar 1018 | FRAMEWORK_SEARCH_PATHS 1019 | 1020 | $(inherited) 1021 | "$(SDKROOT)/Developer/Library/Frameworks" 1022 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks" 1023 | 1024 | GCC_PRECOMPILE_PREFIX_HEADER 1025 | YES 1026 | GCC_PREFIX_HEADER 1027 | ObjectiveSugar/ObjectiveSugar-Prefix.pch 1028 | INFOPLIST_FILE 1029 | ObjectiveSugarTests/ObjectiveSugarTests-Info.plist 1030 | PRODUCT_NAME 1031 | $(TARGET_NAME) 1032 | TEST_HOST 1033 | $(BUNDLE_LOADER) 1034 | 1035 | isa 1036 | XCBuildConfiguration 1037 | name 1038 | Release 1039 | 1040 | 89690161173C16A100B403D2 1041 | 1042 | fileEncoding 1043 | 4 1044 | isa 1045 | PBXFileReference 1046 | lastKnownFileType 1047 | sourcecode.c.objc 1048 | path 1049 | CAdditionsTests.m 1050 | sourceTree 1051 | <group> 1052 | 1053 | 89690162173C16A100B403D2 1054 | 1055 | fileEncoding 1056 | 4 1057 | isa 1058 | PBXFileReference 1059 | lastKnownFileType 1060 | sourcecode.c.objc 1061 | path 1062 | NSArrayTests.m 1063 | sourceTree 1064 | <group> 1065 | 1066 | 89690163173C16A100B403D2 1067 | 1068 | fileEncoding 1069 | 4 1070 | isa 1071 | PBXFileReference 1072 | lastKnownFileType 1073 | sourcecode.c.objc 1074 | path 1075 | NSDictionaryTests.m 1076 | sourceTree 1077 | <group> 1078 | 1079 | 89690164173C16A100B403D2 1080 | 1081 | fileEncoding 1082 | 4 1083 | isa 1084 | PBXFileReference 1085 | lastKnownFileType 1086 | sourcecode.c.objc 1087 | path 1088 | NSMutableArrayTests.m 1089 | sourceTree 1090 | <group> 1091 | 1092 | 89690165173C16A100B403D2 1093 | 1094 | fileEncoding 1095 | 4 1096 | isa 1097 | PBXFileReference 1098 | lastKnownFileType 1099 | sourcecode.c.objc 1100 | path 1101 | NSNumberTests.m 1102 | sourceTree 1103 | <group> 1104 | 1105 | 89690166173C16A100B403D2 1106 | 1107 | fileEncoding 1108 | 4 1109 | isa 1110 | PBXFileReference 1111 | lastKnownFileType 1112 | sourcecode.c.objc 1113 | path 1114 | NSSetTests.m 1115 | sourceTree 1116 | <group> 1117 | 1118 | 89690167173C16A100B403D2 1119 | 1120 | fileEncoding 1121 | 4 1122 | isa 1123 | PBXFileReference 1124 | lastKnownFileType 1125 | sourcecode.c.objc 1126 | path 1127 | NSStringTests.m 1128 | sourceTree 1129 | <group> 1130 | 1131 | 89690168173C16A100B403D2 1132 | 1133 | fileRef 1134 | 89690161173C16A100B403D2 1135 | isa 1136 | PBXBuildFile 1137 | 1138 | 89690169173C16A100B403D2 1139 | 1140 | fileRef 1141 | 89690162173C16A100B403D2 1142 | isa 1143 | PBXBuildFile 1144 | 1145 | 8969016A173C16A100B403D2 1146 | 1147 | fileRef 1148 | 89690163173C16A100B403D2 1149 | isa 1150 | PBXBuildFile 1151 | 1152 | 8969016B173C16A100B403D2 1153 | 1154 | fileRef 1155 | 89690164173C16A100B403D2 1156 | isa 1157 | PBXBuildFile 1158 | 1159 | 8969016C173C16A100B403D2 1160 | 1161 | fileRef 1162 | 89690165173C16A100B403D2 1163 | isa 1164 | PBXBuildFile 1165 | 1166 | 8969016D173C16A100B403D2 1167 | 1168 | fileRef 1169 | 89690166173C16A100B403D2 1170 | isa 1171 | PBXBuildFile 1172 | 1173 | 8969016E173C16A100B403D2 1174 | 1175 | fileRef 1176 | 89690167173C16A100B403D2 1177 | isa 1178 | PBXBuildFile 1179 | 1180 | 94C453035CB8232DD1A5513E 1181 | 1182 | includeInIndex 1183 | 1 1184 | isa 1185 | PBXFileReference 1186 | lastKnownFileType 1187 | text.xcconfig 1188 | name 1189 | Pods.debug.xcconfig 1190 | path 1191 | Pods/Target Support Files/Pods/Pods.debug.xcconfig 1192 | sourceTree 1193 | <group> 1194 | 1195 | 9ADDC0C494D895C067A28C9A 1196 | 1197 | children 1198 | 1199 | 94C453035CB8232DD1A5513E 1200 | 9FAA69FB133006101A9A7843 1201 | 893C6A18A162B9FB06935ED4 1202 | 0D82F54D4F82F5960EA2CBA9 1203 | 1204 | isa 1205 | PBXGroup 1206 | name 1207 | Pods 1208 | sourceTree 1209 | <group> 1210 | 1211 | 9FAA69FB133006101A9A7843 1212 | 1213 | includeInIndex 1214 | 1 1215 | isa 1216 | PBXFileReference 1217 | lastKnownFileType 1218 | text.xcconfig 1219 | name 1220 | Pods.release.xcconfig 1221 | path 1222 | Pods/Target Support Files/Pods/Pods.release.xcconfig 1223 | sourceTree 1224 | <group> 1225 | 1226 | A06180063BF14EB68414F45F 1227 | 1228 | fileRef 1229 | D3D0B2406C7A45249244F020 1230 | isa 1231 | PBXBuildFile 1232 | 1233 | BC624B20376C4C728DE6A392 1234 | 1235 | buildActionMask 1236 | 2147483647 1237 | files 1238 | 1239 | inputPaths 1240 | 1241 | isa 1242 | PBXShellScriptBuildPhase 1243 | name 1244 | Check Pods Manifest.lock 1245 | outputPaths 1246 | 1247 | runOnlyForDeploymentPostprocessing 1248 | 0 1249 | shellPath 1250 | /bin/sh 1251 | shellScript 1252 | diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null 1253 | if [[ $? != 0 ]] ; then 1254 | cat << EOM 1255 | error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. 1256 | EOM 1257 | exit 1 1258 | fi 1259 | 1260 | 1261 | D3D0B2406C7A45249244F020 1262 | 1263 | explicitFileType 1264 | archive.ar 1265 | includeInIndex 1266 | 0 1267 | isa 1268 | PBXFileReference 1269 | path 1270 | libPods-ObjectiveSugarTests.a 1271 | sourceTree 1272 | BUILT_PRODUCTS_DIR 1273 | 1274 | 1275 | rootObject 1276 | 89690121173C0E4B00B403D2 1277 | 1278 | 1279 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar.xcodeproj/xcshareddata/xcschemes/ObjectiveSugar.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 57 | 63 | 64 | 65 | 66 | 67 | 72 | 73 | 75 | 81 | 82 | 83 | 84 | 85 | 91 | 92 | 93 | 94 | 95 | 96 | 106 | 108 | 114 | 115 | 116 | 117 | 118 | 119 | 125 | 127 | 133 | 134 | 135 | 136 | 138 | 139 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar.xcworkspace/xcshareddata/ObjectiveSugar.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | D33A2B74-EF5E-434B-9D41-0462E9F67889 9 | IDESourceControlProjectName 10 | ObjectiveSugar 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | A903A85C48C7E54B4809BF250D9264F78D3D0DB1 14 | ssh://github.com/supermarin/ObjectiveSugar.git 15 | 16 | IDESourceControlProjectPath 17 | Example/ObjectiveSugar.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | A903A85C48C7E54B4809BF250D9264F78D3D0DB1 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/supermarin/ObjectiveSugar.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | A903A85C48C7E54B4809BF250D9264F78D3D0DB1 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | A903A85C48C7E54B4809BF250D9264F78D3D0DB1 36 | IDESourceControlWCCName 37 | ObjectiveSugar 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ObjectiveSugar 4 | // 5 | // Created by Marin Usalj on 5/9/13. 6 | // Copyright (c) 2013 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ObjectiveSugar 4 | // 5 | // Created by Marin Usalj on 5/9/13. 6 | // Copyright (c) 2013 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | return YES; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supermarin/ObjectiveSugar/3d1c39adb701938ce2e19f8e5a789c72450f18ed/Example/ObjectiveSugar/Default-568h@2x.png -------------------------------------------------------------------------------- /Example/ObjectiveSugar/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supermarin/ObjectiveSugar/3d1c39adb701938ce2e19f8e5a789c72450f18ed/Example/ObjectiveSugar/Default.png -------------------------------------------------------------------------------- /Example/ObjectiveSugar/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supermarin/ObjectiveSugar/3d1c39adb701938ce2e19f8e5a789c72450f18ed/Example/ObjectiveSugar/Default@2x.png -------------------------------------------------------------------------------- /Example/ObjectiveSugar/ObjectiveSugar-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | in.supermar.${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 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar/ObjectiveSugar-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ObjectiveSugar' target in the 'ObjectiveSugar' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iOS SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/ObjectiveSugar/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ObjectiveSugar 4 | // 5 | // Created by Marin Usalj on 5/9/13. 6 | // Copyright (c) 2013 supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/CAdditionsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CAdditionsTests.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 1/15/13. 6 | // Copyright 2013 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ObjectiveSugar.h" 11 | 12 | SPEC_BEGIN(c_additions_tests) 13 | 14 | describe(@"unless", ^{ 15 | __block BOOL wasExecuted; 16 | beforeEach(^{ 17 | wasExecuted = NO; 18 | }); 19 | 20 | context(@"condition is false", ^{ 21 | it(@"executes the body", ^{ 22 | unless(NO) { 23 | wasExecuted = YES; 24 | } 25 | [[@(wasExecuted) should] beYes]; 26 | }); 27 | it(@"works with inline conditions", ^{ 28 | unless((1 == 4)) { 29 | wasExecuted = YES; 30 | } 31 | [[@(wasExecuted) should] beYes]; 32 | }); 33 | it(@"works with inline conditions without extra parenthesies", ^{ 34 | unless(1 == 4) { 35 | wasExecuted = YES; 36 | } 37 | [[@(wasExecuted) should] beYes]; 38 | }); 39 | it(@"works with the comma operator", ^{ 40 | int i = 1; 41 | unless(i++, 1 == 4) { 42 | wasExecuted = YES; 43 | } 44 | [[@(i) should] equal:@2]; 45 | [[@(wasExecuted) should] beYes]; 46 | }); 47 | }); 48 | 49 | context(@"condition is true", ^{ 50 | it(@"doesn't execute the body", ^{ 51 | unless(YES) { 52 | [[@(YES) should] beNo]; 53 | } 54 | }); 55 | }); 56 | }); 57 | 58 | describe(@"until", ^{ 59 | __block int condCount; 60 | __block int bodyCount; 61 | 62 | beforeEach(^{ 63 | condCount = 0; 64 | bodyCount = 0; 65 | }); 66 | 67 | context(@"In an until loop", ^{ 68 | context(@"condition is true", ^{ 69 | it(@"evaluates the condition once", ^{ 70 | int i = 1; 71 | until(--i == 0) { 72 | [[@(YES) should] beNo]; 73 | } 74 | [[@(i) should] equal:@0]; 75 | }); 76 | it(@"doesn't execute the body", ^{ 77 | until(YES) { 78 | [[@(YES) should] beNo]; 79 | } 80 | }); 81 | it(@"works with the comma operator", ^{ 82 | BOOL didEval = NO; 83 | until(didEval = YES, YES) { 84 | [[@(YES) should] beNo]; 85 | } 86 | [[@(didEval) should] beYes]; 87 | }); 88 | }); 89 | 90 | context(@"condition starts false and later changes to true", ^{ 91 | it(@"evaluates the condition one fewer time than the body", ^{ 92 | int iterations = 10; 93 | until(condCount++, iterations == 0) { 94 | bodyCount++; 95 | iterations--; 96 | } 97 | [[@(iterations) should] equal:@0]; 98 | [[@(bodyCount) should] equal:@10]; 99 | [[@(condCount) should] equal:@(bodyCount + 1)]; 100 | }); 101 | it(@"allows early breaking out of the loop", ^{ 102 | until(condCount++, bodyCount == 100) { 103 | bodyCount++; 104 | unless(bodyCount < 50) 105 | break; 106 | } 107 | [[@(bodyCount) should] equal:@50]; 108 | [[@(condCount) should] equal:@(bodyCount)]; 109 | }); 110 | }); 111 | }); 112 | 113 | context(@"In a do-until loop", ^{ 114 | context(@"condition is true", ^{ 115 | it(@"both evaluates the condition and executes the body exactly once", ^{ 116 | do { 117 | bodyCount++; 118 | } until(condCount++, YES); 119 | [[@(bodyCount) should] equal:@1]; 120 | [[@(condCount) should] equal:@(bodyCount)]; 121 | }); 122 | it(@"allows breaking out of the loop without ever evaluating the condition", ^{ 123 | do { 124 | bodyCount++; 125 | break; 126 | } until(condCount++, YES); 127 | [[@(bodyCount) should] equal:@1]; 128 | [[@(condCount) should] equal:@0]; 129 | }); 130 | }); 131 | 132 | context(@"condition starts false and later changes to true", ^{ 133 | it(@"evaluates the condition and the body the same number of times", ^{ 134 | int iterations = 10; 135 | do { 136 | bodyCount++; 137 | iterations--; 138 | } until(condCount++, iterations == 0); 139 | [[@(iterations) should] equal:@0]; 140 | [[@(bodyCount) should] equal:@10]; 141 | [[@(condCount) should] equal:@(bodyCount)]; 142 | }); 143 | it(@"allows breaking out of the loop early without evaluating the condition again", ^{ 144 | do { 145 | bodyCount++; 146 | if(bodyCount == 50) 147 | break; 148 | } until(condCount++, bodyCount == 100); 149 | [[@(bodyCount) should] equal:@50]; 150 | [[@(condCount) should] equal:@(bodyCount - 1)]; 151 | }); 152 | it(@"allows continuing to next loop iteration", ^{ 153 | int a = 2, b = 3; 154 | do { 155 | bodyCount++; 156 | a++; 157 | unless(a - b >= 4) 158 | continue; 159 | b += a; 160 | } until(condCount++, a == 13); 161 | [[@(a) should] equal:@13]; 162 | [[@(b) should] equal:@10]; 163 | [[@(bodyCount) should] equal:@11]; 164 | [[@(condCount) should] equal:@(bodyCount)]; 165 | }); 166 | }); 167 | 168 | context(@"condition is always false", ^{ 169 | it(@"allows breaking out of the loop to avoid an infinite loop", ^{ 170 | do { 171 | bodyCount++; 172 | unless(bodyCount < 42) 173 | break; 174 | } until(condCount++, NO); 175 | [[@(bodyCount) should] equal:@42]; 176 | [[@(condCount) should] equal:@(bodyCount - 1)]; 177 | }); 178 | }); 179 | }); 180 | }); 181 | 182 | SPEC_END 183 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/NSArrayTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArrayCategoriesTests.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 7/13/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "ObjectiveSugar.h" 10 | #import "Kiwi.h" 11 | 12 | SPEC_BEGIN(ArrayAdditions) 13 | 14 | describe(@"NSArray categories", ^{ 15 | 16 | NSArray *sampleArray = @[@"first", @"second", @"third"]; 17 | NSArray *oneToTen = @[ @1, @2, @3, @4, @5, @6, @7, @8, @9, @10 ]; 18 | let(duplicate, ^{ return [NSMutableArray new]; }); 19 | 20 | it(@"aliases -anyObject to -sample", ^{ 21 | [[sampleArray should] receive:@selector(sample)]; 22 | [sampleArray anyObject]; 23 | }); 24 | 25 | it(@"-sample returns a random object", ^{ 26 | [[theValue([sampleArray indexOfObject:sampleArray.sample]) shouldNot] equal:theValue(NSNotFound)]; 27 | }); 28 | 29 | it(@"-sample of empty array returns nil", ^{ 30 | NSArray *emptyArray = @[]; 31 | [emptyArray.sample shouldBeNil]; 32 | }); 33 | 34 | context(@"Iterating using block", ^{ 35 | 36 | it(@"iterates using -each:^", ^{ 37 | [sampleArray each:^(id object) { 38 | [duplicate addObject:object]; 39 | }]; 40 | [[duplicate should] equal:sampleArray]; 41 | }); 42 | 43 | it(@"iterates using -eachWithIndex:^", ^{ 44 | [sampleArray eachWithIndex:^(id object, NSUInteger index) { 45 | [[object should] equal:[sampleArray objectAtIndex:index]]; 46 | [duplicate addObject:object]; 47 | }]; 48 | [[duplicate should] equal:sampleArray]; 49 | }); 50 | 51 | it(@"iterates using -each:^withOptions:", ^{ 52 | [sampleArray each:^(id object) { 53 | [duplicate addObject:object]; 54 | } options:NSEnumerationReverse]; 55 | 56 | [[duplicate should] equal:[sampleArray reverse]]; 57 | }); 58 | 59 | it(@"iterates using -eachWithIndex:^withOptions:", ^{ 60 | [sampleArray eachWithIndex:^(id object, NSUInteger index) { 61 | [[object should] equal:[sampleArray objectAtIndex:index]]; 62 | [duplicate addObject:object]; 63 | } options:NSEnumerationReverse]; 64 | 65 | [[duplicate should] equal:[sampleArray reverse]]; 66 | }); 67 | 68 | }); 69 | 70 | it(@"aliases -containsObject to -includes", ^{ 71 | [[@([sampleArray includes:@"second"]) should] equal:@(YES)]; 72 | }); 73 | 74 | it(@"-map returns an array of objects returned by the block", ^{ 75 | NSArray *mapped = [sampleArray map:^id(id object) { 76 | return [NSNumber numberWithBool:[object isEqualToString:@"second"]]; 77 | }]; 78 | 79 | [[mapped should] containObjects:@(NO), @(YES), @(NO), nil]; 80 | }); 81 | 82 | it(@"-map treats nils the same way as -valueForKeyPath:", ^{ 83 | NSArray *users = @[@{@"name": @"Marin"},@{},@{@"name": @"Neil"}]; 84 | [[[users map:^id(NSDictionary *user) { 85 | return user[@"name"]; 86 | }] should] equal:[users valueForKeyPath:@"name"]]; 87 | }); 88 | 89 | it(@"-select returns an array containing all the elements of NSArray for which block is not false", ^{ 90 | [[[oneToTen select:^BOOL(id object) { 91 | return [object integerValue] % 3 == 0; 92 | }] should] equal:@[ @3, @6, @9 ]]; 93 | }); 94 | 95 | it(@"-detect returns the first element in NSArray for which block is true", ^{ 96 | [[[oneToTen detect:^BOOL(id object) { 97 | return [object intValue] % 3 == 0; 98 | }] should] equal:@3]; 99 | }); 100 | 101 | it(@"-detect is safe", ^{ 102 | [[[oneToTen detect:^BOOL(id object) { 103 | return [object intValue] == 1232132143; 104 | }] should] beNil]; 105 | }); 106 | 107 | it(@"-find aliases detect", ^{ 108 | [[[oneToTen find:^BOOL(id object) { 109 | return [object intValue] % 3 == 0; 110 | }] should] equal:@3]; 111 | }); 112 | 113 | it(@"-reject returns an array containing all the elements of NSArray for which block is false", ^{ 114 | [[[oneToTen reject:^BOOL(id object) { 115 | return [object integerValue] % 3 == 0; 116 | }] should] equal:@[ @1, @2, @4, @5, @7, @8, @10 ]]; 117 | }); 118 | 119 | it(@"-flatten returns a one-dimensional array that is a recursive flattening of the array", ^{ 120 | NSArray *multiDimensionalArray = @[ @[ @1, @2, @3 ], @[ @4, @5, @6, @[ @7, @8 ] ], @9, @10 ]; 121 | [[[multiDimensionalArray flatten] should] equal:oneToTen]; 122 | }); 123 | 124 | it(@"-compact removes NSNull objects", ^{ 125 | NSArray *arrayWithNulls = @[@1, @2, [NSNull null], @3, [NSNull null], @4]; 126 | [[[arrayWithNulls compact] should] equal:@[@1, @2, @3, @4]]; 127 | }); 128 | 129 | it(@"-reduce returns a result of all the elements", ^{ 130 | [[[sampleArray reduce:^id(NSString *accumulator, NSString *word) { 131 | return [accumulator stringByAppendingString:word.uppercaseString]; 132 | }] should] equal:@"firstSECONDTHIRD"]; 133 | 134 | [[[oneToTen reduce:^id(NSNumber *accumulator, NSNumber *numbah) { 135 | return @(accumulator.intValue + numbah.intValue); 136 | }] should] equal:@55]; 137 | }); 138 | 139 | it(@"-reduce:withBlock with accumulator behaves like -reduce and starts with user provided element", ^{ 140 | [[[sampleArray reduce:@"" withBlock:^id(NSString *accumulator, NSString *word) { 141 | return [accumulator stringByAppendingString:word.uppercaseString]; 142 | }] should] equal:@"FIRSTSECONDTHIRD"]; 143 | 144 | [[[oneToTen reduce:@5 withBlock:^id(NSNumber *accumulator, NSNumber *numbah) { 145 | return @(accumulator.intValue + numbah.intValue); 146 | }] should] equal:@60]; 147 | }); 148 | 149 | it(@"-unique produces a duplicate-free array", ^{ 150 | NSArray *arrayWithDuplicates = @[@1, @1, @"something", @"something"]; 151 | [[[arrayWithDuplicates unique] should] equal:@[@1, @"something"]]; 152 | }); 153 | 154 | context(@"array subsets", ^{ 155 | 156 | it(@"creates subset of array", ^{ 157 | [[[sampleArray take:2] should] equal:@[ @"first", @"second" ]]; 158 | }); 159 | 160 | 161 | it(@"creates subset of array and shouldn't raise exeption", ^{ 162 | [[[sampleArray take:[sampleArray count]+1] should] equal:sampleArray]; 163 | }); 164 | 165 | it(@"creates subset of array using block", ^{ 166 | [[[sampleArray takeWhile:^BOOL(id object) { 167 | 168 | return ![object isEqualToString:@"third"]; 169 | 170 | }] should] equal:@[ @"first", @"second" ]]; 171 | }); 172 | 173 | }); 174 | 175 | context(@"array range subscripting", ^{ 176 | 177 | it(@"returns an array containing elements at the specified range when passing an NSValue containing an NSRange", ^{ 178 | NSValue *range = [NSValue valueWithRange: NSMakeRange(2, 5)]; 179 | [[oneToTen[range] should] equal:@[@3, @4, @5, @6, @7]]; 180 | }); 181 | 182 | it(@"returns subarray with NSRange in string format", ^{ 183 | [[oneToTen[@"2,5"] should] equal:@[@3, @4, @5, @6, @7]]; 184 | }); 185 | 186 | it(@"returns subarray with inclusive range", ^{ 187 | [[oneToTen[@"2..5"] should] equal:@[@3, @4, @5, @6]]; 188 | }); 189 | 190 | it(@"returns subarray with inclusive range but excludes the end value", ^{ 191 | [[oneToTen[@"2...5"] should] equal:@[@3, @4, @5]]; 192 | }); 193 | 194 | it(@"returns subarray with inclusive range up to the end element", ^{ 195 | [[oneToTen[@"2..-1"] should] equal:@[@3, @4, @5, @6, @7, @8, @9, @10]]; 196 | }); 197 | 198 | it(@"returns subarray with inclusive range up to the element before end element", ^{ 199 | [[oneToTen[@"2...-1"] should] equal:@[@3, @4, @5, @6, @7, @8, @9]]; 200 | }); 201 | 202 | it(@"returns an empty array when passing an invalid or empty range", ^{ 203 | [[oneToTen[@"notarange"] should] equal:@[]]; 204 | }); 205 | 206 | it(@"throws an invalid argument exception when passing anything other than an NSString or NSValue", ^{ 207 | [[theBlock(^{ 208 | [oneToTen[[[NSSet alloc] initWithArray:@[@1, @2]]] description]; 209 | }) should] raiseWithName:NSInvalidArgumentException reason:@"expected NSString or NSValue argument, got __NSSetI instead"]; 210 | }); 211 | 212 | it(@"shouldn't break existing indexed subscripting", ^{ 213 | [[oneToTen[1] should] equal:@2]; 214 | }); 215 | }); 216 | 217 | context(@"join elements", ^{ 218 | 219 | it(@"join the array with elements ", ^{ 220 | [[[oneToTen join] should] equal:@"12345678910"]; 221 | }); 222 | 223 | it(@"join the array with elements separated by a dash", ^{ 224 | [[[sampleArray join:@"-"] should] equal:@"first-second-third"]; 225 | }); 226 | 227 | }); 228 | 229 | context(@"reverse elements", ^{ 230 | it(@"reverses the elements in the array", ^{ 231 | [[[oneToTen reverse] should] equal:@[@10, @9, @8, @7, @6, @5, @4, @3, @2, @1]]; 232 | }); 233 | }); 234 | 235 | context(@"sorting", ^{ 236 | 237 | it(@"-sort aliases -sortUsingComparator:", ^{ 238 | [[[@[ @4, @1, @3, @2 ] sort] should] equal:@[ @1, @2, @3, @4 ]]; 239 | }); 240 | 241 | it(@"-sortsortBy sorts using the default comparator on the given key:", ^{ 242 | NSDictionary *dict_1 = @{@"name": @"1"}; 243 | NSDictionary *dict_2 = @{@"name": @"2"}; 244 | NSDictionary *dict_3 = @{@"name": @"3"}; 245 | NSDictionary *dict_4 = @{@"name": @"3"}; 246 | [[[@[ dict_4, dict_1, dict_3, dict_2 ] sortBy:@"name"] should] equal:@[ dict_1, dict_2, dict_3, dict_4 ]]; 247 | }); 248 | 249 | }); 250 | 251 | }); 252 | 253 | 254 | describe(@"Set operations", ^{ 255 | 256 | NSArray *a = @[ @1, @2 ]; 257 | NSArray *b = @[ @2, @3 ]; 258 | 259 | it(@"return the elements common to both arrays ", ^{ 260 | [[[a intersectionWithArray:b] should] equal:@[ @2 ]]; 261 | }); 262 | 263 | it(@"combine the two arrays, removing duplicate elements", ^{ 264 | [[[a unionWithArray:b] should] equal:@[ @1, @2, @3 ]]; 265 | }); 266 | 267 | it(@"return the elements in a that are not in b", ^{ 268 | [[[a relativeComplement:b] should] equal:@[ @1 ]]; 269 | }); 270 | 271 | it(@"return the elements in b that are not in a", ^{ 272 | [[[b relativeComplement:a] should] equal:@[ @3 ]]; 273 | }); 274 | 275 | it(@"return the elements unique to both arrays", ^{ 276 | [[[a symmetricDifference:b] should] equal:@[ @1, @3 ]]; 277 | }); 278 | }); 279 | 280 | 281 | SPEC_END 282 | 283 | 284 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/NSDictionaryTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionaryTests.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "Kiwi.h" 10 | #import "ObjectiveSugar.h" 11 | 12 | SPEC_BEGIN(DictionaryAdditions) 13 | 14 | describe(@"Iterators", ^{ 15 | 16 | NSDictionary *sampleDict = @{ 17 | @"one" : @1, 18 | @"two" : @2, 19 | @"three" : @3 20 | }; 21 | __block NSInteger counter; 22 | 23 | beforeEach(^{ 24 | counter = 0; 25 | }); 26 | 27 | afterEach(^{ 28 | [[@(counter) should] equal:@(sampleDict.allKeys.count)]; 29 | }); 30 | 31 | 32 | it(@"iterates each key and value", ^{ 33 | [sampleDict each:^(id key, id value) { 34 | [[sampleDict.allKeys[counter] should] equal:key]; 35 | [[sampleDict.allValues[counter] should] equal:value]; 36 | counter ++; 37 | }]; 38 | }); 39 | 40 | it(@"iterates all keys", ^{ 41 | [sampleDict eachKey:^(id key){ 42 | [[sampleDict.allKeys[counter] should] equal:key]; 43 | counter ++; 44 | }]; 45 | }); 46 | 47 | it(@"iterates all values", ^{ 48 | [sampleDict eachValue:^(id value){ 49 | [[sampleDict.allValues[counter] should] equal:value]; 50 | counter ++; 51 | }]; 52 | }); 53 | 54 | it(@"iterates all keys when mapping", ^{ 55 | NSArray *mapped = [sampleDict map:^id(id key, id value) { 56 | counter ++; 57 | return key; 58 | }]; 59 | 60 | [[mapped should] equal:sampleDict.allKeys]; 61 | }); 62 | 63 | it(@"iterates all values when mapping", ^{ 64 | NSArray *mapped = [sampleDict map:^id(id key, id value) { 65 | counter ++; 66 | return value; 67 | }]; 68 | 69 | [[mapped should] equal:sampleDict.allValues]; 70 | }); 71 | }); 72 | 73 | describe(@"Keys", ^{ 74 | 75 | NSDictionary *sampleDict = @{ 76 | @"one": @1, 77 | @"two": @2, 78 | @"null": [NSNull null] 79 | }; 80 | 81 | it(@"checks that dictionary contains the specified key", ^{ 82 | [[@([sampleDict hasKey:@"one"]) should] beTrue]; 83 | [[@([sampleDict hasKey:@"imaginaryKey"]) should] beFalse]; 84 | }); 85 | 86 | it(@"tolerates null keys", ^{ 87 | [[@([sampleDict hasKey:@"null"]) should] beTrue]; 88 | }); 89 | 90 | }); 91 | 92 | describe(@"Pick", ^{ 93 | NSDictionary *sampleDict = @{ 94 | @"one": @1, 95 | @"two": @2, 96 | @"null": [NSNull null] 97 | }; 98 | 99 | it(@"returns a new dictionary with only the whitelisted keys", ^{ 100 | [[[sampleDict pick:@[@"one", @"two"]] should] equal:@{ 101 | @"one": @1, 102 | @"two": @2 103 | }]; 104 | }); 105 | }); 106 | 107 | describe(@"Omit", ^{ 108 | NSDictionary *sampleDict = @{ 109 | @"one": @1, 110 | @"two": @2, 111 | @"null": [NSNull null] 112 | }; 113 | 114 | it(@"returns a new dictionary without the blacklisted keys", ^{ 115 | [[[sampleDict omit:@[@"one", @"two"]] should] equal:@{@"null": [NSNull null]}]; 116 | }); 117 | }); 118 | 119 | describe(@"Merge", ^{ 120 | let(h1, ^{ return @{ @"a" : @100, @"b" : @200 }; }); 121 | let(h2, ^{ return @{ @"b" : @254, @"c" : @300 }; }); 122 | 123 | it(@"returns a new dictionary containing the contents of h1 and h2", ^{ 124 | [[[h1 merge:h2] should] equal:@{ @"a" : @100, @"b" : @254, @"c" : @300 }]; 125 | }); 126 | }); 127 | 128 | describe(@"Merge with block", ^{ 129 | let(h1, ^{ return @{ @"a" : @100, @"b" : @200 }; }); 130 | let(h2, ^{ return @{ @"b" : @254, @"c" : @300 }; }); 131 | 132 | it(@"returns a new dictionary containing the contents of h1 and h2", ^{ 133 | [[[h1 merge:h2 block:^id(id key, id oldVal, id newVal) { 134 | return @([newVal intValue] - [oldVal intValue]); 135 | }] should] equal:@{ @"a" : @100, @"b" : @54, @"c" : @300 }]; 136 | }); 137 | }); 138 | 139 | describe(@"Invert", ^{ 140 | let(sampleDict, ^{ return @{ @"one" : @1, 141 | @"two" : @2, 142 | @"three" : @3 }; }); 143 | 144 | it(@"returns a new dictionary where keys are values and values are keys", ^{ 145 | [[[sampleDict invert] should] equal:@{ @1 : @"one", @2 : @"two", @3 : @"three" }]; 146 | NSLog(@"%@", [sampleDict invert]); 147 | }); 148 | }); 149 | 150 | SPEC_END 151 | 152 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/NSMutableArrayTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArrayTests.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "ObjectiveSugar.h" 10 | #import "Kiwi.h" 11 | 12 | SPEC_BEGIN(MutableArrayAdditions) 13 | 14 | describe(@"NSMutableArray categories", ^{ 15 | 16 | let(mutableArray, ^{ return [NSMutableArray arrayWithObjects:@1, @2, @3, @4, @5, nil]; }); 17 | 18 | it(@"-push aliases addObject", ^{ 19 | [mutableArray push:@6]; 20 | [[mutableArray should] equal:@[ @1, @2, @3, @4, @5, @6 ]]; 21 | }); 22 | 23 | it(@"-pop removes and returns the last element of the array", ^{ 24 | [[[mutableArray pop] should] equal:@5]; 25 | [[mutableArray should] equal:@[ @1, @2, @3, @4 ]]; 26 | }); 27 | 28 | it(@"-pop with parameter removes and returns the last n elements of the array", ^{ 29 | [[[mutableArray pop:2] should] equal:@[ @4, @5 ]]; 30 | [[mutableArray should] equal:@[ @1, @2, @3 ]]; 31 | }); 32 | 33 | it(@"-concat aliases addObjectsFromArray:", ^{ 34 | [mutableArray concat:@[ @7, @8, @9 ]]; 35 | [[mutableArray should] equal:@[ @1, @2, @3, @4, @5, @7, @8, @9 ]]; 36 | }); 37 | 38 | it(@"-shift removes and returns the first element of the array", ^{ 39 | [[[mutableArray shift] should] equal:@1]; 40 | [[mutableArray should] equal:@[ @2, @3, @4, @5 ]]; 41 | }); 42 | 43 | it(@"-shift with parameter removes and returns n elements of the array", ^{ 44 | [[[mutableArray shift:2] should] equal:@[ @1, @2 ]]; 45 | [[mutableArray should] equal:@[ @3, @4, @5 ]]; 46 | }); 47 | 48 | it(@"-shift with 0 returns an empty array and doesn't change original mutable array", ^{ 49 | [[[mutableArray shift:0] should] equal:@[]]; 50 | [[mutableArray should] equal:@[ @1, @2, @3, @4, @5 ]]; 51 | }); 52 | 53 | it(@"-keepIf keeps the objects passing the block", ^{ 54 | NSMutableArray *array = @[@8, @5, @9, @1, @7, @14, @17, @87, @64].mutableCopy; 55 | 56 | [array keepIf:^BOOL(id object) { 57 | return [object intValue] % 2 == 0; 58 | }]; 59 | 60 | [[array should] equal:@[ @8, @14, @64 ]]; 61 | }); 62 | 63 | }); 64 | 65 | SPEC_END 66 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/NSNumberTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumberTests.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/21/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "ObjectiveSugar.h" 10 | #import "Kiwi.h" 11 | 12 | SPEC_BEGIN(NumberAdditions) 13 | 14 | describe(@"Iterators", ^{ 15 | 16 | __block NSInteger counter; 17 | 18 | beforeEach(^{ 19 | counter = 0; 20 | }); 21 | 22 | it(@"-upto iterates inclusively", ^{ 23 | __block NSInteger startingPoint = 5; 24 | 25 | [@(startingPoint) upto:8 do:^(NSInteger number) { 26 | [[@(number) should] equal:@(startingPoint + counter)]; 27 | counter ++; 28 | }]; 29 | 30 | [[@(counter) should] equal:@(4)]; 31 | }); 32 | 33 | it(@"-downto iterates inclusively", ^{ 34 | __block NSInteger startingPoint = 8; 35 | 36 | [@(startingPoint) downto:4 do:^(NSInteger number) { 37 | [[@(number) should] equal:@(startingPoint - counter)]; 38 | counter ++; 39 | }]; 40 | 41 | [[@(counter) should] equal:@(5)]; 42 | }); 43 | 44 | 45 | it(@"times: iterates the exact number of times", ^{ 46 | [@5 times:^{ 47 | counter ++; 48 | }]; 49 | [[@(counter) should] equal:@5]; 50 | }); 51 | 52 | it(@"timesWithIndex: iterates with the right index", ^{ 53 | [@5 timesWithIndex:^(NSUInteger index) { 54 | [[@(index) should] equal:@(counter)]; 55 | counter ++; 56 | }]; 57 | [[@(counter) should] equal:@5]; 58 | }); 59 | 60 | it(@"tests singular date inflections", ^{ 61 | [[[@1 second] should] equal:@1]; 62 | [[[@1 minute] should] equal:@60]; 63 | [[[@1 hour] should] equal:@3600]; 64 | [[[@1 day] should] equal:@86400]; 65 | [[[@1 week] should] equal:@604800]; 66 | [[[@1 fortnight] should] equal:@1209600]; 67 | [[[@1 month] should] equal:@2592000]; 68 | [[[@1 year] should] equal:@31557600]; 69 | }); 70 | 71 | it(@"tests plural date inflections", ^{ 72 | [[@(2).seconds should] equal:@2]; 73 | [[@(2).minutes should] equal:@120]; 74 | [[@(2).hours should] equal:@7200]; 75 | [[@(2).days should] equal:@172800]; 76 | [[@(2).weeks should] equal:@1209600]; 77 | [[@(2).fortnights should] equal:@2419200]; 78 | [[@(2).months should] equal:@5184000]; 79 | [[@(2).years should] equal:@63115200]; 80 | }); 81 | 82 | it(@"tests ago inflection", ^{ 83 | NSDate *testValue = @(30).seconds.ago; 84 | NSDate *compareValue = [NSDate dateWithTimeIntervalSinceNow:-30]; 85 | 86 | [[@([testValue timeIntervalSince1970]) should] equal:@([compareValue timeIntervalSince1970]).doubleValue 87 | withDelta:0.0001]; 88 | }); 89 | 90 | it(@"tests since inflection", ^{ 91 | NSDate *now = [NSDate date]; 92 | NSDate *testValue = [@(10).minutes since:now]; 93 | NSDate *compareValue = [NSDate dateWithTimeInterval:600 sinceDate:now]; 94 | 95 | [[@([testValue timeIntervalSince1970]) should] equal:@([compareValue timeIntervalSince1970])]; 96 | }); 97 | 98 | it(@"tests until inflection", ^{ 99 | NSDate *now = [NSDate date]; 100 | NSDate *testValue = [@(10).minutes until:now]; 101 | NSDate *compareValue = [NSDate dateWithTimeInterval:-600 sinceDate:now]; 102 | 103 | [[@([testValue timeIntervalSince1970]) should] equal:@([compareValue timeIntervalSince1970])]; 104 | }); 105 | 106 | it(@"tests from_now inflection", ^{ 107 | NSDate *testValue = @(30).minutes.fromNow; 108 | NSDate *compareValue = [NSDate dateWithTimeIntervalSinceNow:(30 * 60)]; 109 | 110 | [[@([testValue timeIntervalSince1970]) should] equal:@([compareValue timeIntervalSince1970]).doubleValue 111 | withDelta:0.0001]; 112 | }); 113 | 114 | }); 115 | 116 | SPEC_END 117 | 118 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/NSSetTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSetTests.m 3 | // SampleProject 4 | // 5 | // Created by Marin Usalj on 11/23/12. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "Kiwi.h" 10 | #import "ObjectiveSugar.h" 11 | 12 | SPEC_BEGIN(SetAdditions) 13 | 14 | describe(@"Iterators", ^{ 15 | 16 | 17 | NSSet *sampleSet = [NSSet setWithArray:@[@"first", @"second", @"third"]]; 18 | 19 | context(@"Iterating using block", ^{ 20 | 21 | it(@"iterates using -each:^", ^{ 22 | NSMutableArray *duplicate = [sampleSet.allObjects mutableCopy]; 23 | 24 | [sampleSet each:^(id object) { 25 | [[duplicate should] contain:object]; 26 | [duplicate removeObject:object]; 27 | }]; 28 | [[duplicate should] beEmpty]; 29 | }); 30 | 31 | it(@"iterates using -eachWithIndex:^", ^{ 32 | NSMutableArray *duplicate = [sampleSet.allObjects mutableCopy]; 33 | 34 | [sampleSet eachWithIndex:^(id object, NSUInteger index) { 35 | [[object should] equal:sampleSet.allObjects[index]]; 36 | [duplicate removeObject:object]; 37 | }]; 38 | [[duplicate should] beEmpty]; 39 | }); 40 | 41 | }); 42 | 43 | context(@"first, last, sample", ^{ 44 | 45 | it(@"-first returns object at index 0", ^{ 46 | [[sampleSet.firstObject should] equal:sampleSet.allObjects[0]]; 47 | }); 48 | 49 | it(@"-first does not crash if there's no objects in set", ^{ 50 | KWBlock *block = [[KWBlock alloc] initWithBlock:^{ 51 | NSSet *empty = [NSSet set]; 52 | [empty.firstObject description]; 53 | }]; 54 | [[block shouldNot] raise]; 55 | }); 56 | 57 | it(@"-last returns the last object", ^{ 58 | [[sampleSet.lastObject should] equal:sampleSet.allObjects.lastObject]; 59 | }); 60 | 61 | it(@"-sample returns a random object", ^{ 62 | [[sampleSet member:sampleSet.sample] shouldNotBeNil]; 63 | }); 64 | 65 | it(@"-sample of empty set returns nil", ^{ 66 | NSSet *emptySet = [NSSet set]; 67 | [emptySet.sample shouldBeNil]; 68 | }); 69 | }); 70 | 71 | context(@"modifications", ^{ 72 | 73 | NSSet *cars = [NSSet setWithArray:@[@"Testarossa", @"F50", @"F458 Italia"]]; 74 | let(items, ^id{ 75 | return @[@{ @"value": @4 }, @{ @"value": @5 }, @{ @"value": @9 }]; 76 | }); 77 | 78 | it(@"-map returns an array of objects returned by the block", ^{ 79 | NSArray *mapped = [sampleSet map:^id(id object) { 80 | return @([object isEqualToString:@"third"]); 81 | }]; 82 | [[mapped should] containObjects:@NO, @YES, @NO, nil]; 83 | }); 84 | 85 | it(@"-map treats nils the same way as -valueForKeyPath:", ^{ 86 | NSSet *users = [NSSet setWithArray:@[@{@"name": @"Marin"}, @{@"fake": @"value"}, @{@"name": @"Neil"}]]; 87 | NSArray *mappedUsers = [users map:^id(NSDictionary *user) { return user[@"name"]; }]; 88 | 89 | [[mappedUsers.sort should] equal:[[users valueForKeyPath:@"name"] allObjects].sort]; 90 | [[mappedUsers should] haveCountOf:2]; 91 | [[mappedUsers should] containObjects:@"Marin", @"Neil", nil]; 92 | }); 93 | 94 | it(@"-select returns an array containing all the elements of NSArray for which block is not false", ^{ 95 | [[[cars select:^BOOL(NSString *car) { 96 | return [car isEqualToString:@"F50"]; 97 | }] should] equal:@[ @"F50" ]]; 98 | }); 99 | 100 | it(@"-reject returns an array containing all the elements of NSArray for which block is false", ^{ 101 | [[[cars reject:^BOOL(NSString* car) { 102 | return [car isEqualToString:@"F50"]; 103 | }] should] equal:@[ @"F458 Italia", @"Testarossa" ]]; 104 | }); 105 | 106 | it(@"-reduce returns a result of all the elements", ^{ 107 | [[[items reduce:^id(NSDictionary *accumulator, NSDictionary *item) { 108 | return [accumulator[@"value"] intValue] > [item[@"value"] intValue] 109 | ? accumulator : item; 110 | }] should] equal:@{ @"value": @9 }]; 111 | }); 112 | 113 | it(@"-reduce:withBlock with accumulator behaves like -reduce and starts with user provided element", ^{ 114 | [[[items reduce:@0 withBlock:^id(NSNumber *accumulator, NSDictionary *item) { 115 | return @(accumulator.intValue + [item[@"value"] intValue]); 116 | }] should] equal:@18]; 117 | }); 118 | 119 | }); 120 | 121 | context(@"sorting", ^{ 122 | 123 | it(@"-sort aliases -sortUsingComparator:", ^{ 124 | NSSet *numbers = [NSSet setWithArray:@[ @4, @1, @3, @2 ]]; 125 | [[[numbers sort] should] equal:@[ @1, @2, @3, @4 ]]; 126 | }); 127 | 128 | }); 129 | 130 | }); 131 | 132 | SPEC_END 133 | 134 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/NSStringTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSStringTests.m 3 | // SampleProject 4 | // 5 | // Created by Neil on 05/12/2012. 6 | // Copyright (c) 2012 @supermarin | supermar.in. All rights reserved. 7 | // 8 | 9 | #import "Kiwi.h" 10 | #import "ObjectiveSugar.h" 11 | 12 | SPEC_BEGIN(StringAdditions) 13 | 14 | describe(@"Foundation-style functions", ^{ 15 | 16 | it(@"NSStringWithFormat makes NSString -stringWithFormat", ^{ 17 | 18 | [[NSStringWithFormat(@"This is %@", @1) should] equal:@"This is 1"]; 19 | }); 20 | 21 | }); 22 | 23 | describe(@"Additions", ^{ 24 | 25 | NSString *sentence = @"Jane Doe's going in a shop,and,yeah;"; 26 | 27 | it(@"-split splits by whitespace", ^{ 28 | [[[@" the\r\nquick brown\t \tfox\n" split] should] equal:@[@"the", @"quick", @"brown", @"fox"]]; 29 | }); 30 | 31 | it(@"-split: splits with given delimiter", ^{ 32 | [[[sentence split:@","] should] equal:@[@"Jane Doe's going in a shop", @"and", @"yeah;"]]; 33 | }); 34 | 35 | it(@"-split: splits with delimiter string, not just a char", ^{ 36 | [[[@"one / two / three" split:@" / "] should] equal:@[@"one", @"two", @"three"]]; 37 | }); 38 | 39 | it(@"contains string", ^{ 40 | [[@([sentence containsString:@"jane doe"]) should] beTrue]; 41 | }); 42 | 43 | context(@"CamelCase and snake_case", ^{ 44 | 45 | it(@"converts snake_cased to CamelCased", ^{ 46 | [[[@"snake_case" camelCase] should] equal:@"SnakeCase"]; 47 | }); 48 | 49 | it(@"handles correctly snake case on beginning and at the end", ^{ 50 | [[[@"_snake_case" camelCase] should] equal:@"SnakeCase"]; 51 | [[[@"snake_case_" camelCase] should] equal:@"SnakeCase"]; 52 | }); 53 | 54 | }); 55 | 56 | context(@"lowerCamelCase", ^{ 57 | it(@"converts snake_case to snakeCase", ^{ 58 | [[[@"snake_case" lowerCamelCase] should] equal:@"snakeCase"]; 59 | }); 60 | 61 | it(@"handles extraneous underscores", ^{ 62 | [[[@"_snake_case" lowerCamelCase] should] equal:@"snakeCase"]; 63 | [[[@"snake_case_" lowerCamelCase] should] equal:@"snakeCase"]; 64 | }); 65 | }); 66 | 67 | it(@"-strip strips whitespaces and newlines from both ends", ^{ 68 | [[[@"\n Look mo, no empties!\n \n\n " strip] should] equal:@"Look mo, no empties!"]; 69 | }); 70 | 71 | }); 72 | 73 | describe(@"match", ^{ 74 | NSString *sentence = @"Find the thing in 'single quotes'!"; 75 | 76 | it(@"Should find the thing in single quotes", ^{ 77 | [[[sentence match:@"'[a-zA-Z ]+'"] should] equal:@"'single quotes'"]; 78 | }); 79 | 80 | it(@"Should return nil is no match is found", ^{ 81 | [[[sentence match:@"<[a-z]>"] should] beNil]; 82 | }); 83 | }); 84 | 85 | 86 | 87 | SPEC_END 88 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/ObjectiveSugarTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.mneorr.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/ObjectiveSugarTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, :deployment_target => "5.0" 2 | 3 | pod 'ObjectiveSugar', :path => '../' 4 | 5 | target :ObjectiveSugarTests, :exclusive => true do 6 | pod 'Kiwi', '~> 2.3.0', :inhibit_warnings => true 7 | end 8 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kiwi (2.3.1) 3 | - ObjectiveSugar (1.1.1) 4 | 5 | DEPENDENCIES: 6 | - Kiwi (~> 2.3.0) 7 | - ObjectiveSugar (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | ObjectiveSugar: 11 | :path: "../" 12 | 13 | SPEC CHECKSUMS: 14 | Kiwi: f038a6c61f7a9e4d7766bff5717aa3b3fdb75f55 15 | ObjectiveSugar: 0729eb74a757ea48b109a8902178c9b89aa58a9f 16 | 17 | COCOAPODS: 0.36.4 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2012 Marin Usalj, http://supermar.in 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /ObjectiveSugar.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'ObjectiveSugar' 3 | s.version = '1.1.1' 4 | s.summary = 'Objective C additions for humans. Write ObjC _like a boss_.' 5 | s.description = '-map, -each, -select, unless(true){}, -includes, -upto, -downto, and many more!' 6 | s.homepage = 'https://github.com/supermarin/ObjectiveSugar' 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.authors = { 'Marin Usalj' => "marin2211@gmail.com",'Neil Cowburn' => 'git@neilcowburn.com'} 9 | s.source = { :git => 'https://github.com/supermarin/ObjectiveSugar.git', :tag => s.version.to_s } 10 | s.social_media_url = "https://twitter.com/_supermarin" 11 | 12 | s.ios.deployment_target = '4.0' 13 | s.osx.deployment_target = '10.6' 14 | 15 | s.requires_arc = true 16 | 17 | s.source_files = 'Classes', 'Classes/**/*.{h,m}' 18 | end 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](http://i.imgur.com/BvHpSz0.png) 2 | 3 | > Write Objective C _like a boss_.
4 | 5 | A set of functional additions for Foundation you wish you'd had in the first place. 6 | 7 | 8 | [![Build Status](https://travis-ci.org/supermarin/ObjectiveSugar.svg?branch=master)](https://travis-ci.org/supermarin/ObjectiveSugar) 9 | 10 | 11 | ## Usage 12 | 13 | 1. Install via [CocoaPods](http://cocoapods.org/) 14 | 15 | ``` 16 | pod 'ObjectiveSugar' 17 | ``` 18 | 2. Import the public header 19 | 20 | ``` 21 | #import 22 | ``` 23 | 24 | 25 | ## Documentation 26 | 27 | 28 | __NSNumber__ additions 29 | ``` objc 30 | [@3 times:^{ 31 | NSLog(@"Hello!"); 32 | }]; 33 | // Hello! 34 | // Hello! 35 | // Hello! 36 | 37 | [@3 timesWithIndex:^(NSUInteger index) { 38 | NSLog(@"Another version with number: %d", index); 39 | }]; 40 | // Another version with number: 0 41 | // Another version with number: 1 42 | // Another version with number: 2 43 | 44 | 45 | [@1 upto:4 do:^(NSInteger numbah) { 46 | NSLog(@"Current number.. %d", numbah); 47 | }]; 48 | // Current number.. 1 49 | // Current number.. 2 50 | // Current number.. 3 51 | // Current number.. 4 52 | 53 | [@7 downto:4 do:^(NSInteger numbah) { 54 | NSLog(@"Current number.. %d", numbah); 55 | }]; 56 | // Current number.. 7 57 | // Current number.. 6 58 | // Current number.. 5 59 | // Current number.. 4 60 | 61 | NSDate *firstOfDecember = [NSDate date]; // let's pretend it's 1st of December 62 | 63 | NSDate *firstOfNovember = [@30.days since:firstOfDecember]; 64 | // 2012-11-01 00:00:00 +0000 65 | 66 | NSDate *christmas = [@7.days until:newYearsDay]; 67 | // 2012-12-25 00:00:00 +0000 68 | 69 | NSDate *future = @24.days.fromNow; 70 | // 2012-12-25 20:49:05 +0000 71 | 72 | NSDate *past = @1.month.ago; 73 | // 2012-11-01 20:50:28 +00:00 74 | ``` 75 | -- 76 | __NSArray__ / __NSSet__ additions 77 | ``` objc 78 | // All of these methods return a modified copy of the array. 79 | // They're not modifying the source array. 80 | 81 | NSArray *cars = @[@"Testarossa", @"F50", @"F458 Italia"]; // or NSSet 82 | 83 | [cars each:^(id object) { 84 | NSLog(@"Car: %@", object); 85 | }]; 86 | // Car: Testarossa 87 | // Car: F50 88 | // Car: F458 Italia 89 | 90 | [cars eachWithIndex:^(id object, NSUInteger index) { 91 | NSLog(@"Car: %@ index: %i", object, index); 92 | }]; 93 | // Car: Testarossa index: 0 94 | // Car: F50 index: 1 95 | // Car: F458 Italia index: 2 96 | 97 | [cars each:^(id object) { 98 | NSLog(@"Car: %@", object); 99 | } options:NSEnumerationReverse]; 100 | // Car: F458 Italia 101 | // Car: F50 102 | // Car: Testarossa 103 | 104 | [cars eachWithIndex:^(id object, NSUInteger index) { 105 | NSLog(@"Car: %@ index: %i", object, index); 106 | } options:NSEnumerationReverse]; 107 | // Car: F458 Italia index: 2 108 | // Car: F50 index: 1 109 | // Car: Testarossa index: 0 110 | 111 | [cars map:^(NSString* car) { 112 | return car.lowercaseString; 113 | }]; 114 | // testarossa, f50, f458 italia 115 | 116 | // Or, a more common example: 117 | [cars map:^(NSString* carName) { 118 | return [[Car alloc] initWithName:carName]; 119 | }]; 120 | // array of Car objects 121 | 122 | NSArray *mixedData = @[ @1, @"Objective Sugar!", @"Github", @4, @"5"]; 123 | 124 | [mixedData select:^BOOL(id object) { 125 | return ([object class] == [NSString class]); 126 | }]; 127 | // Objective Sugar, Github, 5 128 | 129 | [mixedData reject:^BOOL(id object) { 130 | return ([object class] == [NSString class]); 131 | }]; 132 | // 1, 4 133 | 134 | NSArray *numbers = @[ @5, @2, @7, @1 ]; 135 | [numbers sort]; 136 | // 1, 2, 5, 7 137 | 138 | cars.sample 139 | // 458 Italia 140 | cars.sample 141 | // F50 142 | 143 | ``` 144 | -- 145 | __NSArray__ only 146 | ``` objc 147 | 148 | NSArray *numbers = @[@1, @2, @3, @4, @5, @6]; 149 | 150 | // index from 2 to 4 151 | numbers[@"2..4"]; 152 | // [@3, @4, @5] 153 | 154 | // index from 2 to 4 (excluded) 155 | numbers[@"2...4"]; 156 | // [@3, @4] 157 | 158 | // With NSRange location: 2, length: 4 159 | numbers[@"2,4"]; 160 | // [@3, @4, @5, @6] 161 | 162 | NSValue *range = [NSValue valueWithRange:NSMakeRange(2, 4)]; 163 | numbers[range]; 164 | // [@3, @4, @5, @6] 165 | 166 | [numbers reverse]; 167 | // [@6, @5, @4, @3, @2, @1] 168 | 169 | 170 | NSArray *fruits = @[ @"banana", @"mango", @"apple", @"pear" ]; 171 | 172 | [fruits includes:@"apple"]; 173 | // YES 174 | 175 | [fruits take:3]; 176 | // banana, mango, apple 177 | 178 | [fruits takeWhile:^BOOL(id fruit) { 179 | return ![fruit isEqualToString:@"apple"]; 180 | }]; 181 | // banana, mango 182 | 183 | NSArray *nestedArray = @[ @[ @1, @2, @3 ], @[ @4, @5, @6, @[ @7, @8 ] ], @9, @10 ]; 184 | [nestedArray flatten]; 185 | // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 186 | 187 | NSArray *abc = @[ @"a", @"b", @"c" ]; 188 | [abc join]; 189 | // abc 190 | 191 | [abc join:@"-"]; 192 | // a-b-c 193 | 194 | NSArray *mixedData = @[ @1, @"Objective Sugar!", @"Github", @4, @"5"]; 195 | 196 | [mixedData detect:^BOOL(id object) { 197 | return ([object class] == [NSString class]); 198 | }]; 199 | // Objective Sugar 200 | 201 | 202 | 203 | // TODO: Make a better / simpler example of this 204 | NSArray *landlockedCountries = @[ @"Bolivia", @"Paraguay", @"Austria", @"Switzerland", @"Hungary" ]; 205 | NSArray *europeanCountries = @[ @"France", @"Germany", @"Austria", @"Spain", @"Hungary", @"Poland", @"Switzerland" ]; 206 | 207 | 208 | [landlockedCountries intersectionWithArray:europeanCountries]; 209 | // landlockedEuropeanCountries = Austria, Switzerland, Hungary 210 | 211 | [landlockedCountries unionWithArray:europeanCountries]; 212 | // landlockedOrEuropean = Bolivia, Paraguay, Austria, Switzerland, Hungary, France, Germany, Spain, Poland 213 | 214 | [landlockedCountries relativeComplement:europeanCountries]; 215 | // nonEuropeanLandlockedCountries = Bolivia, Paraguay 216 | 217 | [europeanCountries relativeComplement:landlockedCountries]; 218 | // notLandlockedEuropeanCountries = France, Germany, Spain, Poland 219 | 220 | [landlockedCountries symmetricDifference:europeanCountries]; 221 | // uniqueCountries = Bolivia, Paraguay, France, Germany, Spain, Poland 222 | ``` 223 | -- 224 | __NSMutableArray__ additions 225 | ``` objc 226 | NSMutableArray *people = @[ @"Alice", @"Benjamin", @"Christopher" ]; 227 | 228 | [people push:@"Daniel"]; // Alice, Benjamin, Christopher, Daniel 229 | 230 | [people pop]; // Daniel 231 | // people = Alice, Benjamin, Christopher 232 | 233 | [people pop:2]; // Benjamin, Christopher 234 | // people = Alice 235 | 236 | [people concat:@[ @"Evan", @"Frank", @"Gavin" ]]; 237 | // people = Alice, Evan, Frank, Gavin 238 | 239 | [people keepIf:^BOOL(id object) { 240 | return [object characterAtIndex:0] == 'E'; 241 | }]; 242 | // people = Evan 243 | 244 | ``` 245 | -- 246 | __NSDictionary__ additions 247 | ``` objc 248 | NSDictionary *dict = @{ @"one" : @1, @"two" : @2, @"three" : @3 }; 249 | 250 | [dict each:^(id key, id value){ 251 | NSLog(@"Key: %@, Value: %@", key, value); 252 | }]; 253 | // Key: one, Value: 1 254 | // Key: two, Value: 2 255 | // Key: three, Value: 3 256 | 257 | [dict eachKey:^(id key) { 258 | NSLog(@"Key: %@", key); 259 | }]; 260 | // Key: one 261 | // Key: two 262 | // Key: three 263 | 264 | [dict eachValue:^(id value) { 265 | NSLog(@"Value: %@", value); 266 | }]; 267 | // Value: 1 268 | // Value: 2 269 | // Value: 3 270 | 271 | [dict invert]; 272 | // { 1 = one, 2 = two, 3 = three} 273 | 274 | NSDictionary *errors = @{ 275 | @"username" : @[ @"already taken" ], 276 | @"password" : @[ @"is too short (minimum is 8 characters)", @"not complex enough" ], 277 | @"email" : @[ @"can't be blank" ]; 278 | }; 279 | 280 | [errors map:^(id attribute, id reasons) { 281 | return NSStringWithFormat(@"%@ %@", attribute, [reasons join:@", "]); 282 | }]; 283 | // username already taken 284 | // password is too short (minimum is 8 characters), not complex enough 285 | // email can't be blank 286 | 287 | [errors hasKey:@"email"] 288 | // true 289 | [errors hasKey:@"Alcatraz"] 290 | // false 291 | ``` 292 | -- 293 | __NSString__ additions 294 | ``` objc 295 | NSString *sentence = NSStringWithFormat(@"This is a text-with-argument %@", @1234); 296 | // This is a text-with-argument 1234 297 | 298 | [sentence split]; 299 | // array = this, is, a, text-with-argument, 1234 300 | 301 | [sentence split:@"-"] 302 | // array = this is a text, with, argument 1234 303 | 304 | [sentence containsString:@"this is a"]; 305 | // YES 306 | 307 | [sentence match:@"-[a-z]+-"] 308 | // -with- 309 | ``` 310 | -- 311 | __C__ additions 312 | ``` objc 313 | unless(_messages) { 314 | // The body is only executed if the condition is false 315 | _messages = [self initializeMessages]; 316 | } 317 | 318 | int iterations = 10; 319 | until(iterations == 0) { 320 | // The body is executed until the condition is false 321 | // 10 9 8 7 6 5 4 3 2 1 322 | printf("%d ", iterations); 323 | iterations--; 324 | } 325 | printf("\n"); 326 | 327 | iterations = 10; 328 | do { 329 | // The body is executed at least once until the condition is false 330 | // Will print: Executed! 331 | printf("Executed!\n"); 332 | } until(true); 333 | ``` 334 | 335 | ## Contributing 336 | 337 | ObjectiveSugar is tested with [Kiwi](https://github.com/allending/Kiwi), and tests are located in Example.
338 | If you plan on contributing to the project, please: 339 | 340 | * Write tests 341 | * Write documentation 342 | 343 | 344 | ## Team 345 | 346 | - Marin Usalj [@supermarin](https://github.com/supermarin) 347 | - Neil Cowburn [@neilco](https://github.com/neilco) 348 | 349 | --------------------------------------------------------------------------------