├── .gitattributes ├── .gitignore ├── GHKit.podspec ├── GHKit.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── GHKit.xccheckout ├── GHKit ├── GHKit.h ├── GHKitDefines.h ├── GHNSArray+Utils.h ├── GHNSArray+Utils.m ├── GHNSBundle+Utils.h ├── GHNSBundle+Utils.m ├── GHNSData+Utils.h ├── GHNSData+Utils.m ├── GHNSDate+Formatters.h ├── GHNSDate+Formatters.m ├── GHNSDate+Utils.h ├── GHNSDate+Utils.m ├── GHNSDictionary+NSNull.h ├── GHNSDictionary+NSNull.m ├── GHNSDictionary+Utils.h ├── GHNSDictionary+Utils.m ├── GHNSError+Utils.h ├── GHNSError+Utils.m ├── GHNSFileManager+Utils.h ├── GHNSFileManager+Utils.m ├── GHNSMutableArray+Utils.h ├── GHNSMutableArray+Utils.m ├── GHNSMutableDictionary+Utils.h ├── GHNSMutableDictionary+Utils.m ├── GHNSNotificationCenter+Utils.h ├── GHNSNotificationCenter+Utils.m ├── GHNSNumber+Utils.h ├── GHNSNumber+Utils.m ├── GHNSObject+Utils.h ├── GHNSObject+Utils.m ├── GHNSString+TimeInterval.h ├── GHNSString+TimeInterval.m ├── GHNSString+URL.h ├── GHNSString+URL.m ├── GHNSString+Utils.h ├── GHNSString+Utils.m ├── GHNSStringEnumerator.h ├── GHNSStringEnumerator.m ├── GHNSURL+Utils.h ├── GHNSURL+Utils.m ├── GHNSUserDefaults+Utils.h ├── GHNSUserDefaults+Utils.m ├── GHReversableDictionary.h ├── GHReversableDictionary.m ├── GHValidators.h ├── GHValidators.m ├── Info.plist └── iOS │ ├── GHCGUtils.h │ ├── GHCGUtils.m │ ├── GHUIColor+Utils.h │ ├── GHUIColor+Utils.m │ ├── GHUIImage+Utils.h │ ├── GHUIImage+Utils.m │ ├── GHUIUtils.h │ ├── GHUIUtils.m │ └── Info.plist ├── LICENSE ├── README.md └── Tests ├── GHDefinesTest.m ├── GHNSStringEnumeratorTest.m ├── GHReversableDictionaryTest.m ├── GHValidatorsTest.m ├── Info.plist ├── NSArrayUtilsTest.m ├── NSDataUtilsTest.m ├── NSDateParsingTest.m ├── NSDateUtilsTest.m ├── NSDictionaryNSNullTest.m ├── NSDictionaryUtilsTest.m ├── NSErrorUtilsTest.m ├── NSFileManagerUtilsTest.m ├── NSMutableArrayUtilsTest.m ├── NSMutableDictionaryUtilsTest.m ├── NSNumberUtilsTest.m ├── NSObjectUtilsTest.m ├── NSStringTimeIntervalTest.m ├── NSStringUtilsTest.m ├── NSURLUtilsTest.m └── iOS ├── GHUIColorUtilsTest.m ├── GHUIImageUtilsTest.m └── Info.plist /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -crlf -diff -merge 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | *.pbxuser 4 | *.mode1v3 5 | xcuserdata 6 | Documentation/output 7 | GHKit.docset 8 | GHKitIPhone.xcodeproj/project.xcworkspace/xcuserdata 9 | Project-iOS/build 10 | Project-iOS/Documentation 11 | tmp 12 | GHKit.xcworkspace/xcuserdata 13 | Pods 14 | -------------------------------------------------------------------------------- /GHKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "GHKit" 4 | s.version = "2.0.3" 5 | s.summary = "Objective-C categories and utilities" 6 | s.homepage = "https://github.com/gabriel/GHKit" 7 | s.license = "MIT" 8 | s.author = { "Gabriel Handford" => "gabrielh@gmail.com" } 9 | s.source = { :git => "https://github.com/gabriel/GHKit.git", :tag => s.version.to_s } 10 | 11 | s.ios.platform = :ios, "7.0" 12 | s.ios.deployment_target = "7.0" 13 | s.ios.source_files = "GHKit", "GHKit/iOS" 14 | 15 | s.osx.platform = :osx, "10.8" 16 | s.osx.deployment_target = "10.8" 17 | s.osx.source_files = "GHKit" 18 | 19 | s.requires_arc = true 20 | 21 | end 22 | -------------------------------------------------------------------------------- /GHKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GHKit.xcodeproj/project.xcworkspace/xcshareddata/GHKit.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 15693D2D-7570-4673-8BBF-E3E5B66EA5D3 9 | IDESourceControlProjectName 10 | GHKit 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | FCF1318D7FA6CAB10513AC324A1290B93E7E7F64 14 | github.com:gabriel/gh-kit.git 15 | 16 | IDESourceControlProjectPath 17 | GHKit.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | FCF1318D7FA6CAB10513AC324A1290B93E7E7F64 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:gabriel/gh-kit.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | FCF1318D7FA6CAB10513AC324A1290B93E7E7F64 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | FCF1318D7FA6CAB10513AC324A1290B93E7E7F64 36 | IDESourceControlWCCName 37 | gh-kit 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /GHKit/GHKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHKit.h 3 | // 4 | // Created by Gabe on 6/30/08. 5 | // 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | //! Project version number for GHKit. 31 | FOUNDATION_EXPORT double GHKitVersionNumber; 32 | 33 | //! Project version string for Testing. 34 | FOUNDATION_EXPORT const unsigned char GHKitVersionString[]; 35 | 36 | #import 37 | 38 | #import 39 | #import 40 | 41 | #import 42 | 43 | #import 44 | #import 45 | #import 46 | 47 | #import 48 | 49 | #import 50 | 51 | #import 52 | 53 | #import 54 | #import 55 | #import 56 | #import 57 | #import 58 | 59 | #import 60 | #import 61 | #import 62 | 63 | #import 64 | 65 | #import 66 | #import 67 | 68 | #import 69 | #import 70 | 71 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED 72 | #import 73 | #import 74 | #import 75 | #import 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /GHKit/GHKitDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHKitDefines.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/28/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #define GHDict(...) [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:__VA_ARGS__, nil] 31 | 32 | #define GHCGRectToString(rect) NSStringFromRect(NSRectFromCGRect(rect)) 33 | #define GHCGSizeToString(size) NSStringFromSize(NSSizeFromCGSize(size)) 34 | #define GHCGPointToString(point) NSStringFromPoint(NSPointFromCGPoint(point)) 35 | 36 | #define XCTAssertMainThread() NSAssert([NSThread isMainThread], @"Should be on main thread") 37 | 38 | // Default epsilon for float comparisons 39 | #define GH_EPSILON 1.0E-5 40 | 41 | #define GHProperties(obj, ...) [[obj dictionaryWithValuesForKeys:[NSArray arrayWithObjects:__VA_ARGS__, nil]] description] 42 | 43 | // Makes it easier to generate descriptions 44 | // - (NSString *)description { 45 | // return GHDescription(@"prop1", @"prop2", @"prop3"); 46 | // } 47 | #define GHDescription(...) \ 48 | [NSString stringWithFormat:@"%@ %@", \ 49 | [super description], \ 50 | [[self dictionaryWithValuesForKeys:[NSArray arrayWithObjects:__VA_ARGS__, nil]] description] \ 51 | ] 52 | 53 | // 54 | // Creates arguments NSArray from var args, with first object named 'object' 55 | // - (void)methodName:(id)arg1 withObjects:object, ... 56 | // 57 | #define GHConvertVarArgs(object) \ 58 | NSMutableArray *arguments = [NSMutableArray array]; \ 59 | do { \ 60 | id arg; \ 61 | va_list args; \ 62 | if (object) { \ 63 | [arguments addObject:object]; \ 64 | va_start(args, object); \ 65 | while ((arg = va_arg(args, id))) \ 66 | [arguments addObject:arg]; \ 67 | va_end(args); \ 68 | } \ 69 | } while(0); 70 | 71 | /*! 72 | Macro defaults. 73 | */ 74 | #undef GHDebug 75 | #define GHDebug(fmt, ...) do {} while(0) 76 | #undef GHErr 77 | #define GHErr(fmt, ...) do {} while(0) 78 | 79 | /*! 80 | Logging macros. 81 | */ 82 | #if DEBUG 83 | #undef GHDebug 84 | #define GHDebug(fmt, ...) NSLog((@"%s:%d: " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 85 | #undef GHErr 86 | #define GHErr(fmt, ...) NSLog((@"%s:%d: " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 87 | #endif 88 | 89 | /*! 90 | Time. 91 | */ 92 | #define GHTimeIntervalMinute (60) 93 | #define GHTimeIntervalHour (GHTimeIntervalMinute * 60) 94 | #define GHTimeIntervalDay (GHTimeIntervalHour * 24) 95 | #define GHTimeIntervalWeek (GHTimeIntervalDay * 7) 96 | #define GHTimeIntervalYear (GHTimeIntervalDay * 365.242199) 97 | #define GHTimeIntervalMax (DBL_MAX) 98 | 99 | /*! 100 | For when you need a weak reference of an object, example: `GHWeakObject(obj) wobj = obj;` 101 | */ 102 | #define GHWeakObject(o) __typeof__(o) __weak 103 | 104 | /*! 105 | For when you need a weak reference to self, example: `GHWeakSelf wself = self;` 106 | */ 107 | #define GHWeakSelf GHWeakObject(self) 108 | 109 | typedef void (^GHTargetBlock)(id sender); 110 | 111 | #define GHNSError(CODE, MESSAGE) [NSError errorWithDomain:NSStringFromClass([self class]) code:CODE userInfo:@{NSLocalizedDescriptionKey:MESSAGE}] 112 | 113 | #define GHMakeError(CODE, fmt, ...) [NSError errorWithDomain:NSStringFromClass(self.class) code:CODE userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:fmt, ##__VA_ARGS__]}] 114 | 115 | #define GHOrNull(obj) (obj ? obj : NSNull.null) 116 | 117 | #define GHIfNull(obj, val) ([obj isEqual:NSNull.null] ? val : obj) 118 | 119 | #define GHEquals(obj1, obj2) ((!obj1 && !obj2) || [obj1 isEqual:obj2]) 120 | 121 | #define GHNSStringFromNSData(data) ([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]) 122 | #define GHNSDataFromNSString(str) ([str dataUsingEncoding:NSUTF8StringEncoding]) 123 | 124 | #define GHNSDataFromBase64String(str) ([[NSData alloc] initWithBase64EncodedString:str options:0]) 125 | #define GHBase64StringFromNSData(data) ([data base64EncodedStringWithOptions:0]) 126 | 127 | -------------------------------------------------------------------------------- /GHKit/GHNSArray+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSArray+Utils.h 3 | // Created by Gabriel Handford on 12/11/08. 4 | // 5 | // Permission is hereby granted, free of charge, to any person 6 | // obtaining a copy of this software and associated documentation 7 | // files (the "Software"), to deal in the Software without 8 | // restriction, including without limitation the rights to use, 9 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following 12 | // conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | // OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | /*! 30 | Utilities for arrays. 31 | */ 32 | @interface NSArray(GHUtils) 33 | 34 | /*! 35 | First object or nil if array is empty. 36 | 37 | @result Object at index 0 38 | */ 39 | - (id)gh_firstObject; 40 | 41 | /*! 42 | Random object in the array. 43 | Uses arc4random_uniform. 44 | 45 | @result Random object 46 | */ 47 | - (id)gh_randomObject; 48 | 49 | /*! 50 | Return new reversed array. 51 | Use reverseObjectEnumerator if you want to enumerate values in reverse. 52 | 53 | @result Reversed array 54 | */ 55 | - (NSArray *)gh_arrayByReversingArray; 56 | 57 | /*! 58 | Safe subarrayWithRange that checks range. 59 | If the length is out of bounds will return all elements from location to the end. 60 | If the location is out of bounds will return nil. 61 | 62 | @param range Range 63 | @result Sub-array 64 | */ 65 | - (NSArray *)gh_subarrayWithRange:(NSRange)range; 66 | 67 | /*! 68 | Get sub-array from location to end. 69 | 70 | @param location Index 71 | @result Sub-array 72 | */ 73 | - (NSArray *)gh_subarrayFromLocation:(NSInteger)location; 74 | 75 | /*! 76 | Remove all instances of NSNull. 77 | 78 | @result New array with NSNull instances removed. Returns self if no NSNull's were found. 79 | */ 80 | - (NSArray *)gh_compact; 81 | 82 | /*! 83 | Safe array with object. 84 | 85 | @param obj Object 86 | @result Array with object. Returns empty if obj is nil. 87 | */ 88 | + (NSArray *)gh_arrayWithObject:(id)obj; 89 | 90 | /*! 91 | Safe object at index. 92 | 93 | @param index Index 94 | @result Object at index, or nil if index < 0 or >= count 95 | */ 96 | - (id)gh_objectAtIndex:(NSInteger)index; 97 | 98 | /*! 99 | Safe object at index with default. 100 | 101 | @param index Index 102 | @param withDefault Default if not found 103 | @result Object at index, or default value if index < 0 or >= count 104 | */ 105 | - (id)gh_objectAtIndex:(NSInteger)index withDefault:(id)withDefault; 106 | 107 | /*! 108 | Filter array. 109 | @param filterBlock Filter block 110 | */ 111 | - (NSArray *)gh_filter:(BOOL(^)(id obj, NSInteger index))filterBlock; 112 | 113 | /*! 114 | Convert array to JSON string. 115 | */ 116 | - (NSString *)gh_toJSON:(NSJSONWritingOptions)options error:(NSError **)error; 117 | 118 | /*! 119 | Except last object. 120 | */ 121 | - (NSArray *)gh_exceptLast; 122 | 123 | /*! 124 | Uniqify (de-dupe) an array. 125 | */ 126 | - (NSArray *)gh_uniq; 127 | 128 | /*! 129 | New array with object removed. 130 | */ 131 | - (NSArray *)gh_arrayByRemovingObject:(id)obj; 132 | 133 | /*! 134 | Array from arrays. 135 | */ 136 | + (NSArray *)gh_arrayWithArrays:(NSArray *)arrays; 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /GHKit/GHNSArray+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSArray+Utils.m 3 | // Created by Gabriel Handford on 12/11/08. 4 | // 5 | // Permission is hereby granted, free of charge, to any person 6 | // obtaining a copy of this software and associated documentation 7 | // files (the "Software"), to deal in the Software without 8 | // restriction, including without limitation the rights to use, 9 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following 12 | // conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | // OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import "GHNSArray+Utils.h" 28 | 29 | @implementation NSArray(GHUtils) 30 | 31 | - (id)gh_firstObject { 32 | if ([self count] > 0) 33 | return [self objectAtIndex:0]; 34 | return nil; 35 | } 36 | 37 | + (NSArray *)gh_arrayWithObject:(id)obj { 38 | if (!obj) return [NSArray array]; 39 | return [NSArray arrayWithObject:obj]; 40 | } 41 | 42 | - (id)gh_objectAtIndex:(NSInteger)index { 43 | return [self gh_objectAtIndex:index withDefault:nil]; 44 | } 45 | 46 | - (id)gh_objectAtIndex:(NSInteger)index withDefault:(id)defaultValue { 47 | if (index >= 0 && index < [self count]) return [self objectAtIndex:index]; 48 | return defaultValue; 49 | } 50 | 51 | - (id)gh_randomObject { 52 | if ([self count] == 0) return nil; 53 | NSUInteger count = [self count]; 54 | NSAssert(count <= UINT32_MAX, @"Array size is greater than rand supports"); 55 | NSUInteger index = arc4random_uniform((int32_t)count); 56 | return [self objectAtIndex:index]; 57 | } 58 | 59 | - (NSArray *)gh_arrayByReversingArray { 60 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]]; 61 | for(id obj in [self reverseObjectEnumerator]) { 62 | [array addObject:obj]; 63 | } 64 | return array; 65 | } 66 | 67 | - (NSArray *)gh_subarrayWithRange:(NSRange)range { 68 | if (range.location >= [self count]) return nil; 69 | NSInteger length = range.length; 70 | if ((range.location + length) >= [self count]) length = [self count] - range.location; 71 | 72 | return [self subarrayWithRange:NSMakeRange(range.location, length)]; 73 | } 74 | 75 | - (NSArray *)gh_subarrayFromLocation:(NSInteger)location { 76 | if (location == 0) return self; 77 | if (location >= [self count]) return [NSArray array]; 78 | return [self subarrayWithRange:NSMakeRange(location, [self count]-location)]; 79 | } 80 | 81 | - (NSArray *)gh_compact { 82 | if ([self count] == 0) return self; 83 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:[self count]]; 84 | BOOL found = NO; 85 | for(id obj in self) { 86 | if (![obj isEqual:[NSNull null]]) [array addObject:obj]; 87 | else found = YES; 88 | } 89 | if (found) { 90 | return array; 91 | } else { 92 | // No NSNulls were found so release array copy and return self 93 | return self; 94 | } 95 | } 96 | 97 | - (NSArray *)gh_uniq { 98 | return [[NSOrderedSet orderedSetWithArray:self] array]; 99 | } 100 | 101 | - (NSArray *)gh_filter:(BOOL(^)(id obj, NSInteger index))filterBlock { 102 | id filteredArray = [NSMutableArray arrayWithCapacity:[self count]]; 103 | NSInteger i = 0; 104 | for (id obj in self) { 105 | if (filterBlock(obj, i)) { 106 | [filteredArray addObject:obj]; 107 | } 108 | i++; 109 | } 110 | return filteredArray; 111 | } 112 | 113 | - (NSString *)gh_toJSON:(NSJSONWritingOptions)options error:(NSError **)error { 114 | NSData *data = [NSJSONSerialization dataWithJSONObject:self options:options error:error]; 115 | if (data) return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 116 | return nil; 117 | } 118 | 119 | - (NSArray *)gh_exceptLast { 120 | if (self.count == 0) return self; 121 | if (self.count == 1) return @[]; 122 | return [self subarrayWithRange:NSMakeRange(0, self.count - 1)]; 123 | } 124 | 125 | - (NSArray *)gh_arrayByRemovingObject:(id)obj { 126 | NSMutableArray *array = [self mutableCopy]; 127 | [array removeObject:obj]; 128 | return array; 129 | } 130 | 131 | + (NSArray *)gh_arrayWithArrays:(NSArray *)arrays { 132 | NSMutableArray *array = [NSMutableArray array]; 133 | for (NSArray *a in arrays) { 134 | [array addObjectsFromArray:a]; 135 | } 136 | return array; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /GHKit/GHNSBundle+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSBundle+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 4/27/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Utilities for accessing bundles. 34 | */ 35 | @interface NSBundle(GHUtils) 36 | 37 | /*! 38 | Load data from resource. 39 | 40 | @param resource Name of resource, such as "file.txt" 41 | @result NSData 42 | */ 43 | - (NSData *)gh_loadDataFromResource:(NSString *)resource; 44 | 45 | /*! 46 | Load string data from resource. 47 | 48 | @param resource Name of resource 49 | @result NSString 50 | */ 51 | - (NSString *)gh_loadStringDataFromResource:(NSString *)resource; 52 | 53 | /*! 54 | Get URL for resource. 55 | 56 | @param resource Name of resource 57 | @result URL to resource 58 | */ 59 | - (NSURL *)gh_URLForResource:(NSString *)resource; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /GHKit/GHNSBundle+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSBundle+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 4/27/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHNSBundle+Utils.h" 31 | 32 | @implementation NSBundle(GHUtils) 33 | 34 | - (NSData *)gh_loadDataFromResource:(NSString *)resource { 35 | NSParameterAssert(resource); 36 | NSString *resourcePath = [self pathForResource:[resource stringByDeletingPathExtension] ofType:[resource pathExtension]]; 37 | if (!resourcePath) [NSException raise:NSInvalidArgumentException format:@"Resource not found: %@", resource]; 38 | NSError *error = nil; 39 | NSData *data = [NSData dataWithContentsOfFile:resourcePath options:0 error:&error]; 40 | if (error) [NSException raise:NSInvalidArgumentException format:@"Error loading resource at path (%@): %@", resourcePath, error]; 41 | return data; 42 | } 43 | 44 | - (NSString *)gh_loadStringDataFromResource:(NSString *)resource { 45 | return [[NSString alloc] initWithData:[self gh_loadDataFromResource:resource] encoding:NSUTF8StringEncoding]; 46 | } 47 | 48 | - (NSURL *)gh_URLForResource:(NSString *)resource { 49 | NSParameterAssert(resource); 50 | NSString *resourcePath = [self pathForResource:[resource stringByDeletingPathExtension] ofType:[resource pathExtension]]; 51 | return resourcePath ? [NSURL fileURLWithPath:resourcePath] : nil; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /GHKit/GHNSData+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSData+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/14/14. 6 | // Copyright (c) 2014 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (GHUtils) 12 | 13 | /*! 14 | Return hex string for this data. 15 | */ 16 | - (NSString *)gh_hexString; 17 | 18 | /*! 19 | Get IP address string from data object. 20 | */ 21 | - (NSString *)gh_IPAddressAsString; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /GHKit/GHNSData+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSData+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/14/14. 6 | // Copyright (c) 2014 rel.me. All rights reserved. 7 | // 8 | 9 | #import "GHNSData+Utils.h" 10 | #import 11 | 12 | @implementation NSData (GHUtils) 13 | 14 | - (NSString *)gh_hexString { 15 | if ([self length] == 0) return nil; 16 | NSMutableString *hex = [NSMutableString stringWithCapacity:[self length] * 2]; 17 | for (NSUInteger i = 0; i < [self length]; ++i) { 18 | [hex appendFormat:@"%02X", *((uint8_t *)[self bytes] + i)]; 19 | } 20 | return hex; 21 | } 22 | 23 | - (NSString *)gh_IPAddressAsString { 24 | struct sockaddr *addr = (struct sockaddr*)[self bytes]; 25 | if (addr->sa_family == AF_INET) { 26 | struct sockaddr_in *addr4 = (struct sockaddr_in*) addr; 27 | char addr4CString[INET_ADDRSTRLEN]; 28 | if (!inet_ntop(AF_INET, &addr4->sin_addr, addr4CString, INET_ADDRSTRLEN)) { 29 | return nil; 30 | } else { 31 | return [[NSString alloc] initWithUTF8String:addr4CString]; 32 | } 33 | } else if (addr->sa_family == AF_INET6) { 34 | struct sockaddr_in6* addr6 = (struct sockaddr_in6*) addr; 35 | char addr6CString[INET6_ADDRSTRLEN]; 36 | if (!inet_ntop(AF_INET6, &addr6->sin6_addr, addr6CString, INET6_ADDRSTRLEN)) { 37 | return nil; 38 | } else { 39 | return [[NSString alloc] initWithUTF8String:addr6CString]; 40 | } 41 | } else { 42 | return nil; 43 | } 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /GHKit/GHNSDate+Formatters.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSDate+Formatters.h 3 | // 4 | // Created by Gabe on 3/18/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Date parsers, formatting and formatters for ISO8601, RFC822, HTTP (RFC1123, RFC850, asctime) and since epoch. 33 | */ 34 | @interface NSDate(GHFormatters) 35 | 36 | /*! 37 | Parse ISO8601 date. For example, "2010-10-07T04:25Z". 38 | 39 | @param dateString Date string to parse, eg. "2010-10-07T04:25Z" 40 | @result Date 41 | */ 42 | + (NSDate *)gh_parseISO8601:(NSString *)dateString; 43 | 44 | /*! 45 | Parse RFC822 encoded date. For example, 'Wed, 01 Mar 2006 12:00:00 -0400'. 46 | 47 | @param dateString Date string to parse, eg. 'Wed, 01 Mar 2006 12:00:00 -0400' 48 | @result Date 49 | */ 50 | + (NSDate *)gh_parseRFC822:(NSString *)dateString; 51 | 52 | /*! 53 | Parse http date. Tries RFC1123, then RFC850, and then ASCTIME. 54 | 55 | HTTP-date = rfc1123-date | rfc850-date | asctime-date 56 | 57 | Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 58 | Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 59 | Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format 60 | 61 | @param dateString Date string to parse 62 | @result Date 63 | */ 64 | + (NSDate *)gh_parseHTTP:(NSString *)dateString; 65 | 66 | /*! 67 | Parse time since epoch (1970) in seconds. 68 | 69 | @param timeSinceEpoch Seconds since Jan 1970 (epoch); An NSNumber or NSString (responds to doubleValue). 70 | @result NSDate or nil if timeSinceEpoch was nil 71 | */ 72 | + (NSDate *)gh_parseTimeSinceEpoch:(id)timeSinceEpoch; 73 | 74 | /*! 75 | Parse time since epoch (1970) in seconds, with default. 76 | 77 | @param timeSinceEpoch Seconds since Jan 1970 (epoch); An NSNumber or NSString (responds to doubleValue) 78 | @param withDefault Default if timeSinceEpoch is nil 79 | @result NSDate or default 80 | */ 81 | + (NSDate *)gh_parseTimeSinceEpoch:(id)timeSinceEpoch withDefault:(id)withDefault; 82 | 83 | /*! 84 | Parse time since epoch (1970) in seconds. 85 | 86 | @param timeSinceEpoch Seconds since Jan 1970 (epoch); An NSNumber or NSString (responds to doubleValue) 87 | @param withDefault If timeSinceEpoch is nil, returns this value 88 | @param timeZone If set, the returned Date will be offset from the supplied timestamp by the difference between timeZone and the system time zone 89 | @result NSDate or nil if timeSinceEpoch was nil 90 | */ 91 | + (NSDate *)gh_parseTimeSinceEpoch:(id)timeSinceEpoch withDefault:(id)withDefault offsetForTimeZone:(NSTimeZone *)timeZone; 92 | 93 | /*! 94 | Get date formatted for RFC822. 95 | 96 | @result The date string, like "Wed, 01 Mar 2006 12:00:00 -0400" 97 | */ 98 | - (NSString *)gh_formatRFC822; 99 | 100 | /*! 101 | Get date formatted for RFC1123 (HTTP date). 102 | 103 | @result The date string, like "Sun, 06 Nov 1994 08:49:37 GMT" 104 | */ 105 | - (NSString *)gh_formatHTTP; 106 | 107 | /*! 108 | Get date formatted for ISO8601 (XML date). 109 | 110 | @result The date string, like "2010-10-07T04:25Z" 111 | */ 112 | - (NSString *)gh_formatISO8601; 113 | 114 | /*! 115 | Date formatter for ISO8601. For example, '2007-10-18T16:05:10.000Z'. 116 | If this is called from the main thread, a cached date formatter is returned. 117 | 118 | @result Date formatter for ISO8601 119 | */ 120 | + (NSArray *)gh_ISO8601DateFormatters; 121 | 122 | /*! 123 | Date formatter for RFC822. For example, 'Wed, 01 Mar 2006 12:00:00 -0400'. 124 | If this is called from the main thread, a cached date formatter is returned. 125 | 126 | @result Date formatter for RFC822 127 | */ 128 | + (NSDateFormatter *)gh_RFC822DateFormatter; 129 | 130 | /*! 131 | Date formatter for RFC1123. For example, 'Wed, 01 Mar 2006 12:00:00 GMT'. 132 | If this is called from the main thread, a cached date formatter is returned. 133 | 134 | @result Date formatter for RFC1123 135 | */ 136 | + (NSDateFormatter *)gh_RFC1123DateFormatter; 137 | 138 | /*! 139 | Date formatter for RFC850. For example, 'Sunday, 06-Nov-94 08:49:37 GMT'. 140 | If this is called from the main thread, a cached date formatter is returned. 141 | 142 | @result Date formatter for RFC850 143 | */ 144 | + (NSDateFormatter *)gh_RFC850DateFormatter; 145 | 146 | /*! 147 | Date formatter for asctime. For example, 'Sun Nov 6 08:49:37 1994'. 148 | If this is called from the main thread, a cached date formatter is returned. 149 | 150 | @result Date formatter for asctime 151 | */ 152 | + (NSDateFormatter *)gh_ascTimeDateFormatter; 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /GHKit/GHNSDate+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSDate+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 2/18/09. 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHNSDate+Utils.h" 30 | #import "GHNSString+TimeInterval.h" 31 | 32 | // Common date formats 33 | NSString *const kDateFormatShortMonthFullYearTime = @"LLL d, yyyy hh:mm a"; 34 | 35 | @implementation NSDate(GHUtils) 36 | 37 | NSUInteger const kUnitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday; 38 | 39 | + (NSDate *)_gh_dateFromDate:(NSDate *)date day:(NSInteger)day month:(NSInteger)month year:(NSInteger)year 40 | addDay:(NSInteger)addDay addMonth:(NSInteger)addMonth addYear:(NSInteger)addYear 41 | normalize:(BOOL)normalize timeZone:(NSTimeZone *)timeZone { 42 | NSCalendar *calendar = [NSCalendar currentCalendar]; 43 | if (timeZone) { 44 | [calendar setTimeZone:timeZone]; 45 | } 46 | NSDateComponents *comps = [calendar components:kUnitFlags fromDate:date]; 47 | if (normalize) { 48 | [comps setHour:0]; 49 | [comps setMinute:0]; 50 | [comps setSecond:0]; 51 | } 52 | if (day != 0) [comps setDay:day]; 53 | if (month != 0) [comps setMonth:month]; 54 | if (year != 0) [comps setYear:year]; 55 | 56 | if (addDay != 0) [comps setDay:([comps day] + addDay)]; 57 | if (addMonth != 0) [comps setMonth:([comps month] + addMonth)]; 58 | if (addYear != 0) [comps setYear:([comps year] + addYear)]; 59 | 60 | return [calendar dateFromComponents:comps]; 61 | } 62 | 63 | + (NSDate *)gh_dateFromDate:(NSDate *)date addDay:(NSInteger)addDay normalize:(BOOL)normalize { 64 | return [self _gh_dateFromDate:date day:0 month:0 year:0 addDay:addDay addMonth:0 addYear:0 65 | normalize:normalize timeZone:nil]; 66 | } 67 | 68 | + (NSDate *)gh_dateWithDay:(NSInteger)day month:(NSInteger)month year:(NSInteger)year 69 | timeZone:(NSTimeZone *)timeZone { 70 | return [self gh_dateWithDay:day month:month year:year addDay:0 addMonth:0 addYear:0 timeZone:timeZone]; 71 | } 72 | 73 | + (NSDate *)gh_dateWithDay:(NSInteger)day month:(NSInteger)month year:(NSInteger)year 74 | addDay:(NSInteger)addDay addMonth:(NSInteger)addMonth addYear:(NSInteger)addYear 75 | timeZone:(NSTimeZone *)timeZone { 76 | return [self _gh_dateFromDate:[NSDate date] day:day month:month year:year addDay:addDay addMonth:addMonth addYear:addYear 77 | normalize:YES timeZone:timeZone]; 78 | 79 | } 80 | 81 | - (NSDateComponents *)gh_dateComponentsFromFlags:(NSUInteger)flags timeZone:(NSTimeZone *)timeZone { 82 | NSCalendar *calendar = [NSCalendar currentCalendar]; 83 | if (timeZone) { 84 | [calendar setTimeZone:timeZone]; 85 | } 86 | return [calendar components:flags fromDate:self]; 87 | } 88 | 89 | - (NSInteger)gh_day { 90 | return [self gh_dayForTimeZone:nil]; 91 | } 92 | 93 | - (NSInteger)gh_dayForTimeZone:(NSTimeZone *)timeZone { 94 | return [[self gh_dateComponentsFromFlags:NSCalendarUnitDay timeZone:timeZone] day]; 95 | } 96 | 97 | - (NSInteger)gh_month { 98 | return [self gh_monthForTimeZone:nil]; 99 | } 100 | 101 | - (NSInteger)gh_monthForTimeZone:(NSTimeZone *)timeZone { 102 | return [[self gh_dateComponentsFromFlags:NSCalendarUnitMonth timeZone:timeZone] month]; 103 | } 104 | 105 | - (NSInteger)gh_year { 106 | return [self gh_yearForTimeZone:nil]; 107 | } 108 | 109 | - (NSInteger)gh_yearForTimeZone:(NSTimeZone *)timeZone { 110 | return [[self gh_dateComponentsFromFlags:NSCalendarUnitYear timeZone:timeZone] year]; 111 | } 112 | 113 | + (NSArray *)gh_monthSymbols { 114 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 115 | NSArray *monthSymbols = [dateFormatter standaloneMonthSymbols]; 116 | return monthSymbols; 117 | } 118 | 119 | - (NSDate *)gh_addDays:(NSInteger)days { 120 | NSCalendar *calendar = [NSCalendar currentCalendar]; 121 | NSDateComponents *comps = [calendar components:kUnitFlags fromDate:self]; 122 | if (days != 0) [comps setDay:[comps day] + days]; 123 | return [calendar dateFromComponents:comps]; 124 | } 125 | 126 | - (NSDate *)gh_beginningOfDay { 127 | return [NSDate gh_dateFromDate:self addDay:0 normalize:YES]; 128 | } 129 | 130 | + (NSDate *)gh_yesterday { 131 | return [NSDate gh_dateFromDate:[NSDate date] addDay:-1 normalize:YES]; 132 | } 133 | 134 | + (NSDate *)gh_tomorrow { 135 | return [NSDate gh_dateFromDate:[NSDate date] addDay:1 normalize:YES]; 136 | } 137 | 138 | - (BOOL)gh_isTomorrow { 139 | return ([[self gh_beginningOfDay] compare:[NSDate gh_tomorrow]] == NSOrderedSame); 140 | } 141 | 142 | - (BOOL)gh_isToday { 143 | return ([[self gh_beginningOfDay] compare:[[NSDate date] gh_beginningOfDay]] == NSOrderedSame); 144 | } 145 | 146 | - (BOOL)gh_wasYesterday { 147 | return ([[self gh_beginningOfDay] compare:[NSDate gh_yesterday]] == NSOrderedSame); 148 | } 149 | 150 | - (NSString *)gh_weekday:(NSDateFormatter *)formatter { 151 | if ([self gh_isTomorrow]) return NSLocalizedString(@"Tomorrow", nil); 152 | else if ([self gh_isToday]) return NSLocalizedString(@"Today", nil); 153 | else if ([self gh_wasYesterday]) return NSLocalizedString(@"Yesterday", nil); 154 | 155 | if (!formatter) return nil; 156 | 157 | NSInteger weekday = [[[NSCalendar currentCalendar] components:kUnitFlags fromDate:self] weekday] - 1; 158 | return [[formatter weekdaySymbols] objectAtIndex:weekday]; 159 | } 160 | 161 | - (NSString *)gh_format:(NSString *)format useWeekday:(BOOL)useWeekday { 162 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 163 | [dateFormatter setDateFormat:format]; 164 | NSString *formatted = [dateFormatter stringFromDate:self]; 165 | if (useWeekday) { 166 | NSString *specialWeekday = [self gh_weekday:dateFormatter]; 167 | formatted = [NSString stringWithFormat:@"%@, %@", specialWeekday, formatted]; 168 | } 169 | return formatted; 170 | } 171 | 172 | - (NSString *)gh_timeAgo:(BOOL)includeSeconds { 173 | return [NSString gh_stringForTimeInterval:[self timeIntervalSinceNow] includeSeconds:includeSeconds]; 174 | } 175 | 176 | - (NSString *)gh_timeAgoAbbreviated { 177 | return [NSString gh_abbreviatedStringForTimeInterval:[self timeIntervalSinceNow]]; 178 | } 179 | 180 | @end 181 | -------------------------------------------------------------------------------- /GHKit/GHNSDictionary+NSNull.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSDictionary+NSNull.h 3 | // Created by Jae Kwon on 5/12/08. 4 | // 5 | // Permission is hereby granted, free of charge, to any person 6 | // obtaining a copy of this software and associated documentation 7 | // files (the "Software"), to deal in the Software without 8 | // restriction, including without limitation the rights to use, 9 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following 12 | // conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | // OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | /*! 30 | For supporting dictionaries with nil values. 31 | */ 32 | @interface NSDictionary(GHNSNull) 33 | 34 | /*! 35 | Create dictionary which supports nil values. 36 | Key is first (instead of value then key). If the value is nil it is stored internally as NSNull, 37 | and when calling objectMaybeNilForKey will return nil. 38 | 39 | For example, 40 | 41 | [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:@"key1", nil, @"key2", @"value2", @"key3", nil, nil]; 42 | 43 | @param firstObject Alternating key, value pairs. Terminated when _key_ is nil. 44 | @param ... Keys and objects 45 | @result Dictionary 46 | */ 47 | + (id)gh_dictionaryWithKeysAndObjectsMaybeNil:(id)firstObject, ... NS_REQUIRES_NIL_TERMINATION; 48 | 49 | /*! 50 | Create dictionary which supports nil values. 51 | Key is first (instead of value then key). If the value is nil it is stored internally as NSNull, 52 | and when calling objectMaybeNilForKey will return nil. 53 | 54 | For example, 55 | 56 | - (void)setDictionary:(id)key, ... { 57 | va_list args; 58 | va_start(args, key); 59 | [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNilWithKey:key args:args ignoreNilValues:NO]; 60 | va_end(args); 61 | } 62 | 63 | @param firstKey First key 64 | @param args Args va_list via va_start 65 | @param ignoreNil If YES, will not store key, value paries where the value is nil 66 | @result Dictionary 67 | */ 68 | + (id)gh_dictionaryWithKeysAndObjectsMaybeNilWithKey:(id)firstKey args:(va_list)args ignoreNil:(BOOL)ignoreNil; 69 | 70 | /*! 71 | Create dictionary which supports nil values. 72 | Key is first (instead of value then key). If the value is nil it is stored internally as NSNull, 73 | and when calling objectMaybeNilForKey will return nil. 74 | 75 | For example, 76 | 77 | [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:@"key1", nil, @"key2", @"value2", @"key3", nil, nil]; 78 | 79 | @param firstObject... Alternating key, value pairs. Terminated when _key_ is nil. 80 | */ 81 | 82 | /*! 83 | Returns objectForKey if you want nil instead of NSNull objects. 84 | 85 | @param key Key 86 | @result Object for key. If object is NSNull, then nil is returned. 87 | */ 88 | - (id)gh_objectMaybeNilForKey:(id)key; 89 | 90 | /*! 91 | Same as gh_objectMaybeNilForKey, but checks the class type and if a mismatch returns nil. 92 | */ 93 | - (id)gh_objectMaybeNilForKey:(id)key ofClass:(Class)aClass; 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /GHKit/GHNSDictionary+NSNull.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSDictionary+NSNull.m 3 | // Created by Jae Kwon on 5/12/08. 4 | // 5 | // Permission is hereby granted, free of charge, to any person 6 | // obtaining a copy of this software and associated documentation 7 | // files (the "Software"), to deal in the Software without 8 | // restriction, including without limitation the rights to use, 9 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following 12 | // conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be 15 | // included in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | // OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #import "GHNSDictionary+NSNull.h" 28 | 29 | @implementation NSDictionary(GHNSNull) 30 | 31 | + (id)gh_dictionaryWithKeysAndObjectsMaybeNilWithKey:(id)firstKey args:(va_list)args ignoreNil:(BOOL)ignoreNil { 32 | if (!firstKey) return [self dictionary]; 33 | 34 | NSMutableArray *keys = [[NSMutableArray alloc] init]; 35 | NSMutableArray *values = [[NSMutableArray alloc] init]; 36 | id key = firstKey; 37 | do { 38 | id value = va_arg(args, id); 39 | if (!value && ignoreNil) { 40 | key = va_arg(args, id); 41 | continue; 42 | } 43 | if (!value) value = [NSNull null]; 44 | [keys addObject:key]; 45 | [values addObject:value]; 46 | key = va_arg(args, id); 47 | } while(key); 48 | NSDictionary *dict = [self dictionaryWithObjects:values forKeys:keys]; 49 | return dict; 50 | } 51 | 52 | + (id)gh_dictionaryWithKeysAndObjectsMaybeNil:(id)firstKey, ... { 53 | va_list args; 54 | va_start(args, firstKey); 55 | NSDictionary *dict = [self gh_dictionaryWithKeysAndObjectsMaybeNilWithKey:firstKey args:args ignoreNil:NO]; 56 | va_end(args); 57 | return dict; 58 | } 59 | 60 | - (id)gh_objectMaybeNilForKey:(id)key { 61 | id object = [self objectForKey:key]; 62 | if (object == [NSNull null] || [object isEqual:[NSNull null]]) 63 | return nil; 64 | 65 | return object; 66 | } 67 | 68 | - (id)gh_objectMaybeNilForKey:(id)key ofClass:(Class)aClass { 69 | id obj = [self gh_objectMaybeNilForKey:key]; 70 | if (![obj isKindOfClass:aClass]) return nil; 71 | return obj; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /GHKit/GHNSDictionary+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSDictionary+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 3/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Utilities for dictionaries. 34 | */ 35 | @interface NSDictionary(GHUtils) 36 | 37 | /*! 38 | Get double value for key. 39 | 40 | @param key Key 41 | @param withDefault If value for key is nil or [NSNull null] this default is returned. 42 | @result Double value 43 | */ 44 | - (double)gh_doubleForKey:(id)key withDefault:(double)withDefault; 45 | 46 | /*! 47 | Get double value for key. 48 | 49 | @param key Key 50 | @result Double value 51 | */ 52 | - (double)gh_doubleForKey:(id)key; 53 | 54 | /*! 55 | Integer for key. 56 | 57 | @param key Key 58 | @param withDefault If value for key is nil or [NSNull null] this default is returned. 59 | @result Integer value 60 | */ 61 | - (NSInteger)gh_integerForKey:(id)key withDefault:(NSInteger)withDefault; 62 | 63 | /*! 64 | Integer for key. 65 | If value for key is nil or [NSNull null], 0 is returned. 66 | 67 | @param key Key 68 | @result Integer value 69 | */ 70 | - (NSInteger)gh_integerForKey:(id)key; 71 | 72 | /*! 73 | Unsigned integer for key. 74 | 75 | @param key Key 76 | @param withDefault If value for key is nil or [NSNull null] this default is returned. 77 | @result Unsigned integer 78 | */ 79 | - (NSUInteger)gh_unsignedIntegerForKey:(id)key withDefault:(NSUInteger)withDefault; 80 | 81 | /*! 82 | Unsigned integer for key. 83 | If value for key is nil or [NSNull null], 0 is returned. 84 | 85 | @param key Key 86 | @result Unsigned integer 87 | */ 88 | - (NSUInteger)gh_unsignedIntegerForKey:(id)key; 89 | 90 | /*! 91 | Number for key. 92 | @param key 93 | @result Number 94 | */ 95 | - (NSNumber *)gh_numberForKey:(id)key; 96 | 97 | /*! 98 | Number for key with default integer value. 99 | 100 | @param key Key 101 | @param withDefaultInteger If value for key is nil or [NSNull null] this default is returned. 102 | @result Number 103 | */ 104 | - (NSNumber *)gh_numberForKey:(id)key withDefaultInteger:(NSInteger)withDefaultInteger; 105 | 106 | /*! 107 | Number for key with default double value. 108 | 109 | @param key Key 110 | @param withDefaultDouble If value for key is nil or [NSNull null] this default is returned. 111 | @result Number 112 | */ 113 | - (NSNumber *)gh_numberForKey:(id)key withDefaultDouble:(double)withDefaultDouble; 114 | 115 | /*! 116 | Get BOOL value for key. 117 | 118 | @param key Key 119 | @param withDefault If value for key is nil or [NSNull null] this default is returned. 120 | @result BOOL value 121 | */ 122 | - (BOOL)gh_boolForKey:(id)key withDefault:(BOOL)withDefault; 123 | 124 | /*! 125 | Get BOOL value for key. 126 | 127 | @param key Key 128 | @result YES if boolValue; If key not found or is NSNull, returns NO. 129 | */ 130 | - (BOOL)gh_boolForKey:(id)key; 131 | 132 | /*! 133 | Get date for key. Value should respond to doubleValue, which should be seconds since epoch GMT. 134 | @param key 135 | */ 136 | - (NSDate *)gh_dateForKey:(id)key; 137 | 138 | /*! 139 | Get date for key. Value should respond to doubleValue, which should be seconds since epoch GMT. 140 | @param key 141 | @param withDefaultDate 142 | */ 143 | - (NSDate *)gh_dateForKey:(id)key withDefaultDate:(NSDate *)withDefaultDate; 144 | 145 | /*! 146 | Object for key with default value, if entry is NSNull or entry does not exist. 147 | 148 | @param key Key 149 | @param withDefault If value for key is nil or [NSNull null] this default is returned. 150 | @result Value for key with default value, if entry is NSNull or entry does not exist 151 | */ 152 | - (id)gh_objectForKey:(id)key withDefault:(id)withDefault; 153 | 154 | /*! 155 | Object for key or NSNull. 156 | Helpful when used with key/value coding. 157 | 158 | @param key Key 159 | @result Value or NSNull#null if not set 160 | */ 161 | - (id)gh_objectForKeyOrNSNull:(id)key; 162 | 163 | /*! 164 | Get boolean value (represented by NSNumber). 165 | Usefuly for coercing any object that responds to boolValue into an object for use with Key Value coding. 166 | 167 | @param key Key 168 | @param withDefault Default if value is missing or [NSNull null] 169 | @result Number 170 | */ 171 | - (NSNumber *)gh_boolValueForKey:(id)key withDefault:(BOOL)withDefault; 172 | 173 | /*! 174 | Get boolean value (represented by NSNumber). 175 | Usefuly for coercing any object that responds to boolValue into an object for use with Key Value coding. 176 | 177 | @param key Key 178 | @result Number 179 | */ 180 | - (NSNumber *)gh_boolValueForKey:(id)key; 181 | 182 | /*! 183 | Get data from base64 string. 184 | @result NSData 185 | */ 186 | - (NSData *)gh_dataAsBase64ForKey:(NSString *)key options:(NSDataBase64DecodingOptions)options; 187 | 188 | /*! 189 | Check if dictionary has all keys. 190 | 191 | @param firstKey First key 192 | @param ... Argument list 193 | @result YES if dictionary has all the keys 194 | */ 195 | - (BOOL)gh_hasAllKeys:(NSString *)firstKey, ... NS_REQUIRES_NIL_TERMINATION; 196 | 197 | /*! 198 | Return dictionary with subset of keys. 199 | 200 | @param keys Keys 201 | */ 202 | - (NSDictionary *)gh_dictionarySubsetWithKeys:(NSArray *)keys; 203 | 204 | /*! 205 | @result Dictionary without entries for keys with NSNull values 206 | */ 207 | - (NSDictionary *)gh_compactDictionary; 208 | 209 | /*! 210 | Query string from dictionary. 211 | 212 | Calls [NSURL gh_dictionaryToQueryString:self]; 213 | */ 214 | - (NSString *)gh_queryString; 215 | 216 | /*! 217 | Create JSON string for dictionary. 218 | */ 219 | - (NSString *)gh_toJSON:(NSJSONWritingOptions)options error:(NSError **)error; 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /GHKit/GHNSDictionary+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSDictionary+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 3/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHNSDictionary+Utils.h" 31 | #import "GHNSDate+Formatters.h" 32 | #import "GHNSURL+Utils.h" 33 | 34 | @implementation NSDictionary(GHUtils) 35 | 36 | - (double)gh_doubleForKey:(id)key withDefault:(double)defaultValue { 37 | id value = [self objectForKey:key]; 38 | if (!value || [value isEqual:[NSNull null]]) return defaultValue; 39 | return [value doubleValue]; 40 | } 41 | 42 | - (double)gh_doubleForKey:(id)key { 43 | return [self gh_doubleForKey:key withDefault:0]; 44 | } 45 | 46 | - (NSInteger)gh_integerForKey:(id)key withDefault:(NSInteger)defaultValue { 47 | id value = [self objectForKey:key]; 48 | if (!value || [value isEqual:[NSNull null]]) return defaultValue; 49 | return [value integerValue]; 50 | } 51 | 52 | - (NSInteger)gh_integerForKey:(id)key { 53 | return [self gh_integerForKey:key withDefault:0]; 54 | } 55 | 56 | - (NSUInteger)gh_unsignedIntegerForKey:(id)key withDefault:(NSUInteger)defaultValue { 57 | id value = [self objectForKey:key]; 58 | if (!value || [value isEqual:[NSNull null]]) return defaultValue; 59 | return [value unsignedIntegerValue]; 60 | } 61 | 62 | - (NSUInteger)gh_unsignedIntegerForKey:(id)key { 63 | return [self gh_unsignedIntegerForKey:key withDefault:0]; 64 | } 65 | 66 | - (NSNumber *)gh_numberForKey:(id)key { 67 | id value = [self objectForKey:key]; 68 | if (!value || [value isEqual:[NSNull null]]) return nil; 69 | NSAssert([value isKindOfClass:[NSNumber class]], @"Value must be a NSNumber"); 70 | return value; 71 | } 72 | 73 | - (NSNumber *)gh_numberForKey:(id)key withDefaultInteger:(NSInteger)defaultValue { 74 | id value = [self objectForKey:key]; 75 | if (!value || [value isEqual:[NSNull null]]) return [NSNumber numberWithInteger:defaultValue]; 76 | NSAssert([value isKindOfClass:[NSNumber class]], @"Value must be a NSNumber"); 77 | return value; 78 | } 79 | 80 | - (NSNumber *)gh_numberForKey:(id)key withDefaultDouble:(double)defaultValue { 81 | id value = [self objectForKey:key]; 82 | if (!value || [value isEqual:[NSNull null]]) return [NSNumber numberWithDouble:defaultValue]; 83 | NSAssert([value isKindOfClass:[NSNumber class]], @"Value must be a NSNumber"); 84 | return value; 85 | } 86 | 87 | - (NSDate *)gh_dateForKey:(id)key { 88 | return [self gh_dateForKey:key withDefaultDate:nil]; 89 | } 90 | 91 | - (NSDate *)gh_dateForKey:(id)key withDefaultDate:(NSDate *)defaultValue { 92 | id value = [self objectForKey:key]; 93 | if (!value || [value isEqual:[NSNull null]]) return defaultValue; 94 | return [NSDate gh_parseTimeSinceEpoch:value]; 95 | } 96 | 97 | - (BOOL)gh_boolForKey:(id)key withDefault:(BOOL)defaultValue { 98 | id value = [self objectForKey:key]; 99 | if (!value || [value isEqual:[NSNull null]]) return defaultValue; 100 | return [value boolValue]; 101 | } 102 | 103 | - (BOOL)gh_boolForKey:(id)key { 104 | return [self gh_boolForKey:key withDefault:NO]; 105 | } 106 | 107 | - (NSNumber *)gh_boolValueForKey:(id)key withDefault:(BOOL)defaultValue { 108 | id value = [self objectForKey:key]; 109 | if (!value || [value isEqual:[NSNull null]]) return [NSNumber numberWithBool:defaultValue]; 110 | return [NSNumber numberWithBool:[value boolValue]]; 111 | } 112 | 113 | - (NSNumber *)gh_boolValueForKey:(id)key { 114 | return [self gh_boolValueForKey:key withDefault:NO]; 115 | } 116 | 117 | - (NSData *)gh_dataAsBase64ForKey:(NSString *)key options:(NSDataBase64DecodingOptions)options { 118 | id value = [self objectForKey:key]; 119 | if (!value || [value isEqual:[NSNull null]]) return nil; 120 | NSAssert([value isKindOfClass:[NSString class]], @"Value must be a Base64 string"); 121 | return [[NSData alloc] initWithBase64EncodedString:value options:0]; 122 | } 123 | 124 | - (id)gh_objectForKey:(id)key withDefault:(id)defaultValue { 125 | id value = [self objectForKey:key]; 126 | if (!value || [value isEqual:[NSNull null]]) return defaultValue; 127 | return value; 128 | } 129 | 130 | - (id)gh_objectForKeyOrNSNull:(id)key { 131 | return [self gh_objectForKey:key withDefault:[NSNull null]]; 132 | } 133 | 134 | - (BOOL)gh_hasAllKeys:(NSString *)firstKey, ... { 135 | va_list vl; 136 | va_start(vl, firstKey); 137 | id key = firstKey; 138 | while(key != nil) { 139 | id value = [self objectForKey:key]; 140 | if (!value || [value isEqual:[NSNull null]]) return NO; 141 | key = va_arg(vl, NSString *); 142 | } 143 | va_end(vl); 144 | return YES; 145 | } 146 | 147 | - (NSDictionary *)gh_dictionarySubsetWithKeys:(NSArray *)keys { 148 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[keys count]]; 149 | for(id key in keys) { 150 | id value = [self objectForKey:key]; 151 | if (value) [dict setObject:value forKey:key]; 152 | } 153 | return dict; 154 | } 155 | 156 | - (NSDictionary *)gh_compactDictionary { 157 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:[self count]]; 158 | for (id key in self) { 159 | id obj = [self objectForKey:key]; 160 | if (obj != [NSNull null]) 161 | [dict setObject:obj forKey:key]; 162 | } 163 | return dict; 164 | } 165 | 166 | - (NSString *)gh_queryString { 167 | return [NSURL gh_dictionaryToQueryString:self]; 168 | } 169 | 170 | - (NSString *)gh_toJSON:(NSJSONWritingOptions)options error:(NSError **)error { 171 | NSData *data = [NSJSONSerialization dataWithJSONObject:self options:options error:error]; 172 | if (data) return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 173 | return nil; 174 | } 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /GHKit/GHNSError+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSError+Utils.h 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 3/9/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Utilities for generating errors. 34 | */ 35 | @interface NSError(GHUtils) 36 | 37 | /*! 38 | Create error with localized description. (userInfo includes NSLocalizedDescriptionKey=localizedDescription) 39 | 40 | @param domain Domain 41 | @param code Code 42 | @param localizedDescription Localized description 43 | @result NSError 44 | */ 45 | + (NSError *)gh_errorWithDomain:(NSString *)domain code:(NSInteger)code localizedDescription:(NSString *)localizedDescription; 46 | 47 | /*! 48 | Create error from exception. 49 | 50 | @param exception Exception 51 | */ 52 | + (NSError *)gh_errorFromException:(NSException *)exception; 53 | 54 | /*! 55 | Get full error description, recusively for any errors within the userInfo. 56 | Useful for getting at CoreData validation errors, for example. 57 | 58 | @result Full error description 59 | */ 60 | - (NSString *)gh_fullDescription; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /GHKit/GHNSError+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSError+Utils.m 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 3/9/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHNSError+Utils.h" 31 | 32 | 33 | @implementation NSError(GHUtils) 34 | 35 | + (NSError *)gh_errorWithDomain:(NSString *)domain code:(NSInteger)code localizedDescription:(NSString *)localizedDescription { 36 | NSDictionary *userInfo = [NSDictionary dictionaryWithObject:localizedDescription forKey:NSLocalizedDescriptionKey]; 37 | return [self errorWithDomain:domain code:code userInfo:userInfo]; 38 | } 39 | 40 | + (NSError *)gh_errorFromException:(NSException *)exception { 41 | NSDictionary *userInfo = [NSDictionary dictionaryWithObject:exception.reason forKey:NSLocalizedDescriptionKey]; 42 | return [self errorWithDomain:exception.name code:-1 userInfo:userInfo]; 43 | } 44 | 45 | - (void)gh_fullDescription:(NSMutableString *)errorDescription level:(NSInteger)level { 46 | [errorDescription appendFormat:@"%@\n", [self localizedDescription]]; 47 | 48 | if (level > 0) [errorDescription appendString:@"--\n"]; 49 | level++; 50 | for(id userInfoKey in [self userInfo]) { 51 | id userInfoEntry = [[self userInfo] objectForKey:userInfoKey]; 52 | if (userInfoEntry == self) continue; 53 | [errorDescription appendFormat:@" %@: ", userInfoKey]; 54 | if ([userInfoEntry isKindOfClass:[NSArray class]]) { 55 | for(id userInfoEntryItem in userInfoEntry) { 56 | if ([userInfoEntryItem isKindOfClass:[NSError class]]) { 57 | [(NSError *)userInfoEntryItem gh_fullDescription:errorDescription level:level]; 58 | } else { 59 | [errorDescription appendFormat:@"%@\n", [userInfoEntryItem description]]; 60 | } 61 | } 62 | } else if ([userInfoEntry isKindOfClass:[NSError class]]) { 63 | [(NSError *)userInfoEntry gh_fullDescription:errorDescription level:level]; 64 | } else { 65 | [errorDescription appendString:[userInfoEntry description]]; 66 | } 67 | } 68 | } 69 | 70 | - (NSString *)gh_fullDescription { 71 | NSMutableString *errorDescription = [[NSMutableString alloc] init]; 72 | [self gh_fullDescription:errorDescription level:0]; 73 | return errorDescription; 74 | } 75 | 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /GHKit/GHNSFileManager+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSFileManager+Utils.h 3 | // 4 | // Created by Gabe on 3/23/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Utilities for NSFileManager, for example, file size, exists, generating temporary or unique 33 | file paths. 34 | */ 35 | @interface NSFileManager(GHUtils) 36 | 37 | /*! 38 | Get size of file. 39 | 40 | @param filePath Path 41 | @param error Out error 42 | @result File size 43 | */ 44 | + (NSNumber *)gh_fileSize:(NSString *)filePath error:(NSError **)error; 45 | 46 | /*! 47 | Check if path is a directory. 48 | 49 | @param filePath Path 50 | @result YES if directory, NO otherwise 51 | */ 52 | + (BOOL)gh_isDirectory:(NSString *)filePath; 53 | 54 | /*! 55 | Check if path exists. 56 | 57 | @param filePath Path 58 | @result YES if exists, NO otherwise 59 | */ 60 | + (BOOL)gh_exist:(NSString *)filePath; 61 | 62 | /*! 63 | Get path to temporary file. 64 | 65 | @param appendPath Path to append to temporary directory name, if not nil 66 | @param deleteIfExists Will delete existing file if it is in the way 67 | @param error If not nil, will be set if an error occurs 68 | @result Path for temporary file 69 | */ 70 | + (NSString *)gh_temporaryFile:(NSString *)appendPath deleteIfExists:(BOOL)deleteIfExists error:(NSError **)error; 71 | 72 | /*! 73 | Get unique filename based on the specified path. 74 | If file does not exist, the same object is returned. 75 | If file does exist a unique variation is returned. 76 | 77 | @param path Path 78 | @result If, for example, foo.txt already exists, will return foo-1.txt, and if that exists foo-2.txt, and so on. 79 | */ 80 | + (NSString *)gh_uniquePathWithNumber:(NSString *)path; 81 | 82 | /*! 83 | Ensure directory exists. 84 | 85 | @param directory Directory 86 | @param created Set if was created 87 | @param error If not nil, will set if error occurs 88 | @result YES If directory exists or was created 89 | */ 90 | + (BOOL)gh_ensureDirectoryExists:(NSString *)directory created:(BOOL *)created error:(NSError **)error; 91 | 92 | /*! 93 | Path to resource in main bundle. 94 | 95 | @param path Path 96 | @result Full path in resource in main bundle. 97 | */ 98 | + (NSString *)gh_pathToResource:(NSString *)path; 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /GHKit/GHNSFileManager+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSFileManager+Utils.m 3 | // 4 | // Created by Gabe on 3/23/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHNSFileManager+Utils.h" 30 | #import "GHNSString+Utils.h" 31 | #import "GHNSError+Utils.h" 32 | 33 | @implementation NSFileManager(GHUtils) 34 | 35 | + (NSNumber *)gh_fileSize:(NSString *)filePath error:(NSError **)error { 36 | NSFileManager *fileManager = [NSFileManager defaultManager]; 37 | BOOL isDir; 38 | if ([fileManager fileExistsAtPath:filePath isDirectory:&isDir] && !isDir) { 39 | NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:error]; 40 | if (fileAttributes) 41 | return [fileAttributes objectForKey:NSFileSize]; 42 | } 43 | return nil; 44 | } 45 | 46 | + (BOOL)gh_isDirectory:(NSString *)filePath { 47 | BOOL isDir; 48 | return ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDir] && isDir); 49 | } 50 | 51 | + (BOOL)gh_exist:(NSString *)filePath { 52 | return [[NSFileManager defaultManager] fileExistsAtPath:filePath]; 53 | } 54 | 55 | + (NSString *)gh_temporaryFile:(NSString *)appendPath deleteIfExists:(BOOL)deleteIfExists error:(NSError **)error { 56 | NSParameterAssert(appendPath); 57 | NSString *tmpFile = NSTemporaryDirectory(); 58 | if (appendPath) tmpFile = [tmpFile stringByAppendingPathComponent:appendPath]; 59 | if (deleteIfExists && [self gh_exist:tmpFile]) { 60 | [[NSFileManager defaultManager] removeItemAtPath:tmpFile error:error]; 61 | } 62 | return tmpFile; 63 | } 64 | 65 | + (BOOL)gh_ensureDirectoryExists:(NSString *)directory created:(BOOL *)created error:(NSError **)error { 66 | if (![self gh_exist:directory]) { 67 | BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:error]; 68 | if (success && created) *created = YES; 69 | return success; 70 | } else if (![self gh_isDirectory:directory]) { 71 | if (error) *error = [NSError gh_errorWithDomain:@"GHNSFileManager" code:-1 localizedDescription:@"Path exists but is not a directory"]; 72 | return NO; 73 | } else { 74 | // Path existed and was a directory 75 | return YES; 76 | } 77 | } 78 | 79 | + (NSString *)gh_uniquePathWithNumber:(NSString *)path { 80 | NSInteger index = 1; 81 | NSString *uniquePath = path; 82 | NSString *prefixPath = nil, *pathExtension = nil; 83 | 84 | while([self gh_exist:uniquePath]) { 85 | if (!prefixPath) prefixPath = [path stringByDeletingPathExtension]; 86 | if (!pathExtension) pathExtension = [path gh_fullPathExtension]; 87 | uniquePath = [NSString stringWithFormat:@"%@-%ld%@", prefixPath, (long)index, pathExtension]; 88 | index++; 89 | } 90 | return uniquePath; 91 | } 92 | 93 | + (NSString *)gh_pathToResource:(NSString *)path { 94 | if (!path) return nil; 95 | return [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:path]; 96 | } 97 | 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /GHKit/GHNSMutableArray+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSMutableArray+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/1/09. 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Utilities for mutable arrays, for example, inserting, replacing at index, and more. 33 | */ 34 | @interface NSMutableArray(GHUtils) 35 | 36 | /*! 37 | Insert objects at index. 38 | 39 | @param objects Objects to insert 40 | @param index Index to insert at 41 | */ 42 | - (void)gh_insertObjects:(NSArray *)objects atIndex:(NSInteger)index; 43 | 44 | /*! 45 | Replace object with another object. 46 | 47 | @param objectToReplace Object to replace 48 | @param object Object that will replace 49 | @result Index object was set; NSNotFound if objectToReplace was not found 50 | */ 51 | - (NSUInteger)gh_replaceObject:(id)objectToReplace withObject:(id)object; 52 | 53 | /*! 54 | Compact. 55 | Remove all instances NSNull. 56 | */ 57 | - (void)gh_mutableCompact; 58 | 59 | /*! 60 | Add object. If object is nil, this is a no op. 61 | 62 | @param obj Object to add 63 | */ 64 | - (void)gh_addObject:(id)obj; 65 | 66 | /*! 67 | Remove last object. 68 | 69 | @result Last object removed 70 | */ 71 | - (id)gh_removeLastObject; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /GHKit/GHNSMutableArray+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSMutableArray+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/1/09. 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHNSMutableArray+Utils.h" 30 | 31 | 32 | @implementation NSMutableArray(GHUtils) 33 | 34 | - (void)gh_insertObjects:(NSArray *)objects atIndex:(NSInteger)index { 35 | NSIndexSet *indexes = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(index, [objects count])]; 36 | [self insertObjects:objects atIndexes:indexes]; 37 | } 38 | 39 | - (NSUInteger)gh_replaceObject:(id)objectToReplace withObject:(id)object { 40 | NSUInteger index = [self indexOfObject:objectToReplace]; 41 | if (index != NSNotFound) { 42 | [self replaceObjectAtIndex:index withObject:object]; 43 | } 44 | return index; 45 | } 46 | 47 | - (void)gh_mutableCompact { 48 | [self removeObjectIdenticalTo:[NSNull null]]; 49 | } 50 | 51 | - (void)gh_addObject:(id)obj { 52 | if (obj) [self addObject:obj]; 53 | } 54 | 55 | - (id)gh_removeLastObject { 56 | id obj = [self lastObject]; 57 | if (obj) [self removeLastObject]; 58 | return obj; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /GHKit/GHNSMutableDictionary+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSMutableDictionary+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 3/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Utilities for NSMutableDictionary. 34 | */ 35 | @interface NSMutableDictionary(GHUtils) 36 | 37 | /*! 38 | Set double value. 39 | 40 | @param d Double 41 | @param forKey Key 42 | */ 43 | - (void)gh_setDouble:(double)d forKey:(id)forKey; 44 | 45 | /*! 46 | Set integer value. 47 | 48 | @param n Integer 49 | @param forKey Key 50 | */ 51 | - (void)gh_setInteger:(NSInteger)n forKey:(id)forKey; 52 | 53 | /*! 54 | Set bool. 55 | 56 | @param b Bool 57 | @param forKey Key 58 | */ 59 | - (void)gh_setBool:(BOOL)b forKey:(id)forKey; 60 | 61 | /*! 62 | Set object or [NSNull null] if nil. 63 | 64 | @param object Object 65 | @param forKey Key 66 | */ 67 | - (void)gh_setObjectMaybeNil:(id)object forKey:(id)forKey; 68 | 69 | /*! 70 | Compact. 71 | Remove all value with instances NSNull. 72 | */ 73 | - (void)gh_mutableCompact; 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /GHKit/GHNSMutableDictionary+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSMutableDictionary+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 3/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHNSMutableDictionary+Utils.h" 31 | 32 | @implementation NSMutableDictionary(GHUtils) 33 | 34 | - (void)gh_setDouble:(double)d forKey:(id)key { 35 | [self setObject:[NSNumber numberWithDouble:d] forKey:key]; 36 | } 37 | 38 | - (void)gh_setInteger:(NSInteger)n forKey:(id)key { 39 | [self setObject:[NSNumber numberWithInteger:n] forKey:key]; 40 | } 41 | 42 | - (void)gh_setBool:(BOOL)b forKey:(id)key { 43 | [self setObject:[NSNumber numberWithBool:b] forKey:key]; 44 | } 45 | 46 | - (void)gh_setObjectMaybeNil:(id)object forKey:(id)key { 47 | if (!object) object = [NSNull null]; 48 | [self setObject:object forKey:key]; 49 | } 50 | 51 | - (void)gh_mutableCompact { 52 | NSMutableArray *keysToRemove = [[NSMutableArray alloc] init]; 53 | for (id key in self) { 54 | if ([[self objectForKey:key] isEqual:[NSNull null]]) 55 | [keysToRemove addObject:key]; 56 | } 57 | [self removeObjectsForKeys:keysToRemove]; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /GHKit/GHNSNotificationCenter+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSNotificationCenter+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 12/7/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Utilities for NSNotificationCenter. 34 | */ 35 | @interface NSNotificationCenter(GHUtils) 36 | 37 | /*! 38 | Replace observer. If present, it removes and re-adds the observer. 39 | 40 | @param observer Observer 41 | @param selector Selector 42 | @param name Name 43 | @param object Object 44 | */ 45 | - (void)gh_replaceObserver:(id)observer selector:(SEL)selector name:(NSString *)name object:(id)object; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /GHKit/GHNSNotificationCenter+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSNotificationCenter+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 12/7/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHNSNotificationCenter+Utils.h" 31 | 32 | 33 | @implementation NSNotificationCenter(GHUtils) 34 | 35 | - (void)gh_replaceObserver:(id)observer selector:(SEL)selector name:(NSString *)name object:(id)object { 36 | [self removeObserver:observer name:name object:object]; 37 | [self addObserver:observer selector:selector name:name object:object]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /GHKit/GHNSNumber+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSNumber+Utils.h 3 | // 4 | // Created by Gabe on 6/24/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Utilites for dealing with numbers, for example, human readable file size, ordinals, random numbers. 33 | */ 34 | @interface NSNumber(GHUtils) 35 | 36 | /*! 37 | File size label. 38 | 39 | @result '904 b', '32 KB', '1.1 MB', 40 | */ 41 | - (NSString *)gh_humanSize; 42 | 43 | /*! 44 | File size label. 45 | 46 | @param delimiter In between numeric and unit 47 | @result '904 b', '32 KB', '1.1 MB', 48 | */ 49 | - (NSString *)gh_humanSizeWithDelimiter:(NSString *)delimiter; 50 | 51 | /*! 52 | Ordinalize. 53 | 54 | 0 => nil 55 | 1 => "1st" 56 | 2 => "2nd" 57 | 3 => "3rd" 58 | 4-9 -> "4th", "5th", ... 59 | Ending in 11, 12 or 13 => "111th", "212th", ... 60 | 61 | @result Ordinal string for integer. 62 | */ 63 | - (NSString *)gh_ordinalize; 64 | 65 | /*! 66 | Ordinalize (masculine). 67 | 68 | @param masculine If YES will format for masculine 69 | @result Ordinal string for integer 70 | */ 71 | - (NSString *)gh_ordinalizeMasculine:(BOOL)masculine; 72 | 73 | /*! 74 | Ordinalize. See gh_ordinalize. 75 | 76 | @param value Value 77 | @result Ordinal string for integer. 78 | */ 79 | + (NSString *)gh_ordinalize:(NSInteger)value; 80 | 81 | /*! 82 | Ordinalize. See gh_ordinalizeMasculine:. 83 | 84 | @param value Value 85 | @param masculine If masculine 86 | @result Ordinal string for integer. 87 | */ 88 | + (NSString *)gh_ordinalize:(NSInteger)value masculine:(BOOL)masculine; 89 | 90 | /*! 91 | Shared bool number instance. 92 | 93 | @param b Bool 94 | */ 95 | + (NSNumber *)gh_bool:(BOOL)b; 96 | 97 | /*! 98 | Shared bool number instance representing NO. 99 | */ 100 | + (NSNumber *)gh_no; 101 | 102 | /*! 103 | Shared bool number instance representing NO. 104 | */ 105 | + (NSNumber *)gh_yes; 106 | 107 | /*! 108 | Random number. 109 | 110 | @result Random integer 111 | */ 112 | + (NSInteger)gh_randomInteger; 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /GHKit/GHNSNumber+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSNumber+Utils.m 3 | // 4 | // Created by Gabe on 6/24/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | 30 | #import "GHNSNumber+Utils.h" 31 | #import "GHNSArray+Utils.h" 32 | 33 | 34 | @implementation NSNumber(GHUtils) 35 | 36 | - (NSString *)gh_humanSize { 37 | return [self gh_humanSizeWithDelimiter:@" "]; 38 | } 39 | 40 | - (NSString *)gh_humanSizeWithDelimiter:(NSString *)delimiter { 41 | double value = [self doubleValue]; 42 | double byteTest = 1024; 43 | double kilobyteTest = 1024 * byteTest; 44 | double megaByteTest = 1024 * kilobyteTest; 45 | 46 | if (value < byteTest) { 47 | return [NSString stringWithFormat:@"%.0f%@B", value, delimiter]; 48 | } else if (value < kilobyteTest) { 49 | double d = value / byteTest; 50 | return [NSString stringWithFormat:@"%.0f%@KB", d, delimiter]; 51 | } else if (value < megaByteTest) { 52 | double d = value / kilobyteTest; 53 | return [NSString stringWithFormat:@"%.1f%@MB", d, delimiter]; 54 | } else { 55 | double d = value / megaByteTest; 56 | return [NSString stringWithFormat:@"%.2f%@GB", d, delimiter]; 57 | } 58 | return nil; 59 | } 60 | 61 | - (NSString *)gh_ordinalize { 62 | return [NSNumber gh_ordinalize:[self integerValue]]; 63 | } 64 | 65 | - (NSString *)gh_ordinalizeMasculine:(BOOL)masculine { 66 | return [NSNumber gh_ordinalize:[self integerValue] masculine:masculine]; 67 | } 68 | 69 | + (NSString *)gh_ordinalizeEn:(NSInteger)value { 70 | NSString *suffix = nil; 71 | if (value > 0) { 72 | switch(value % 10) { 73 | case 1: suffix = @"st"; break; 74 | case 2: suffix = @"nd"; break; 75 | case 3: suffix = @"rd"; break; 76 | default: suffix = @"th"; break; 77 | } 78 | if (value % 100 >= 11 && value % 100 <= 13) suffix = @"th"; // Handle 11-13 79 | if (suffix) return [NSString stringWithFormat:@"%ld%@", (long)value, suffix]; 80 | } 81 | return [NSString stringWithFormat:@"%ld", (long)value]; 82 | } 83 | 84 | // NOTE(johnb): Ignoring plural forms of ordinals (ers / res / es) 85 | + (NSString *)gh_ordinalizeFr:(NSInteger)value masculine:(BOOL)masculine { 86 | NSString *suffix = nil; 87 | switch(value) { 88 | case 0: break; 89 | case 1: suffix = masculine ? @"er" : @"re"; break; 90 | default: suffix = @"e"; break; 91 | } 92 | if (suffix) return [NSString stringWithFormat:@"%ld%@", (long)value, suffix]; 93 | return [NSString stringWithFormat:@"%ld", (long)value]; 94 | } 95 | 96 | + (NSString *)gh_ordinalizeDe:(NSInteger)value masculine:(BOOL)masculine { 97 | NSString *suffix = nil; 98 | switch (value) { 99 | case 0: break; 100 | default: suffix = masculine ? @"ter" : @"te"; break; 101 | } 102 | if (suffix) return [NSString stringWithFormat:@"%ld%@", (long)value, suffix]; 103 | return [NSString stringWithFormat:@"%ld", (long)value]; 104 | } 105 | 106 | // NOTE(johnb): Ignoring plural forms or ordinals (os / as) 107 | + (NSString *)gh_ordinalizeEs:(NSInteger)value masculine:(BOOL)masculine { 108 | NSString *suffix = nil; 109 | switch(value) { 110 | case 0: break; 111 | default: suffix = masculine ? @"º" : @"ª"; break; 112 | } 113 | if (suffix) return [NSString stringWithFormat:@"%ld%@", (long)value, suffix]; 114 | return [NSString stringWithFormat:@"%ld", (long)value]; 115 | } 116 | 117 | // The following OpenOffice wiki page is helpful for information about ordinals 118 | // http://wiki.services.openoffice.org/wiki/Localized_AutoCorrection_of_Ordinal_Numbers_(1st_2nd)#French 119 | + (NSString *)gh_ordinalize:(NSInteger)value masculine:(BOOL)masculine { 120 | NSString *languageCode = [[NSLocale preferredLanguages] gh_firstObject]; 121 | if ([languageCode isEqual:@"en"]) { 122 | return [NSNumber gh_ordinalizeEn:value]; 123 | } else if ([languageCode isEqual:@"fr"]) { 124 | return [NSNumber gh_ordinalizeFr:value masculine:masculine]; 125 | } else if ([languageCode isEqual:@"de"]) { 126 | return [NSNumber gh_ordinalizeDe:value masculine:masculine]; 127 | } else if ([languageCode isEqual:@"es"]) { 128 | return [NSNumber gh_ordinalizeEs:value masculine:masculine]; 129 | } 130 | return [NSString stringWithFormat:@"%ld", (long)value]; 131 | } 132 | 133 | + (NSString *)gh_ordinalize:(NSInteger)value { 134 | return [NSNumber gh_ordinalize:value masculine:YES]; 135 | } 136 | 137 | + (NSNumber *)gh_bool:(BOOL)b { 138 | if (!b) { 139 | static NSNumber *NumberForNo = NULL; 140 | if (NumberForNo == NULL) NumberForNo = [NSNumber numberWithBool:NO]; 141 | return NumberForNo; 142 | } else { 143 | static NSNumber *NumberForYes = NULL; 144 | if (NumberForYes == NULL) NumberForYes = [NSNumber numberWithBool:YES]; 145 | return NumberForYes; 146 | } 147 | } 148 | 149 | + (NSNumber *)gh_no { 150 | return [self gh_bool:NO]; 151 | } 152 | 153 | + (NSNumber *)gh_yes { 154 | return [self gh_bool:YES]; 155 | } 156 | 157 | + (NSInteger)gh_randomInteger { 158 | double r = (double)arc4random()/INT_MAX; 159 | return NSIntegerMax * r; 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /GHKit/GHNSObject+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSObject+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/8/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Utilities for NSObject. 34 | */ 35 | @interface NSObject(GHUtils) 36 | 37 | /*! 38 | Check if not equal to NSNull. 39 | Useful for checking nil and NSNull in a single expression: 40 | 41 | if ([foo gh_isNotNSNull]) { } // Will not evaluate if foo == nil or foo is equal to [NSNull null] 42 | 43 | */ 44 | - (BOOL)gh_isNotNSNull; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /GHKit/GHNSObject+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSObject+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/8/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | 31 | #import "GHNSObject+Utils.h" 32 | 33 | 34 | @implementation NSObject(GHUtils) 35 | 36 | - (BOOL)gh_isNotNSNull { 37 | return ![self isEqual:[NSNull null]]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /GHKit/GHNSString+TimeInterval.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSString+TimeInterval.h 3 | // 4 | // Created by Gabe on 6/6/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Utilities for generating time ago in words. 33 | */ 34 | @interface NSString(GHTimeInterval) 35 | 36 | /*! 37 | Time ago in words. 38 | 39 | For localized values see the localization keys below. 40 | This method calls gh_localizedStringForTimeInterval with nil tableName and [NSBundle mainBundle] bundle. 41 | 42 | @param interval Interval in seconds 43 | @param includeSeconds If YES, will say 'less than N seconds', otherwise will show 'less than a minute' 44 | @result Time ago in words 45 | */ 46 | + (NSString *)gh_stringForTimeInterval:(NSTimeInterval)interval includeSeconds:(BOOL)includeSeconds; 47 | 48 | /*! 49 | Time ago in words (localized). 50 | 51 | These are the localized defaults, that you can override: 52 | 53 | LessThanAMinute = "less than a minute"; 54 | LessThanXSeconds = "less than %d seconds"; 55 | HalfMinute = "half a minute"; 56 | 1Minute = "1 minute"; 57 | XMinutes = "%.0f minutes"; 58 | About1Hour = "about 1 hour"; 59 | AboutXHours = "about %.0f hours"; 60 | 1Day = "1 day"; 61 | XDays = "%.0f days"; 62 | About1Month = "about 1 month"; 63 | XMonths = "%.0f months"; 64 | About1Year = "about 1 year"; 65 | OverXYears = "over %.0f years"; 66 | 67 | @param interval Interval in seconds 68 | @param includeSeconds If YES, will say 'less than N seconds', otherwise will show 'less than a minute' 69 | @param tableName Table name for localized string 70 | @param bundle Bundle for localized string 71 | @result Time ago in words 72 | */ 73 | + (NSString *)gh_localizedStringForTimeInterval:(NSTimeInterval)interval includeSeconds:(BOOL)includeSeconds tableName:(NSString *)tableName bundle:(NSBundle *)bundle; 74 | 75 | 76 | 77 | 78 | /*! 79 | Time ago in abbreviated format. 80 | 81 | For localized values see the localization keys below. 82 | This method calls gh_localizedStringForTimeInterval with nil tableName and [NSBundle mainBundle] bundle. 83 | 84 | @param interval Interval in seconds 85 | @result Time ago in abbreviated format 86 | */ 87 | + (NSString *)gh_abbreviatedStringForTimeInterval:(NSTimeInterval)interval; 88 | 89 | /*! 90 | Time ago in words (abbreviated and localized). 91 | 92 | These are the localized defaults, that you can override: 93 | 94 | XSecondsAbbreviated = "%.0fs"; 95 | XMinutesAbbreviated = "%.0fm"; 96 | XHoursAbbreviated = "%.0fh"; 97 | XDaysAbbreviated = "%.0fd"; 98 | XMonthsAbbreviated = "%.0fmo"; 99 | XYearsAbbreviated = "%.0fy"; 100 | 101 | @param interval Interval in seconds 102 | @param tableName Table name for localized string 103 | @param bundle Bundle for localized string 104 | @result Time ago in abbreviated words 105 | */ 106 | + (NSString *)gh_localizedAbbreviatedStringForTimeInterval:(NSTimeInterval)interval tableName:(NSString *)tableName bundle:(NSBundle *)bundle; 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /GHKit/GHNSString+TimeInterval.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSString+TimeInterval.m 3 | // 4 | // Created by Gabe on 6/6/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHNSString+TimeInterval.h" 30 | 31 | #import 32 | 33 | @implementation NSString(GHTimeInterval) 34 | 35 | #define GHIntervalLocalize(key, defaultValue) NSLocalizedStringWithDefaultValue(key, tableName, bundle, defaultValue, nil) 36 | 37 | + (NSString *)gh_stringForTimeInterval:(NSTimeInterval)interval includeSeconds:(BOOL)includeSeconds { 38 | return [self gh_localizedStringForTimeInterval:interval includeSeconds:includeSeconds tableName:nil bundle:[NSBundle mainBundle]]; 39 | } 40 | 41 | + (NSString *)gh_localizedStringForTimeInterval:(NSTimeInterval)interval includeSeconds:(BOOL)includeSeconds tableName:(NSString *)tableName bundle:(NSBundle *)bundle { 42 | NSTimeInterval intervalInSeconds = fabs(interval); 43 | double intervalInMinutes = round(intervalInSeconds/60.0); 44 | 45 | if (intervalInMinutes >= 0 && intervalInMinutes <= 1) { 46 | if (!includeSeconds) return intervalInMinutes <= 0 ? GHIntervalLocalize(@"LessThanAMinute", @"less than a minute") : GHIntervalLocalize(@"1Minute", @"1 minute"); 47 | if (intervalInSeconds >= 0 && intervalInSeconds < 5) return [NSString stringWithFormat:GHIntervalLocalize(@"LessThanXSeconds", @"less than %d seconds"), 5]; 48 | else if (intervalInSeconds >= 5 && intervalInSeconds < 10) return [NSString stringWithFormat:GHIntervalLocalize(@"LessThanXSeconds", @"less than %d seconds"), 10]; 49 | else if (intervalInSeconds >= 10 && intervalInSeconds < 20) return [NSString stringWithFormat:GHIntervalLocalize(@"LessThanXSeconds", @"less than %d seconds"), 20]; 50 | else if (intervalInSeconds >= 20 && intervalInSeconds < 40) return GHIntervalLocalize(@"HalfMinute", @"half a minute"); 51 | else if (intervalInSeconds >= 40 && intervalInSeconds < 60) return GHIntervalLocalize(@"LessThanAMinute", @"less than a minute"); 52 | else return GHIntervalLocalize(@"1Minute", @"1 minute"); 53 | } 54 | else if (intervalInMinutes >= 2 && intervalInMinutes <= 44) return [NSString stringWithFormat:GHIntervalLocalize(@"XMinutes", @"%.0f minutes"), intervalInMinutes]; 55 | else if (intervalInMinutes >= 45 && intervalInMinutes <= 89) return GHIntervalLocalize(@"About1Hour", @"about 1 hour"); 56 | else if (intervalInMinutes >= 90 && intervalInMinutes <= 1439) return [NSString stringWithFormat:GHIntervalLocalize(@"AboutXHours", @"about %.0f hours"), round(intervalInMinutes/60.0)]; 57 | else if (intervalInMinutes >= 1440 && intervalInMinutes <= 2879) return GHIntervalLocalize(@"1Day", @"1 day"); 58 | else if (intervalInMinutes >= 2880 && intervalInMinutes <= 43199) return [NSString stringWithFormat:GHIntervalLocalize(@"XDays", @"%.0f days"), round(intervalInMinutes/1440.0)]; 59 | else if (intervalInMinutes >= 43200 && intervalInMinutes <= 86399) return GHIntervalLocalize(@"About1Month", @"about 1 month"); 60 | else if (intervalInMinutes >= 86400 && intervalInMinutes <= 525599) return [NSString stringWithFormat:GHIntervalLocalize(@"XMonths", @"%.0f months"), round(intervalInMinutes/43200.0)]; 61 | else if (intervalInMinutes >= 525600 && intervalInMinutes <= 1051199) return GHIntervalLocalize(@"About1Year", @"about 1 year"); 62 | else 63 | return [NSString stringWithFormat:GHIntervalLocalize(@"OverXYears", @"over %.0f years"), floor(intervalInMinutes/525600.0)]; 64 | } 65 | 66 | + (NSString *)gh_abbreviatedStringForTimeInterval:(NSTimeInterval)interval { 67 | return [self gh_localizedAbbreviatedStringForTimeInterval:interval tableName:nil bundle:[NSBundle mainBundle]]; 68 | } 69 | 70 | + (NSString *)gh_localizedAbbreviatedStringForTimeInterval:(NSTimeInterval)interval tableName:(NSString *)tableName bundle:(NSBundle *)bundle { 71 | NSTimeInterval intervalInSeconds = fabs(interval); 72 | double intervalInMinutes = round(intervalInSeconds/60.0); 73 | 74 | if (intervalInSeconds < 60) return [NSString stringWithFormat:GHIntervalLocalize(@"XSecondsAbbreviated", @"%.0fs"), intervalInSeconds]; 75 | else if (intervalInMinutes >= 1 && intervalInMinutes < 60) return [NSString stringWithFormat:GHIntervalLocalize(@"XMinutesAbbreviated", @"%.0fm"), intervalInMinutes]; 76 | else if (intervalInMinutes >= 60 && intervalInMinutes < 1440) return [NSString stringWithFormat:GHIntervalLocalize(@"XHoursAbbreviated", @"%.0fh"), round(intervalInMinutes/60.0)]; 77 | else if (intervalInMinutes >= 1440 && intervalInMinutes < 43200) return [NSString stringWithFormat:GHIntervalLocalize(@"XDaysAbbreviated", @"%.0fd"), round(intervalInMinutes/1440.0)]; 78 | else if (intervalInMinutes >= 43200 && intervalInMinutes < 525600) return [NSString stringWithFormat:GHIntervalLocalize(@"XMonthsAbbreviated", @"%.0fmo"), round(intervalInMinutes/43200.0)]; 79 | else 80 | return [NSString stringWithFormat:GHIntervalLocalize(@"XYearsAbbreviated", @"%.0fy"), floor(intervalInMinutes/525600.0)]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /GHKit/GHNSString+URL.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSString+URL.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 2/18/09. 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHNSURL+Utils.h" 30 | 31 | /*! 32 | Utilities for URL encoding/decoding. 33 | */ 34 | @interface NSString(GHURL) 35 | 36 | /*! 37 | Decode URL encoded string. 38 | @see NSURL#gh_decode: 39 | */ 40 | - (NSString *)gh_URLDecode; 41 | 42 | /*! 43 | Encode URL string. 44 | 45 | "~!@#$%^&*(){}[]=:/,;?+'\"\\" => ~!@#$%25%5E&*()%7B%7D%5B%5D=:/,;?+'%22%5C 46 | 47 | Doesn't encode: ~!@#$&*()=:/,;?+' 48 | 49 | Does encode: %^{}[]"\ 50 | 51 | Should be the same as javascript's encodeURI(). 52 | See http://xkr.us/articles/javascript/encode-compare/ 53 | 54 | @see NSURL#gh_encode: 55 | */ 56 | - (NSString *)gh_URLEncode; 57 | 58 | /*! 59 | Encode URL string (for escaping URL key/value params). 60 | 61 | "~!@#$%^&*(){}[]=:/,;?+'\"\\" => ~!%40%23%24%25%5E%26*()%7B%7D%5B%5D%3D%3A%2F%2C%3B%3F%2B'%22%5C 62 | 63 | Doesn't encode: ~!*()' 64 | 65 | Does encode: @#$%^&{}[]=:/,;?+"\ 66 | 67 | Should be the same as javascript's encodeURIComponent(). 68 | See http://xkr.us/articles/javascript/encode-compare/ 69 | 70 | @see NSURL#gh_encodeComponent 71 | */ 72 | - (NSString *)gh_URLEncodeComponent; 73 | 74 | /*! 75 | Encode URL string (all characters). 76 | 77 | Encodes: @#$%^&{}[]=:/,;?+"\~!*()' 78 | 79 | @see NSURL#gh_escapeAll 80 | */ 81 | - (NSString *)gh_URLEscapeAll; 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /GHKit/GHNSString+URL.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSString+URL.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 2/18/09. 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHNSString+URL.h" 30 | 31 | 32 | @implementation NSString(GHURL) 33 | 34 | - (NSString *)gh_URLDecode { 35 | return [NSURL gh_decode:self]; 36 | } 37 | 38 | - (NSString *)gh_URLEncode { 39 | return [NSURL gh_encode:self]; 40 | } 41 | 42 | - (NSString *)gh_URLEncodeComponent { 43 | return [NSURL gh_encodeComponent:self]; 44 | } 45 | 46 | - (NSString *)gh_URLEscapeAll { 47 | return [NSURL gh_escapeAll:self]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /GHKit/GHNSStringEnumerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSStringEnumerator.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 6/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | For scanning, and enumerating strings. 34 | 35 | NSString *string = @"matz can't\n patch blues"; 36 | GHNSStringEnumerator *enumerator = [[GHNSStringEnumerator alloc] initWithString:string separatorCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 37 | 38 | NSArray *results = [enumerator allObjects]; 39 | NSArray *expected = [NSArray arrayWithObjects:@"matz", @" ", @"can't", @"\n ", @"patch", @" ", @"blues", nil]; 40 | XCTAssertEqualObjects(results, expected, nil); 41 | 42 | */ 43 | @interface GHNSStringEnumerator : NSEnumerator { 44 | NSScanner *_scanner; 45 | 46 | NSCharacterSet *_separatorCharacterSet; 47 | NSString *_separatorString; 48 | 49 | BOOL _inSeparator; 50 | } 51 | 52 | @property (readonly, nonatomic, getter=isInSeparator) BOOL inSeparator; 53 | 54 | /*! 55 | Create string enumerator with string and separator character set. 56 | 57 | @param string String 58 | @param separatorCharacterSet Separator characters 59 | */ 60 | - (id)initWithString:(NSString *)string separatorCharacterSet:(NSCharacterSet *)separatorCharacterSet; 61 | 62 | /*! 63 | Create string enumerator with string and separator character set. 64 | 65 | @param string String 66 | @param separatorString Separator characters 67 | */ 68 | - (id)initWithString:(NSString *)string separatorString:(NSString *)separatorString; 69 | 70 | /*! 71 | Next string. 72 | 73 | @result Next string 74 | */ 75 | - (NSString *)nextString; 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /GHKit/GHNSStringEnumerator.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSStringEnumerator.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 6/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | 31 | #import "GHNSStringEnumerator.h" 32 | 33 | 34 | @implementation GHNSStringEnumerator 35 | 36 | @synthesize inSeparator=_inSeparator; 37 | 38 | - (id)initWithString:(NSString *)string separatorCharacterSet:(NSCharacterSet *)separatorCharacterSet 39 | separatorString:(NSString *)separatorString { 40 | 41 | if ((self = [super init])) { 42 | _scanner = [[NSScanner alloc] initWithString:string]; 43 | _scanner.charactersToBeSkipped = nil; 44 | _separatorCharacterSet = separatorCharacterSet; 45 | _separatorString = separatorString; 46 | _inSeparator = YES; 47 | } 48 | return self; 49 | } 50 | 51 | - (id)initWithString:(NSString *)string separatorCharacterSet:(NSCharacterSet *)separatorCharacterSet { 52 | return [self initWithString:string separatorCharacterSet:separatorCharacterSet separatorString:nil]; 53 | } 54 | 55 | - (id)initWithString:(NSString *)string separatorString:(NSString *)separatorString { 56 | return [self initWithString:string separatorCharacterSet:nil separatorString:separatorString]; 57 | } 58 | 59 | - (NSString *)nextString { 60 | if (_scanner.isAtEnd) return nil; 61 | 62 | NSString *s = nil; 63 | while(s == nil) { 64 | _inSeparator = !_inSeparator; 65 | if (_separatorCharacterSet) { 66 | if (!_inSeparator) { 67 | [_scanner scanUpToCharactersFromSet:_separatorCharacterSet intoString:&s]; 68 | } else { 69 | [_scanner scanCharactersFromSet:_separatorCharacterSet intoString:&s]; 70 | } 71 | } else if (_separatorString) { 72 | if (!_inSeparator) { 73 | [_scanner scanUpToString:_separatorString intoString:&s]; 74 | } else { 75 | [_scanner scanString:_separatorString intoString:&s]; 76 | } 77 | } 78 | } 79 | return s; 80 | } 81 | 82 | - (id)nextObject { 83 | return [self nextString]; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /GHKit/GHNSURL+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSURL+Utils.h 3 | // 4 | // Created by Gabe on 3/19/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Utilities for URLs, for example, encoding, escaping, parsing, splitting out or sorting query params, and more. 33 | */ 34 | @interface NSURL(GHUtils) 35 | 36 | /*! 37 | Get dictionary from NSURL query parameter. 38 | 39 | @result Dictionary of key, value pairs from parsing query parameter 40 | */ 41 | - (NSMutableDictionary *)gh_queryDictionary; 42 | 43 | /*! 44 | Dictionary to query string. Escapes any encoded characters. 45 | 46 | @param queryDictionary Dictionary of key value params 47 | @result Query string, key1=value1&key2=value2 48 | */ 49 | + (NSString *)gh_dictionaryToQueryString:(NSDictionary *)queryDictionary; 50 | 51 | /*! 52 | Convert dictionary to URL query string. 53 | Escapes any encoded characters. 54 | 55 | @param queryDictionary Dictionary 56 | @param sort If YES, will sort items 57 | @result Query string, key1=value1&key2=value2 58 | */ 59 | + (NSString *)gh_dictionaryToQueryString:(NSDictionary *)queryDictionary sort:(BOOL)sort; 60 | 61 | /*! 62 | Convert dictionary to array of query strings. 63 | 64 | @param queryDictionary Dictionary 65 | @param sort If YES, will sort items 66 | @param encoded If YES, will be URL component encoded 67 | @result Query strings, ['key1=value1', 'key2=value2'] 68 | */ 69 | + (NSArray *)gh_dictionaryToQueryArray:(NSDictionary *)queryDictionary sort:(BOOL)sort encoded:(BOOL)encoded; 70 | 71 | /*! 72 | Convert URL query string to dictionary. 73 | 74 | @param string URL params string, key1=value1&key2=value2 75 | @result Dictionary 76 | */ 77 | + (NSMutableDictionary *)gh_queryStringToDictionary:(NSString *)string; 78 | 79 | /*! 80 | Get query string, sorted by key. 81 | For example, "b=c&a=d" is returned as "a=d&b=c". 82 | 83 | @result Sorted query string 84 | */ 85 | - (NSString *)gh_sortedQuery; 86 | 87 | /*! 88 | Derive new URL with a new query. All other fields should be the same. 89 | 90 | @param query Query string 91 | @result URL with new query 92 | */ 93 | - (NSURL *)gh_deriveWithQuery:(NSString *)query; 94 | 95 | /*! 96 | Canonical form of URL. 97 | 98 | @result Canonical URL 99 | */ 100 | - (NSURL *)gh_canonical; 101 | 102 | /*! 103 | Canonical form of URL. 104 | 105 | @param ignore Do not include the set of query params 106 | @result Canonical URL 107 | */ 108 | - (NSURL *)gh_canonicalWithIgnore:(NSArray *)ignore; 109 | 110 | /*! 111 | Remove query params. 112 | 113 | @param filterQueryParams List of keys to filter 114 | @param sort Whether to sort query params 115 | @result URL without query params. 116 | */ 117 | - (NSURL *)gh_filterQueryParams:(NSArray *)filterQueryParams sort:(BOOL)sort; 118 | 119 | /*! 120 | Encode URL string. 121 | 122 | "~!@#$%^&*(){}[]=:/,;?+'\"\\" => ~!@#$%25%5E&*()%7B%7D%5B%5D=:/,;?+'%22%5C 123 | 124 | Doesn't encode: ~!@#$&*()=:/,;?+' 125 | 126 | Does encode: %^{}[]"\ 127 | 128 | Should be the same as javascript's encodeURI(). 129 | See http://xkr.us/articles/javascript/encode-compare/ 130 | 131 | @param s String to escape 132 | @result Encode string 133 | */ 134 | + (NSString *)gh_encode:(NSString *)s; 135 | 136 | /*! 137 | Encode URL string (for escaping URL key/value params). 138 | 139 | "~!@#$%^&*(){}[]=:/,;?+'\"\\" => ~!%40%23%24%25%5E%26*()%7B%7D%5B%5D%3D%3A%2F%2C%3B%3F%2B'%22%5C 140 | 141 | Doesn't encode: ~!*()' 142 | 143 | Does encode: @#$%^&{}[]=:/,;?+"\ 144 | 145 | Should be the same as javascript's encodeURIComponent(). 146 | See http://xkr.us/articles/javascript/encode-compare/ 147 | 148 | @param s String to encode 149 | @result Encoded string 150 | */ 151 | + (NSString *)gh_encodeComponent:(NSString *)s; 152 | 153 | /*! 154 | Encode URL string. 155 | 156 | Encodes: @#$%^&{}[]=:/,;?+"\~!*()' 157 | 158 | @param s String to encode 159 | @result Encoded string 160 | */ 161 | + (NSString *)gh_escapeAll:(NSString *)s; 162 | 163 | /*! 164 | Decode URL string. 165 | 166 | @param s String to decode 167 | @result Decoded URL string 168 | */ 169 | + (NSString *)gh_decode:(NSString *)s; 170 | 171 | #if TARGET_OS_OSX 172 | 173 | /*! 174 | Copy URL to pasteboard. For Mac OS X only. 175 | */ 176 | - (void)gh_copyLinkToPasteboard; 177 | 178 | /*! 179 | Open file URL. 180 | Opens path in Finder or whatever is registered for the file:// scheme. 181 | For Mac OS X only. 182 | 183 | @param path Path to open 184 | @result YES if opened 185 | */ 186 | + (BOOL)gh_openFile:(NSString *)path; 187 | 188 | /*! 189 | Opens directory of file at path (or the path itself if it is a directory), 190 | in the Finder or whatever is registered for the file:// scheme. 191 | For Mac OS X only. 192 | 193 | @param path Path 194 | */ 195 | + (void)gh_openContainingFolder:(NSString *)path; 196 | 197 | #endif 198 | 199 | @end 200 | -------------------------------------------------------------------------------- /GHKit/GHNSUserDefaults+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSUserDefaults+Utils.h 3 | // GHKitIOS 4 | // 5 | // Created by Gabriel Handford on 7/21/11. 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Utilities for URLs, for example, encoding, escaping, parsing, splitting out or sorting query params, and more. 33 | */ 34 | @interface NSUserDefaults(GHUtils) 35 | 36 | /*! 37 | Check if user defaults contains key. 38 | 39 | @param key Key 40 | @result YES if defaults contains key 41 | */ 42 | - (BOOL)gh_containsKey:(NSString *)key; 43 | 44 | /*! 45 | BOOL for key. 46 | 47 | @param key Key 48 | @param withDefault Default value 49 | @result BOOL for key 50 | */ 51 | - (BOOL)gh_boolForKey:(id)key withDefault:(BOOL)withDefault; 52 | 53 | /*! 54 | Set BOOL for key. 55 | 56 | @param b Bool 57 | @param forKey Key 58 | */ 59 | - (void)gh_setBool:(BOOL)b forKey:(NSString *)forKey; 60 | 61 | /*! 62 | Double for key. 63 | 64 | @param key Key 65 | @param withDefault Value if not in defaults 66 | @result double for key 67 | */ 68 | - (double)gh_doubleForKey:(NSString *)key withDefault:(double)withDefault; 69 | 70 | /*! 71 | Set double for key. 72 | 73 | @param d Double 74 | @param forKey Key 75 | */ 76 | - (void)gh_setDouble:(double)d forKey:(NSString *)forKey; 77 | 78 | /*! 79 | Integer for key. 80 | 81 | @param key Key 82 | @param withDefault Value if not in defaults 83 | @result Integer for key 84 | */ 85 | - (NSInteger)gh_integerForKey:(NSString *)key withDefault:(NSInteger)withDefault; 86 | 87 | /*! 88 | Set integer for key. 89 | 90 | @param integer Integer 91 | @param forKey Key 92 | */ 93 | - (void)gh_setInteger:(NSInteger)integer forKey:(NSString *)forKey; 94 | 95 | /*! 96 | Object for key. 97 | 98 | @param key Key 99 | @param withDefault Value if not in defaults 100 | */ 101 | - (id)gh_objectForKey:(NSString *)key withDefault:(id)withDefault; 102 | 103 | /*! 104 | Set object (stored as NSData) for key. 105 | 106 | @param obj Object (should be NSCoding) 107 | @param forKey Key 108 | @result Object from data for key 109 | */ 110 | - (void)gh_setObjectAsData:(id)obj forKey:(NSString *)forKey; 111 | 112 | /*! 113 | Object from data for key. 114 | 115 | @param key Key 116 | @result Object from data for key 117 | */ 118 | - (id)gh_objectFromDataForKey:(NSString *)key; 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /GHKit/GHNSUserDefaults+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSUserDefaults+Utils.m 3 | // GHKitIOS 4 | // 5 | // Created by Gabriel Handford on 7/21/11. 6 | // Copyright 2011 rel.me. All rights reserved. 7 | // 8 | 9 | #import "GHNSUserDefaults+Utils.h" 10 | 11 | 12 | @implementation NSUserDefaults(GHUtils) 13 | 14 | - (BOOL)gh_containsKey:(NSString *)key { 15 | id value = [self objectForKey:key]; 16 | return (value != nil); 17 | } 18 | 19 | - (BOOL)gh_boolForKey:(id)key withDefault:(BOOL)defaultValue { 20 | id value = [self objectForKey:key]; 21 | if (!value) return defaultValue; 22 | return [value boolValue]; 23 | } 24 | 25 | - (void)gh_setBool:(BOOL)b forKey:(NSString *)key { 26 | [self setObject:[NSNumber numberWithBool:b] forKey:key]; 27 | } 28 | 29 | - (double)gh_doubleForKey:(NSString *)key withDefault:(double)defaultValue { 30 | id value = [self objectForKey:key]; 31 | if (!value) return defaultValue; 32 | return [value doubleValue]; 33 | } 34 | 35 | - (void)gh_setDouble:(double)d forKey:(NSString *)key { 36 | [self setObject:[NSNumber numberWithDouble:d] forKey:key]; 37 | } 38 | 39 | - (NSInteger)gh_integerForKey:(NSString *)key withDefault:(NSInteger)defaultValue { 40 | id value = [self objectForKey:key]; 41 | if (!value) return defaultValue; 42 | return [value integerValue]; 43 | } 44 | 45 | - (void)gh_setInteger:(NSInteger)integer forKey:(NSString *)key { 46 | [self setObject:[NSNumber numberWithInteger:integer] forKey:key]; 47 | } 48 | 49 | - (void)gh_setObjectAsData:(id)obj forKey:(NSString *)key { 50 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:obj]; 51 | if (!data) { 52 | [NSException raise:NSInvalidArgumentException format:@"Invalid data for key: %@", key]; 53 | return; 54 | } 55 | [self setObject:data forKey:key]; 56 | } 57 | 58 | - (id)gh_objectFromDataForKey:(NSString *)key { 59 | NSData *data = [self objectForKey:key]; 60 | if (!data) return nil; 61 | return [NSKeyedUnarchiver unarchiveObjectWithData:data]; 62 | } 63 | 64 | - (id)gh_objectForKey:(NSString *)key withDefault:(id)defaultValue { 65 | id value = [self objectForKey:key]; 66 | if (!value) return defaultValue; 67 | return value; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /GHKit/GHReversableDictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHReversableDictionary.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/25/10. 6 | // Copyright 2010. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | Reversable dictionary, where keys and values point to each other. 34 | */ 35 | @interface GHReversableDictionary : NSObject { 36 | NSMutableDictionary *_dict; 37 | NSMutableDictionary *_reversedDict; 38 | } 39 | 40 | /*! 41 | Create with capacity. 42 | 43 | @param capacity Capacity 44 | */ 45 | - (id)initWithCapacity:(NSInteger)capacity; 46 | 47 | /*! 48 | Create with objects and keys. 49 | 50 | @param firstObject Objects 51 | @param ... Objects and keys 52 | */ 53 | - (id)initWithObjectsAndKeys:(id)firstObject, ... NS_REQUIRES_NIL_TERMINATION; 54 | 55 | /*! 56 | Key for object. 57 | 58 | @param obj Object 59 | @result Key 60 | */ 61 | - (id)keyForObject:(id)obj; 62 | 63 | /*! 64 | Set object for key. 65 | 66 | @param obj Object 67 | @param key Key 68 | */ 69 | - (void)setObject:(id)obj forKey:(id)key; 70 | 71 | /*! 72 | Get object for key. 73 | 74 | @param key Key 75 | @result Object for key 76 | */ 77 | - (id)objectForKey:(id)key; 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /GHKit/GHReversableDictionary.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHReversableDictionary.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/25/10. 6 | // Copyright 2010. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHReversableDictionary.h" 31 | #import "GHKitDefines.h" 32 | 33 | 34 | @implementation GHReversableDictionary 35 | 36 | - (id)init { 37 | return [self initWithCapacity:10]; 38 | } 39 | 40 | - (id)initWithCapacity:(NSInteger)capacity { 41 | if ((self = [super init])) { 42 | _dict = [[NSMutableDictionary alloc] initWithCapacity:capacity]; 43 | _reversedDict = [[NSMutableDictionary alloc] initWithCapacity:capacity]; 44 | } 45 | return self; 46 | } 47 | 48 | - (id)initWithObjectsAndKeys:(id)firstObject, ... { 49 | GHConvertVarArgs(firstObject); 50 | self = [self init]; 51 | for(NSInteger i = 0, count = [arguments count]; i < count; i += 2) { 52 | if ((i + 1) >= [arguments count]) break; 53 | [self setObject:[arguments objectAtIndex:i] forKey:[arguments objectAtIndex:i+1]]; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)setObject:(id)obj forKey:(id)key { 59 | [_dict setObject:obj forKey:key]; 60 | [_reversedDict setObject:key forKey:obj]; 61 | } 62 | 63 | - (id)objectForKey:(id)key { 64 | return [_dict objectForKey:key]; 65 | } 66 | 67 | - (id)keyForObject:(id)obj { 68 | return [_reversedDict objectForKey:obj]; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /GHKit/GHValidators.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSString+Validation.h 3 | // 4 | // Created by Gabe on 7/20/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | /*! 32 | Validators for input, such as email addresses. 33 | */ 34 | @interface GHValidators : NSObject { } 35 | 36 | /*! 37 | Check if string is a valid email address. 38 | 39 | @param str String to validate 40 | @result YES if string is a valid email address 41 | */ 42 | + (BOOL)isEmailAddress:(NSString *)str; 43 | 44 | /*! 45 | Check if credit card number passes Luhn validation. 46 | 47 | @param numberString Credit card number as string 48 | @result YES if passes luhn validation 49 | */ 50 | + (BOOL)isCreditCardNumber:(NSString *)numberString; 51 | 52 | /*! 53 | Check if string is a valid credit card expiration. 54 | Accept 1/12, 01/12, or 01/2012, or 01/10/2012. 55 | 56 | @param expiration Expiration string. 57 | @param date Current date, fails if expiration < this date 58 | @result YES if passes validation 59 | */ 60 | + (BOOL)isCreditCardExpiration:(NSString *)expiration date:(NSDate *)date; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /GHKit/GHValidators.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHValidators.m 3 | // 4 | // Created by Gabe on 7/20/08. 5 | // Copyright 2008 Gabriel Handford 6 | // 7 | // Permission is hereby granted, free of charge, to any person 8 | // obtaining a copy of this software and associated documentation 9 | // files (the "Software"), to deal in the Software without 10 | // restriction, including without limitation the rights to use, 11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following 14 | // conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | // OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "GHValidators.h" 30 | #import "GHNSDate+Utils.h" 31 | 32 | @implementation GHValidators 33 | 34 | + (BOOL)isEmailAddress:(NSString *)str { 35 | if (!str) return NO; 36 | 37 | NSString *emailRegexPattern = @"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]+$"; 38 | 39 | NSError *error = nil; 40 | NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:emailRegexPattern options:NSRegularExpressionCaseInsensitive error:&error]; 41 | 42 | NSAssert(!error, @"Error in regex: %@", error); 43 | 44 | NSUInteger matchCount = [regex numberOfMatchesInString:str options:0 range:NSMakeRange(0, [str length])]; 45 | 46 | return (matchCount == 1); 47 | } 48 | 49 | + (BOOL)isCreditCardNumber:(NSString *)numberString { 50 | NSInteger digitsLength = [numberString length]; 51 | if (digitsLength == 0) return NO; 52 | NSInteger digitsArray[digitsLength]; 53 | for (NSInteger i = 0; i < digitsLength; i++) { 54 | NSString *digitString = [numberString substringWithRange:NSMakeRange(i, 1)]; 55 | // Check for 0 manually since integerValue returns 0 on failure 56 | if ([digitString isEqual:@"0"]) { 57 | digitsArray[i] = 0; 58 | } else { 59 | NSInteger digit = [digitString integerValue]; 60 | if (digit == 0) return NO; 61 | digitsArray[i] = digit; 62 | } 63 | } 64 | 65 | for (NSInteger i = digitsLength - 2; i > -1; i-=2) { 66 | digitsArray[i] *= 2; 67 | if (digitsArray[i] > 9) { 68 | digitsArray[i] -= 9; 69 | } 70 | } 71 | 72 | NSInteger total = 0; 73 | for (NSInteger i = 0; i < digitsLength; i++) { 74 | total += digitsArray[i]; 75 | } 76 | 77 | return (total % 10 == 0); 78 | } 79 | 80 | + (BOOL)isCreditCardExpiration:(NSString *)expiration date:(NSDate *)date { 81 | if (!expiration) return NO; 82 | NSArray *split = [expiration componentsSeparatedByString:@"/"]; 83 | if ([split count] != 2) return NO; 84 | NSInteger month = [[split objectAtIndex:0] integerValue]; 85 | NSInteger year = [[split objectAtIndex:1] integerValue]; 86 | if (month <= 0 || year <= 0) return NO; 87 | if (month > 12) return NO; 88 | 89 | if (year >= 0 && year < 100) year += 2000; // TODO(gabe): Y3K 90 | 91 | if (date) { 92 | // Check >= date. 93 | // Comparing date components like this isn't completely precise across time zones 94 | // so we ignore the first day of the month. 95 | NSInteger currentYear = [date gh_year]; 96 | if (year < currentYear) return NO; 97 | if (year == currentYear && month < [date gh_month] && [date gh_day] != 1) { 98 | return NO; 99 | } 100 | } 101 | return YES; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /GHKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 rel.me. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /GHKit/iOS/GHUIColor+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Utils.h 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 12/18/08. 6 | // Copyright 2008 Gabriel Handford 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | #import 32 | 33 | /*! 34 | Macro for UIColor from hex. 35 | UIColor *color = GHUIColorFromRGB(0xBC1128); 36 | */ 37 | #define GHUIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] 38 | 39 | /*! 40 | Represents an RGBA value. 41 | */ 42 | typedef struct { 43 | CGFloat red; 44 | CGFloat green; 45 | CGFloat blue; 46 | CGFloat alpha; 47 | } GH_RGBA; 48 | 49 | /*! 50 | Represents an HSV value. 51 | 52 | HSV is the same as HSB. 53 | */ 54 | typedef struct { 55 | float hue; 56 | float saturation; 57 | float value; 58 | } GH_HSV; 59 | 60 | 61 | /*! 62 | Utilities for UIColor. 63 | 64 | @ingroup iPhone 65 | */ 66 | @interface UIColor(GHUtils) 67 | 68 | /*! 69 | RGBA value. 70 | 71 | @result RGBA 72 | */ 73 | - (GH_RGBA)gh_rgba; 74 | 75 | /*! 76 | HSV value. 77 | 78 | @result HSV 79 | */ 80 | - (GH_HSV)gh_hsv; 81 | 82 | /*! 83 | HSV value from RGB. 84 | 85 | @param red Red 86 | @param green Green 87 | @param blue Blue 88 | @result HSV 89 | */ 90 | + (GH_HSV)gh_hsvFromRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue; 91 | 92 | // See NSColor#getComponents: 93 | - (void)gh_getComponents:(CGFloat *)components; 94 | 95 | // See NSColor#numberOfComponents 96 | - (NSInteger)gh_numberOfComponents; 97 | 98 | // See NSColor#getRed:green:blue:alpha 99 | - (void)gh_getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha; 100 | 101 | - (UIColor *)gh_darkenColor:(CGFloat)value; 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /GHKit/iOS/GHUIColor+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Utils.m 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 12/18/08. 6 | // Copyright 2008 Gabriel Handford 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "GHUIColor+Utils.h" 31 | 32 | #import 33 | 34 | @implementation UIColor(GHUtils) 35 | 36 | // From http://www.easyrgb.com/index.php?X=MATH&H=20#text20 37 | + (GH_HSV)gh_hsvFromRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue { 38 | 39 | float min = fminf(fminf(red, green), blue); 40 | float max = fmaxf(fmaxf(red, green), blue); 41 | float delta = max-min; 42 | 43 | float hue = 0.0; 44 | float saturation = 0.0; 45 | float value = max; 46 | 47 | if (delta <= 1.0E-5) { 48 | hue = 0.0; 49 | saturation = 0.0; 50 | } else { 51 | saturation = delta / max; 52 | 53 | float deltaR = (((max - red) / 6.0) + (max/2.0)) / delta; 54 | float deltaG = (((max - green) / 6.0) + (max/2.0)) / delta; 55 | float deltaB = (((max - blue) / 6.0) + (max/2.0)) / delta; 56 | 57 | if (red <= (max+1.0E-5) && red >= (max-1.0E-5)) hue = deltaB - deltaG; 58 | else if (green <= (max+1.0E-5) && green >= (max-1.0E-5)) hue = (1.0/3.0) + deltaR - deltaB; 59 | else if (blue <= (max+1.0E-5) && blue >= (max-1.0E-5)) hue = (2.0/3.0) + deltaG - deltaR; 60 | 61 | if (hue < 0) hue += 1; 62 | if (hue > 1) hue -= 1; 63 | } 64 | 65 | GH_HSV hsv; 66 | hsv.hue = hue; 67 | hsv.saturation = saturation; 68 | hsv.value = value; 69 | return hsv; 70 | } 71 | 72 | - (GH_RGBA)gh_rgba { 73 | const CGFloat *components = CGColorGetComponents(self.CGColor); 74 | GH_RGBA color = { .red = 0, .green = 0, .blue = 0, .alpha = 1 }; 75 | CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor)); 76 | if (colorSpaceModel == kCGColorSpaceModelRGB) { 77 | color.red = components[0]; 78 | color.green = components[1]; 79 | color.blue = components[2]; 80 | color.alpha = components[3]; 81 | } else if (colorSpaceModel == kCGColorSpaceModelMonochrome) { 82 | color.red = components[0]; 83 | color.green = components[0]; 84 | color.blue = components[0]; 85 | color.alpha = components[1]; 86 | } else { 87 | NSAssert(NO, @"Unable to convert to RGBA from color space"); 88 | } 89 | return color; 90 | } 91 | 92 | - (GH_HSV)gh_hsv { 93 | GH_RGBA rgba = [self gh_rgba]; 94 | return [UIColor gh_hsvFromRed:rgba.red green:rgba.green blue:rgba.blue]; 95 | } 96 | 97 | - (void)gh_getComponents:(CGFloat *)components { 98 | memcpy(components, CGColorGetComponents(self.CGColor), CGColorGetNumberOfComponents(self.CGColor) * sizeof(CGFloat)); 99 | } 100 | 101 | - (NSInteger)gh_numberOfComponents { 102 | size_t num = CGColorGetNumberOfComponents(self.CGColor); 103 | return (NSInteger)num; 104 | } 105 | 106 | - (void)gh_getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha { 107 | const CGFloat *components = CGColorGetComponents(self.CGColor); 108 | *red = *green = *blue = 0.0; 109 | *alpha = 1.0; 110 | size_t num = CGColorGetNumberOfComponents(self.CGColor); 111 | if (num <= 2) { 112 | *red = components[0]; 113 | *green = components[0]; 114 | *blue = components[0]; 115 | if (num == 2) *alpha = components[1]; 116 | } else if (num >= 3) { 117 | *red = components[0]; 118 | *green = components[1]; 119 | *blue = components[2]; 120 | if (num >= 4) *alpha = components[3]; 121 | } 122 | } 123 | 124 | - (UIColor *)gh_darkenColor:(CGFloat)value { 125 | NSUInteger totalComponents = CGColorGetNumberOfComponents(self.CGColor); 126 | BOOL isGreyscale = (totalComponents == 2) ? YES : NO; 127 | 128 | CGFloat *oldComponents = (CGFloat *)CGColorGetComponents(self.CGColor); 129 | CGFloat newComponents[4]; 130 | 131 | if(isGreyscale) { 132 | newComponents[0] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value; 133 | newComponents[1] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value; 134 | newComponents[2] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value; 135 | newComponents[3] = oldComponents[1]; 136 | } 137 | else { 138 | newComponents[0] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value; 139 | newComponents[1] = oldComponents[1] - value < 0.0f ? 0.0f : oldComponents[1] - value; 140 | newComponents[2] = oldComponents[2] - value < 0.0f ? 0.0f : oldComponents[2] - value; 141 | newComponents[3] = oldComponents[3]; 142 | } 143 | 144 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 145 | CGColorRef newColor = CGColorCreate(colorSpace, newComponents); 146 | CGColorSpaceRelease(colorSpace); 147 | 148 | UIColor *retColor = [UIColor colorWithCGColor:newColor]; 149 | CGColorRelease(newColor); 150 | 151 | return retColor; 152 | } 153 | 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /GHKit/iOS/GHUIImage+Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHUIImage+Utils.h 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 11/26/13. 6 | // Copyright (c) 2013 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface UIImage (GHUtils) 14 | 15 | + (NSString *)gh_mimeType:(NSString *)name; 16 | 17 | - (UIImage *)gh_imageFlippedHorizontal; 18 | 19 | - (UIImage *)gh_imageMaskWithColor:(UIColor *)maskColor; 20 | 21 | - (UIImage *)gh_imageByRotatingImageUpright; 22 | 23 | - (UIImage *)gh_resizedImageInSize:(CGSize)size contentMode:(UIViewContentMode)contentMode opaque:(BOOL)opaque; 24 | 25 | - (UIImage *)gh_croppedImageFromFrame:(CGRect)frame; 26 | 27 | + (UIImage *)gh_imageFromView:(UIView *)view; 28 | 29 | + (UIImage *)gh_imageFromDrawOperations:(void(^)(CGContextRef context))drawOperations size:(CGSize)size opaque:(BOOL)opaque; 30 | 31 | - (UIImage *)gh_imageScaledToMaxWidth:(CGFloat)scaledToMaxWidth; 32 | 33 | - (UIImage *)gh_imageScaledToWidth:(CGFloat)scaledToWidth; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /GHKit/iOS/GHUIImage+Utils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHUIImage+Utils.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 11/26/13. 6 | // Copyright (c) 2013 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GHUIImage+Utils.h" 12 | #import "GHCGUtils.h" 13 | 14 | @implementation UIImage (GHUtils) 15 | 16 | - (UIImage *)gh_imageFlippedHorizontal { 17 | return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:UIImageOrientationUpMirrored]; 18 | } 19 | 20 | - (UIImage *)gh_imageMaskWithColor:(UIColor *)maskColor { 21 | CGRect imageRect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height); 22 | 23 | UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, self.scale); 24 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 25 | 26 | CGContextScaleCTM(ctx, 1.0f, -1.0f); 27 | CGContextTranslateCTM(ctx, 0.0f, -(imageRect.size.height)); 28 | 29 | CGContextClipToMask(ctx, imageRect, self.CGImage); 30 | CGContextSetFillColorWithColor(ctx, maskColor.CGColor); 31 | CGContextFillRect(ctx, imageRect); 32 | 33 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 34 | UIGraphicsEndImageContext(); 35 | 36 | return newImage; 37 | } 38 | 39 | + (NSString *)gh_mimeType:(NSString *)name { 40 | CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[name pathExtension], NULL); 41 | NSString *mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType)); 42 | CFRelease(UTI); 43 | return mimeType; 44 | } 45 | 46 | + (UIImage *)gh_imageFromDrawOperations:(void(^)(CGContextRef context))drawOperations size:(CGSize)size opaque:(BOOL)opaque { 47 | UIGraphicsBeginImageContextWithOptions(size, opaque, [[UIScreen mainScreen] scale]); 48 | CGContextRef context = UIGraphicsGetCurrentContext(); 49 | // Flip coordinate system, otherwise image will be drawn upside down 50 | CGContextTranslateCTM(context, 0, size.height); 51 | CGContextScaleCTM (context, 1.0, -1.0); 52 | drawOperations(context); 53 | UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext(); 54 | UIGraphicsEndImageContext(); 55 | return renderedImage; 56 | } 57 | 58 | + (UIImage *)gh_imageFromView:(UIView *)view { 59 | [view setNeedsDisplay]; 60 | UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, [[UIScreen mainScreen] scale]); 61 | CALayer *layer = view.layer; 62 | CGContextRef context = UIGraphicsGetCurrentContext(); 63 | [layer renderInContext:context]; 64 | UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); 65 | UIGraphicsEndImageContext(); 66 | return viewImage; 67 | } 68 | 69 | - (UIImage *)gh_croppedImageFromFrame:(CGRect)frame { 70 | CGFloat scale = [self scale]; 71 | frame.origin.x *= scale; 72 | frame.origin.y *= scale; 73 | frame.size.height *= scale; 74 | frame.size.width *= scale; 75 | CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, frame); 76 | UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale orientation:self.imageOrientation]; 77 | CGImageRelease(imageRef); 78 | return image; 79 | } 80 | 81 | - (UIImage *)gh_resizedImageInSize:(CGSize)size contentMode:(UIViewContentMode)contentMode opaque:(BOOL)opaque { 82 | CGRect imageRect = GHCGRectConvert(CGRectMake(0, 0, size.width, size.height), self.size, contentMode); 83 | 84 | UIImage *resizedImage = [UIImage gh_imageFromDrawOperations:^(CGContextRef context) { 85 | CGContextDrawImage(context, CGRectMake(0, 0, imageRect.size.width, imageRect.size.height), self.CGImage); 86 | } size:imageRect.size opaque:opaque]; 87 | 88 | return resizedImage; 89 | } 90 | 91 | - (UIImage *)gh_imageByRotatingImageUpright { 92 | UIGraphicsBeginImageContextWithOptions(self.size, YES, self.scale); 93 | CGContextRef context = UIGraphicsGetCurrentContext(); 94 | 95 | [self drawAtPoint:CGPointZero]; 96 | 97 | if (self.imageOrientation == UIImageOrientationRight) { 98 | // Phone is upright 99 | CGContextRotateCTM (context, -M_PI_2); 100 | } else if (self.imageOrientation == UIImageOrientationLeft) { 101 | // Phone is upside down 102 | CGContextRotateCTM (context, M_PI_2); 103 | } else if (self.imageOrientation == UIImageOrientationDown) { 104 | // Phone is in landscape with the volume buttons facing up 105 | CGContextRotateCTM (context, M_PI); 106 | } else if (self.imageOrientation == UIImageOrientationUp) { 107 | // Phone is in landscape with the volume buttons facing down 108 | } 109 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 110 | UIGraphicsEndImageContext(); 111 | 112 | return image; 113 | } 114 | 115 | - (UIImage *)gh_imageScaledToMaxWidth:(CGFloat)scaledToMaxWidth { 116 | CGFloat sourceWidth = self.size.width * self.scale; 117 | if (scaledToMaxWidth > sourceWidth) scaledToMaxWidth = sourceWidth; 118 | return [self gh_imageScaledToWidth:scaledToMaxWidth]; 119 | } 120 | 121 | - (UIImage *)gh_imageScaledToWidth:(CGFloat)scaledToWidth { 122 | CGFloat sourceWidth = self.size.width * self.scale; 123 | CGFloat sourceHeight = self.size.height * self.scale; 124 | CGRect sourceRect = CGRectMake(0, 0, sourceWidth, sourceHeight); 125 | CGFloat sourceRatio = sourceWidth / sourceHeight; 126 | 127 | CGFloat targetWidth = scaledToWidth; 128 | CGFloat targetHeight = roundf(targetWidth * (1.0/sourceRatio)); 129 | CGRect targetRect = CGRectMake(0, 0, targetWidth, targetHeight); 130 | 131 | UIGraphicsBeginImageContextWithOptions(targetRect.size, NO, 1.0); // 0.f for scale means "scale for device's main screen". 132 | CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect); // cropping happens here. 133 | UIImage *image = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage. 134 | CGImageRelease(sourceImg); 135 | [image drawInRect:targetRect]; // the actual scaling happens here, and orientation is taken care of automatically. 136 | image = UIGraphicsGetImageFromCurrentImageContext(); 137 | UIGraphicsEndImageContext(); 138 | return image; 139 | } 140 | 141 | @end -------------------------------------------------------------------------------- /GHKit/iOS/GHUIUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // GHUIUtils.h 3 | // GHUIKit 4 | // 5 | // Created by Gabriel Handford on 11/5/13. 6 | // Copyright (c) 2013 Gabriel Handford. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface GHUIUtils : NSObject 13 | 14 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font; 15 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font width:(CGFloat)width; 16 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font width:(CGFloat)width truncate:(BOOL)truncate; 17 | 18 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font size:(CGSize)size; 19 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font size:(CGSize)size truncate:(BOOL)truncate; 20 | 21 | + (NSInteger)fontSizeForText:(NSString *)text minFontSize:(NSInteger)minFontSize maxFontSize:(NSInteger)maxFontSize familyName:(NSString *)familyName size:(CGSize)size; 22 | 23 | + (void)drawText:(NSString *)text rect:(CGRect)rect font:(UIFont *)font color:(UIColor *)color alignment:(NSTextAlignment)alignment truncate:(BOOL)truncate; 24 | 25 | + (UIView *)subview:(UIView *)view forClass:(Class)class; 26 | 27 | + (NSMutableAttributedString *)joinAttributedStrings:(NSArray *)strings delimeter:(NSAttributedString *)delimeter; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /GHKit/iOS/GHUIUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHUIUtils.m 3 | // GHUIKit 4 | // 5 | // Created by Gabriel Handford on 11/5/13. 6 | // Copyright (c) 2013 Gabriel Handford. All rights reserved. 7 | // 8 | 9 | #import "GHUIUtils.h" 10 | 11 | @implementation GHUIUtils 12 | 13 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font { 14 | return [self sizeWithText:text font:font width:CGFLOAT_MAX]; 15 | } 16 | 17 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font width:(CGFloat)width { 18 | return [self sizeWithText:text font:font width:width truncate:NO]; 19 | } 20 | 21 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font width:(CGFloat)width truncate:(BOOL)truncate { 22 | return [self sizeWithText:text font:font size:(CGSize){width, CGFLOAT_MAX} truncate:truncate]; 23 | } 24 | 25 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font size:(CGSize)size { 26 | return [self sizeWithText:text font:font size:size truncate:NO]; 27 | } 28 | 29 | + (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font size:(CGSize)size truncate:(BOOL)truncate { 30 | if (!text) return CGSizeZero; 31 | NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName:font}]; 32 | NSStringDrawingOptions options = 0; 33 | options |= NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading; 34 | if (truncate) options |= NSStringDrawingTruncatesLastVisibleLine; 35 | CGRect rect = [attributedText boundingRectWithSize:size options:options context:nil]; 36 | return rect.size; 37 | } 38 | 39 | + (NSInteger)fontSizeForText:(NSString *)text minFontSize:(NSInteger)minFontSize maxFontSize:(NSInteger)maxFontSize familyName:(NSString *)familyName size:(CGSize)size { 40 | if (maxFontSize < minFontSize) return maxFontSize; 41 | if (!text) return 0; 42 | 43 | NSInteger fontSize = roundf((float)(minFontSize + maxFontSize) / 2.0); 44 | 45 | UIFont *font = [UIFont fontWithName:familyName size:fontSize]; 46 | CGSize textSize = [self sizeWithText:text font:font size:CGSizeMake(size.width, CGFLOAT_MAX)]; 47 | 48 | if (textSize.height >= (size.height + 10) && textSize.width >= (size.width + 10) && textSize.height <= (size.height) && textSize.width <= (size.width) ) { 49 | return fontSize; 50 | } else if (textSize.height > size.height || textSize.width > size.width) { 51 | return [self fontSizeForText:text minFontSize:minFontSize maxFontSize:(fontSize - 1) familyName:familyName size:size]; 52 | } else { 53 | return [self fontSizeForText:text minFontSize:(fontSize + 1) maxFontSize:maxFontSize familyName:familyName size:size]; 54 | } 55 | } 56 | 57 | + (void)drawText:(NSString *)text rect:(CGRect)rect font:(UIFont *)font color:(UIColor *)color alignment:(NSTextAlignment)alignment truncate:(BOOL)truncate { 58 | NSStringDrawingOptions options = 0; 59 | options |= NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading; 60 | if (truncate) options |= NSStringDrawingTruncatesLastVisibleLine; 61 | NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 62 | [paragraphStyle setAlignment:alignment]; 63 | [text drawWithRect:rect options:options attributes:@{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle, NSForegroundColorAttributeName:color} context:nil]; 64 | } 65 | 66 | + (UIView *)subview:(UIView *)view forClass:(Class)class { 67 | return [self _subview:view forClass:class iteration:0]; 68 | } 69 | 70 | + (UIView *)_subview:(UIView *)view forClass:(Class)class iteration:(NSInteger)iteration { 71 | if (iteration > 10) return nil; 72 | if ([view isKindOfClass:class]) return view; 73 | 74 | // Search breadth first since thats more likely 75 | for (UIView *subview in [view subviews]) { 76 | if ([subview isKindOfClass:class]) return subview; 77 | } 78 | ++iteration; 79 | for (UIView *subview in [view subviews]) { 80 | UIView *view = [GHUIUtils _subview:subview forClass:class iteration:iteration]; 81 | if (view) return view; 82 | } 83 | return nil; 84 | } 85 | 86 | + (NSMutableAttributedString *)joinAttributedStrings:(NSArray *)strings delimeter:(NSAttributedString *)delimeter { 87 | NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; 88 | for (NSInteger index = 0; index < strings.count; index++) { 89 | NSAttributedString *as = strings[index]; 90 | if (as.length > 0) { 91 | [text appendAttributedString:as]; 92 | if (delimeter && index < strings.count - 1) { 93 | [text appendAttributedString:delimeter]; 94 | } 95 | } 96 | } 97 | return text; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /GHKit/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Gabriel Handford 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GHKit 2 | 3 | The GHKit framework is a set of extensions and utilities for Mac OS X and iOS. 4 | 5 | ## Podfile 6 | 7 | pod "GHKit" 8 | 9 | ## Usage 10 | 11 | GHKit defines various categories and general purpose utilities. 12 | 13 | For example, parsing date strings, date math, string manipulations, URL dictionary formatting, etc. Some examples are below. 14 | 15 | All categories are namespaced with gh_ to avoid conflicts. 16 | 17 | ***Import:*** 18 | 19 | ```objc 20 | #import 21 | ``` 22 | 23 | 24 | ***Dates:*** 25 | 26 | `GHNSDate+Formatters.h`: Date parsers, formatting and formatters for ISO8601, RFC822, HTTP (RFC1123, RFC850, asctime) and since epoch. 27 | 28 | ```objc 29 | NSDate *date = [NSDate gh_parseISO8601:@"2010-10-07T04:25Z"]; 30 | NSString *dateString = [date gh_formatHTTP]; // Formatted like: Sun, 06 Nov 1994 08:49:37 GMT" 31 | NSDate *date = [NSDate gh_parseTimeSinceEpoch:@(1234567890)]; 32 | ``` 33 | 34 | `GHNSDate+Utils.h`: For time ago in words and date component arithmentic (adding days), tomorrow, yesterday, and more. 35 | 36 | ```objc 37 | NSDate *date = [NSDate date]; 38 | [date gh_isToday]; // YES 39 | [[date gh_yesterday] gh_isToday]; // NO 40 | 41 | date = [date gh_addDays:-1]; 42 | [date gh_wasYesterday]; // YES 43 | 44 | [date gh_timeAgo:NO]; // @"1 day" 45 | ``` 46 | 47 | ***Arrays:*** 48 | 49 | `GHNSArray+Utils.h`: Random object, safe object at index, uniq, compact 50 | 51 | ```objc 52 | [@[@(1), @(2), @(3)] gh_random]; // Random object 53 | [@[@(1), @(1), @(3)] gh_uniq]; // @[@(1), @(3)] 54 | [@[] gh_objectAtIndex:0]; // nil (Safe objectAtIndex) 55 | [@[@(1), NSNull.null] gh_compact]; // @[@(1)] 56 | ``` 57 | 58 | ***Dictionaries:*** 59 | 60 | `GHDictionary+Utils.h`: 61 | 62 | ```objc 63 | NSDictionary *dict = @{@"key1": @(2), @"key2": @(3.1), @"key3": @YES}; 64 | NSString *JSONString = [dict gh_toJSON:NSJSONWritingPrettyPrinted error:nil]; 65 | ``` 66 | 67 | ***Strings:*** 68 | 69 | `GHNSString+Utils.h`: Stripping, reversing, counting and more. 70 | 71 | ```objc 72 | [NSString gh_isBlank:@" "]; // YES 73 | [NSString gh_isBlank:nil]; // YES 74 | [@" some text " gh_strip]; // @"some text" 75 | [@" " gh_isPresent]; // NO 76 | [@"abc" gh_isPresent]; // YES 77 | [@" " gh_present]; // nil 78 | [@"some text" gh_present]; // @"some text" 79 | 80 | [@"abc" gh_reverse]; // @"cba" 81 | 82 | [@"ababababcde" gh_count:@"ab"]; // 4 (@"ab" appears 4 times) 83 | 84 | [NSString gh_localizedStringForTimeInterval:30]; // "half a minute" 85 | [NSString gh_abbreviatedStringForTimeInterval:30]; // @"30s" 86 | 87 | [@"WWW.test.com" gh_startsWith:@"www." options:NSCaseInsensitiveSearch]; // YES 88 | [@"foo:bar" gh_lastSplitWithString:@":" options:NSCaseInsensitiveSearch]; // @"bar" 89 | 90 | [@"e̊gâds" gh_characters]; // @[@"e̊", @"g", @"â", @"d", @"s"]; 91 | ``` 92 | 93 | ***URLs:*** 94 | 95 | `GHNSURL+Utils.h`: Encoding, escaping, parsing, splitting out or sorting query params, and more. 96 | 97 | ```objc 98 | NSDictionary *dict = [@"c=d&a=b" gh_queryStringToDictionary]; // Dictionary with a => b, c => d 99 | [NSDictionary gh_dictionaryToQueryString:dict sort:YES]; // @"a=b&c=d" 100 | ``` 101 | 102 | ***Colors:*** 103 | 104 | `GHUIColor+Utils.h`: Colors from hex, color space changes, darken. 105 | 106 | ```objc 107 | UIColor *color = GHUIColorFromRGB(0xBC1128); 108 | GH_HSV hsvColor = [color gh_hsv]; 109 | UIColor *darkenedColor = [color gh_darkenColor:0.1]; // Darken 10% 110 | ``` 111 | 112 | And more... 113 | -------------------------------------------------------------------------------- /Tests/GHDefinesTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHDefinesTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel on 10/23/14. 6 | // Copyright (c) 2014 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface GHDefinesTest : XCTestCase { } 15 | @end 16 | 17 | @implementation GHDefinesTest 18 | 19 | - (void)testEquals { 20 | XCTAssertTrue(GHEquals((id)nil, nil)); 21 | XCTAssertTrue(GHEquals(@(1), @(1))); 22 | XCTAssertFalse(GHEquals((id)nil, @(1))); 23 | XCTAssertFalse(GHEquals(@(1), nil)); 24 | } 25 | 26 | - (void)testBase64 { 27 | GHBase64StringFromNSData([NSData data]); 28 | GHNSDataFromBase64String(@""); 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Tests/GHNSStringEnumeratorTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHNSStringEnumeratorTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 6/12/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import 13 | 14 | @interface GHNSStringEnumeratorTest : XCTestCase {} 15 | @end 16 | 17 | @implementation GHNSStringEnumeratorTest 18 | 19 | - (void)testCharacterSet { 20 | NSString *string = @"matz can't\n patch blues"; 21 | GHNSStringEnumerator *enumerator = [[GHNSStringEnumerator alloc] initWithString:string 22 | separatorCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 23 | 24 | NSArray *results = [enumerator allObjects]; 25 | NSArray *expected = [NSArray arrayWithObjects:@"matz", @" ", @"can't", @"\n ", @"patch", @" ", @"blues", nil]; 26 | XCTAssertEqualObjects(results, expected); 27 | } 28 | 29 | - (void)testCharacterSet2 { 30 | NSString *string = @" matz can't\n patch blues"; 31 | GHNSStringEnumerator *enumerator = [[GHNSStringEnumerator alloc] initWithString:string 32 | separatorCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 33 | 34 | NSArray *results = [enumerator allObjects]; 35 | NSArray *expected = [NSArray arrayWithObjects:@" ", @"matz", @" ", @"can't", @"\n ", @"patch", @" ", @"blues", nil]; 36 | XCTAssertEqualObjects(results, expected); 37 | } 38 | 39 | - (void)testString { 40 | NSString *string = @"matz can't\n patch blues"; 41 | GHNSStringEnumerator *enumerator = [[GHNSStringEnumerator alloc] initWithString:string 42 | separatorString:@" "]; 43 | 44 | NSArray *results = [enumerator allObjects]; 45 | NSArray *expected = [NSArray arrayWithObjects:@"matz", @" ", @"can't\n", @" ", @"patch", @" ", @"blues", nil]; 46 | XCTAssertEqualObjects(results, expected); 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Tests/GHReversableDictionaryTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHReversableDictionaryTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/25/10. 6 | // Copyright 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface GHReversableDictionaryTest : XCTestCase { } 15 | @end 16 | 17 | @implementation GHReversableDictionaryTest 18 | 19 | - (void)test { 20 | GHReversableDictionary *dict = [[GHReversableDictionary alloc] initWithObjectsAndKeys: 21 | @"value1", @"key1", 22 | @"value2", @"key2", 23 | nil]; 24 | [dict setObject:@"value3" forKey:@"key3"]; 25 | 26 | XCTAssertEqualObjects([dict objectForKey:@"key1"], @"value1"); 27 | XCTAssertEqualObjects([dict keyForObject:@"value1"], @"key1"); 28 | XCTAssertEqualObjects([dict objectForKey:@"key3"], @"value3"); 29 | XCTAssertEqualObjects([dict keyForObject:@"value3"], @"key3"); 30 | 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Tests/GHValidatorsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHValidatorsTest.m 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 10/1/10. 6 | // Copyright 2010 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface GHValidatorsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation GHValidatorsTest 18 | 19 | - (void)testValidateEmail { 20 | XCTAssertTrue([GHValidators isEmailAddress:@"test@domain.com"]); 21 | XCTAssertFalse([GHValidators isEmailAddress:@"foo"]); 22 | XCTAssertFalse([GHValidators isEmailAddress:@""]); 23 | XCTAssertFalse([GHValidators isEmailAddress:nil]); 24 | XCTAssertFalse([GHValidators isEmailAddress:@"~gabrielh@gmail.com"]); 25 | XCTAssertTrue([GHValidators isEmailAddress:@"gabrielh@gmail.commmmmmm"]); 26 | } 27 | 28 | - (void)testValidateCreditCard { 29 | XCTAssertFalse([GHValidators isCreditCardNumber:@" "]); 30 | XCTAssertFalse([GHValidators isCreditCardNumber:@""]); 31 | XCTAssertFalse([GHValidators isCreditCardNumber:nil]); 32 | XCTAssertTrue([GHValidators isCreditCardNumber:@"49927398716"]); 33 | XCTAssertFalse([GHValidators isCreditCardNumber:@"1234"]); 34 | XCTAssertFalse([GHValidators isCreditCardNumber:@"abc"]); 35 | } 36 | 37 | - (void)testValidateCreditCardExpiration { 38 | NSDate *date = [NSDate gh_dateWithDay:1 month:12 year:2011 timeZone:nil]; 39 | 40 | // Valid 41 | XCTAssertTrue([GHValidators isCreditCardExpiration:@"01/12" date:date]); 42 | XCTAssertTrue([GHValidators isCreditCardExpiration:@"1/12" date:date]); 43 | XCTAssertTrue([GHValidators isCreditCardExpiration:@"1/2012" date:date]); 44 | XCTAssertTrue([GHValidators isCreditCardExpiration:@"01/12" date:date]); 45 | 46 | // Empty 47 | XCTAssertFalse([GHValidators isCreditCardExpiration:@" " date:date]); 48 | XCTAssertFalse([GHValidators isCreditCardExpiration:@"" date:date]); 49 | XCTAssertFalse([GHValidators isCreditCardExpiration:nil date:date]); 50 | 51 | // Expired but valid on the one day 52 | XCTAssertTrue([GHValidators isCreditCardExpiration:@"11/11" date:date]); 53 | NSDate *nextDay = [NSDate gh_dateWithDay:2 month:12 year:2011 timeZone:nil]; 54 | XCTAssertFalse([GHValidators isCreditCardExpiration:@"11/11" date:nextDay]); 55 | 56 | // Invalid month or year 57 | XCTAssertFalse([GHValidators isCreditCardExpiration:@"13/12" date:date]); 58 | XCTAssertFalse([GHValidators isCreditCardExpiration:@"0/12" date:date]); 59 | 60 | // Expired 61 | XCTAssertFalse([GHValidators isCreditCardExpiration:@"1/2010" date:date]); 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/NSArrayUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArrayUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/9/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSArrayUtilsTest : XCTestCase { } 15 | @end 16 | 17 | 18 | @implementation NSArrayUtilsTest 19 | 20 | - (void)testRandom { 21 | NSSet *set = [NSSet setWithObjects:@"1", @"2", @"3", nil]; 22 | 23 | NSString *obj = [[set allObjects] gh_randomObject]; 24 | XCTAssertTrue([set containsObject:obj]); 25 | } 26 | 27 | - (void)testReversed { 28 | NSArray *array = [[NSArray arrayWithObjects:@"1", @"2", @"3", nil] gh_arrayByReversingArray]; 29 | NSArray *expected = [NSArray arrayWithObjects:@"3", @"2", @"1", nil]; 30 | XCTAssertEqualObjects(array, expected); 31 | } 32 | 33 | - (void)testSubarrayWithRange { 34 | NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil]; 35 | 36 | // Test 0 length 37 | NSArray *subarray1 = [array gh_subarrayWithRange:NSMakeRange(0, 0)]; 38 | NSArray *expected1 = [NSArray array]; 39 | XCTAssertEqualObjects(subarray1, expected1); 40 | 41 | // Test equal length 42 | NSArray *subarray2 = [array gh_subarrayWithRange:NSMakeRange(0, 3)]; 43 | XCTAssertEqualObjects(subarray2, array); 44 | 45 | // Test normal 46 | NSArray *subarray3 = [array gh_subarrayWithRange:NSMakeRange(1, 2)]; 47 | NSArray *expected3 = [NSArray arrayWithObjects:@"2", @"3", nil]; 48 | XCTAssertEqualObjects(subarray3, expected3); 49 | 50 | // Test length overflow 51 | NSArray *subarray4 = [array gh_subarrayWithRange:NSMakeRange(1, 4)]; 52 | NSArray *expected4 = [NSArray arrayWithObjects:@"2", @"3", nil]; 53 | XCTAssertEqualObjects(subarray4, expected4); 54 | 55 | // Test location overflow -> nil 56 | NSArray *subarray5 = [array gh_subarrayWithRange:NSMakeRange(3, 0)]; 57 | XCTAssertNil(subarray5); 58 | } 59 | 60 | - (void)testSubarrayFromLocation { 61 | NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil]; 62 | 63 | // Test 0 position 64 | NSArray *subarray1 = [array gh_subarrayFromLocation:0]; 65 | XCTAssertEqualObjects(subarray1, array); 66 | 67 | NSArray *subarray2 = [array gh_subarrayFromLocation:1]; 68 | NSArray *expected2 = [NSArray arrayWithObjects:@"2", @"3", nil]; 69 | XCTAssertEqualObjects(subarray2, expected2); 70 | 71 | NSArray *subarray3 = [array gh_subarrayFromLocation:2]; 72 | NSArray *expected3 = [NSArray arrayWithObjects:@"3", nil]; 73 | XCTAssertEqualObjects(subarray3, expected3); 74 | 75 | // Test location == count 76 | NSArray *subarray4 = [array gh_subarrayFromLocation:3]; 77 | NSArray *expected4 = [NSArray array]; 78 | XCTAssertEqualObjects(subarray4, expected4); 79 | 80 | // Test location overflow 81 | NSArray *subarray5 = [array gh_subarrayFromLocation:4]; 82 | NSArray *expected5 = [NSArray array]; 83 | XCTAssertEqualObjects(subarray5, expected5); 84 | } 85 | 86 | - (void)testCompact { 87 | NSArray *array = [[NSArray arrayWithObjects:@"1", @"2", [NSNull null], nil] gh_compact]; 88 | NSArray *expected = [NSArray arrayWithObjects:@"1", @"2", nil]; 89 | XCTAssertEqualObjects(array, expected); 90 | 91 | NSArray *array2 = [NSArray arrayWithObjects:@"1", @"2", @"3", nil]; 92 | NSArray *compactArray2 = [array2 gh_compact]; 93 | NSArray *expected2 = [NSArray arrayWithObjects:@"1", @"2", @"3", nil]; 94 | XCTAssertEqualObjects(compactArray2, expected2); 95 | XCTAssertEqual(array2, compactArray2); // Will also be == 96 | 97 | NSArray *array3 = [NSArray array]; 98 | NSArray *compactArray3 = [array3 gh_compact]; 99 | NSArray *expected3 = [NSArray array]; 100 | XCTAssertEqualObjects(compactArray3, expected3); 101 | XCTAssertEqual(array3, compactArray3); // Will also be == 102 | 103 | NSArray *array4 = [NSArray arrayWithObject:[NSNull null]]; 104 | NSArray *compactArray4 = [array4 gh_compact]; 105 | NSArray *expected4 = [NSArray array]; 106 | XCTAssertEqualObjects(compactArray4, expected4); 107 | } 108 | 109 | - (void)testObjectAtIndex { 110 | id value1 = [[NSArray array] gh_objectAtIndex:0 withDefault:@"default"]; 111 | XCTAssertEqualObjects(value1, @"default"); 112 | 113 | id value2 = [[NSArray array] gh_objectAtIndex:1]; 114 | XCTAssertNil(value2); 115 | 116 | id value3 = [[NSArray array] gh_objectAtIndex:-1]; 117 | XCTAssertNil(value3); 118 | 119 | id value4 = [[NSArray arrayWithObject:@"0"] gh_objectAtIndex:0]; 120 | XCTAssertEqualObjects(value4, @"0"); 121 | 122 | id value5 = [[NSArray arrayWithObject:@"0"] gh_objectAtIndex:1]; 123 | XCTAssertNil(value5); 124 | 125 | id value6 = [[NSArray arrayWithObject:@"0"] gh_objectAtIndex:-1]; 126 | XCTAssertNil(value6); 127 | 128 | id value7 = [[NSArray arrayWithObjects:@"0", @"1", nil] gh_objectAtIndex:2]; 129 | XCTAssertNil(value7); 130 | 131 | id value8 = [[NSArray arrayWithObjects:@"0", @"1", nil] gh_objectAtIndex:1]; 132 | XCTAssertEqualObjects(value8, @"1"); 133 | } 134 | 135 | - (void)testJSON { 136 | NSString *JSONString = [@[@"string", @(2), @(3.1), @YES] gh_toJSON:0 error:nil]; 137 | XCTAssertEqualObjects(@"[\"string\",2,3.1,true]", JSONString); 138 | } 139 | 140 | - (void)testUniq { 141 | NSArray *uniq = [@[@(1), @(1), @(3)] gh_uniq]; 142 | NSArray *expected = @[@(1), @(3)]; 143 | XCTAssertEqualObjects(uniq, expected); 144 | } 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /Tests/NSDataUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDataUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/14/14. 6 | // Copyright (c) 2014 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSDataUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSDataUtilsTest 18 | 19 | - (void)testHexString { 20 | NSString *testString = @"test string"; 21 | NSData *data = [testString dataUsingEncoding:NSUTF8StringEncoding]; 22 | XCTAssertEqualObjects(@"7465737420737472696E67", [data gh_hexString]); 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Tests/NSDateParsingTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDateParsingTest.m 3 | // GHKit 4 | // 5 | // Created by Gabe on 6/30/08. 6 | // Copyright 2008 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSDateParsingTest : XCTestCase { } 15 | @end 16 | 17 | @interface NSDate (Private) 18 | - (id)initWithString:(NSString *)s; 19 | @end 20 | 21 | @implementation NSDateParsingTest 22 | 23 | - (void)testFormatRFC822 { 24 | NSString *rfc822 = @"Sun, 06 Nov 1994 08:49:37 +0000"; 25 | NSDate *date = [[NSDate alloc] initWithString:@"1994-11-06 08:49:37 +0000"]; 26 | NSString *formatted = [date gh_formatRFC822]; 27 | XCTAssertEqualObjects(formatted, rfc822); 28 | } 29 | 30 | - (void)testParseHTTPDate { 31 | NSDate *date = [[NSDate alloc] initWithString:@"1994-11-06 08:49:37 +0000"]; 32 | 33 | NSString *rfc1123 = @"Sun, 06 Nov 1994 08:49:37 GMT"; //; RFC 822, updated by RFC 1123 34 | NSString *rfc850 = @"Sunday, 06-Nov-94 08:49:37 GMT"; //; RFC 850, obsoleted by RFC 1036 35 | NSString *ascTime = @"Sun Nov 6 08:49:37 1994"; //; ANSI C's asctime() format 36 | 37 | NSDate *parsed = nil; 38 | 39 | parsed = [NSDate gh_parseHTTP:rfc1123]; 40 | XCTAssertEqualObjects(parsed, date); 41 | 42 | parsed = [NSDate gh_parseHTTP:rfc850]; 43 | XCTAssertEqualObjects(parsed, date); 44 | 45 | parsed = [NSDate gh_parseHTTP:ascTime]; 46 | XCTAssertEqualObjects(parsed, date); 47 | } 48 | 49 | - (void)testOffset { 50 | // Set local time zone to eastern time 51 | [NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithName:@"America/New_York"]]; 52 | NSDate *date = [NSDate date]; 53 | NSDate *newDate = [NSDate gh_parseTimeSinceEpoch:[NSNumber numberWithDouble:[date timeIntervalSince1970]] withDefault:nil offsetForTimeZone:[NSTimeZone timeZoneWithName:@"America/Los_Angeles"]]; 54 | // Times should be three hours (in seconds) apart 55 | XCTAssertEqualWithAccuracy(([newDate timeIntervalSince1970] - [date timeIntervalSince1970]), (3.0 * 60.0 * 60.0), 0.01); 56 | } 57 | 58 | - (void)testTimestamp { 59 | NSDate *date = [NSDate gh_parseTimeSinceEpoch:@(1234567890)]; 60 | XCTAssertEqualObjects(@(1234567890), [NSNumber numberWithUnsignedLongLong:[date timeIntervalSince1970]]); 61 | } 62 | 63 | - (void)testISO8601 { 64 | NSDate *date = [NSDate gh_parseISO8601:@"1997-07-16T19:20:30.045Z"]; 65 | XCTAssertEqual(869080830.045, [date timeIntervalSince1970]); 66 | 67 | NSDate *date2 = [NSDate gh_parseISO8601:@"1997-07-16T19:20:30Z"]; 68 | XCTAssertEqual((NSTimeInterval)869080830, [date2 timeIntervalSince1970]); 69 | 70 | NSDate *date3 = [NSDate gh_parseISO8601:@"2014-01-28T16:44:58-08:00"]; 71 | XCTAssertEqual((NSTimeInterval)1390956298, [date3 timeIntervalSince1970]); 72 | 73 | NSDate *date4 = [NSDate gh_parseISO8601:@"2014-01-28T16:44:58+01:00"]; 74 | XCTAssertEqual((NSTimeInterval)1390923898, [date4 timeIntervalSince1970]); 75 | 76 | NSDate *date5 = [NSDate gh_parseISO8601:@"2014-01-28T16:44:58.045+01:00"]; 77 | XCTAssertEqual((NSTimeInterval)1390923898.045, [date5 timeIntervalSince1970]); 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Tests/NSDateUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+UtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 2/18/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSDateUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSDateUtilsTest 18 | 19 | - (void)testYesterday { 20 | NSDate *date = [[NSDate date] dateByAddingTimeInterval:-(60 * 60 * 24)]; // This could fail daylight savings 21 | XCTAssertTrue([date gh_wasYesterday]); 22 | } 23 | 24 | - (void)testTomorrow { 25 | NSDate *date = [[NSDate date] dateByAddingTimeInterval:(60 * 60 * 24)]; // This could fail daylight savings 26 | XCTAssertTrue([date gh_isTomorrow]); 27 | } 28 | 29 | - (void)testWeekday { 30 | NSLog(@"[NSDate gh_tomorrow]: %@", [NSDate gh_tomorrow]); 31 | XCTAssertEqualObjects([[NSDate gh_yesterday] gh_weekday:nil], @"Yesterday"); 32 | XCTAssertEqualObjects([[NSDate gh_tomorrow] gh_weekday:nil], @"Tomorrow"); 33 | XCTAssertEqualObjects([[NSDate date] gh_weekday:nil], @"Today"); 34 | 35 | NSDate *date = [[NSDate date] gh_addDays:-3]; 36 | 37 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 38 | [dateFormatter setDateFormat:@"EEEE"]; 39 | NSString *weekday = [dateFormatter stringFromDate:date]; 40 | XCTAssertEqualObjects(weekday, [date gh_weekday:dateFormatter]); 41 | } 42 | 43 | - (void)testDate { 44 | NSInteger year = [[NSDate date] gh_year] - 1930; 45 | 46 | NSDate *date = [NSDate gh_dateWithDay:1 month:1 year:0 addDay:0 addMonth:0 addYear:-30 timeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; 47 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 48 | [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; 49 | [dateFormatter setDateStyle:NSDateFormatterShortStyle]; 50 | NSString *dateString = [dateFormatter stringFromDate:date]; 51 | NSString *expected = [NSString stringWithFormat:@"1/1/%ld", (long)year]; 52 | XCTAssertEqualObjects(dateString, expected); 53 | } 54 | 55 | - (void)testComponents { 56 | NSDate *date = [NSDate gh_dateWithDay:1 month:2 year:2012 timeZone:nil]; 57 | XCTAssertTrue([date gh_day] == 1); 58 | XCTAssertTrue([date gh_month] == 2); 59 | XCTAssertTrue([date gh_year] == 2012); 60 | } 61 | 62 | - (void)testMonthSymbolsForFormat { 63 | NSArray *monthSymbols = [NSDate gh_monthSymbols]; 64 | NSLog(@"%@", [monthSymbols description]); 65 | } 66 | 67 | @end -------------------------------------------------------------------------------- /Tests/NSDictionaryNSNullTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionaryNSNullTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/1/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSDictionaryNSNullTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSDictionaryNSNullTest 18 | 19 | - (void)testNilValues { 20 | NSDictionary *dict = [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:@"key1", nil, @"key2", @"value2", @"key3", nil, nil]; 21 | NSDictionary *expected = [NSDictionary dictionaryWithObjectsAndKeys:[NSNull null], @"key1", @"value2", @"key2", [NSNull null], @"key3", nil]; 22 | XCTAssertEqualObjects(dict, expected); 23 | } 24 | 25 | - (void)testPrematureNilKeyDoesntCrash { 26 | NSDictionary *dict = [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:@"key1", nil, nil, @"", @"", nil, nil]; 27 | NSDictionary *expected = [NSDictionary dictionaryWithObjectsAndKeys:[NSNull null], @"key1", nil]; 28 | XCTAssertEqualObjects(dict, expected); 29 | } 30 | 31 | //- (void)testValueForKey { 32 | // NSDictionary *dict = [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:@"key1", nil, @"key2", @"value2", @"key3", nil, nil]; 33 | // id value = [dict valueForKey:@"key1"]; 34 | // XCTAssertNil(value, @"Should handle in KVC"); 35 | //} 36 | 37 | - (void)testEmpty { 38 | NSDictionary *dict = [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:nil]; 39 | NSDictionary *expected = [NSDictionary dictionary]; 40 | XCTAssertEqualObjects(dict, expected); 41 | } 42 | 43 | - (NSDictionary *)_testVAList:(id)obj ignoreNil:(BOOL)ignoreNil keysAndObjects:(id)firstKey, ... { 44 | va_list args; 45 | va_start(args, firstKey); 46 | NSDictionary *dict = [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNilWithKey:firstKey args:args ignoreNil:ignoreNil]; 47 | va_end(args); 48 | return dict; 49 | } 50 | 51 | - (void)testVAList { 52 | NSDictionary *dict = [self _testVAList:nil ignoreNil:NO keysAndObjects:@"key1", nil, @"key2", @"value2", @"key3", nil, nil]; 53 | NSDictionary *expected = [NSDictionary dictionaryWithObjectsAndKeys:[NSNull null], @"key1", @"value2", @"key2", [NSNull null], @"key3", nil]; 54 | XCTAssertEqualObjects(dict, expected); 55 | 56 | NSDictionary *dict2 = [self _testVAList:nil ignoreNil:YES keysAndObjects:@"key1", nil, @"key2", @"value2", @"key3", nil, nil]; 57 | NSDictionary *expected2 = [NSDictionary dictionaryWithObjectsAndKeys:@"value2", @"key2", nil]; 58 | XCTAssertEqualObjects(dict2, expected2); 59 | 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Tests/NSDictionaryUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionaryUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 6/4/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSDictionaryUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSDictionaryUtilsTest 18 | 19 | - (void)testBoolValue { 20 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 21 | @"1", @"key1", 22 | [NSNumber numberWithBool:YES], @"key2", 23 | [NSNumber numberWithBool:NO], @"key3", 24 | [NSNull null], @"key4", 25 | nil]; 26 | 27 | XCTAssertTrue([[dict gh_boolValueForKey:@"key1"] boolValue]); 28 | XCTAssertTrue([[dict gh_boolValueForKey:@"key2"] boolValue]); 29 | XCTAssertFalse([[dict gh_boolValueForKey:@"key3"] boolValue]); 30 | XCTAssertFalse([[dict gh_boolValueForKey:@"key4"] boolValue]); 31 | } 32 | 33 | - (void)testDataValue { 34 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 35 | @"hwLWnp0tXoCzs+W0UdVPUaBSfiW1Q5B9m6xzDPFXvJqpqXvk1nnmMBNziXJWE4M823j8oSRl0uLhCAhxJW6ah7/a25DjGzwa", @"key1", 36 | nil]; 37 | NSData *data = [dict gh_dataAsBase64ForKey:@"key1" options:0]; 38 | NSData *expected = [[NSData alloc] initWithBase64EncodedString:@"hwLWnp0tXoCzs+W0UdVPUaBSfiW1Q5B9m6xzDPFXvJqpqXvk1nnmMBNziXJWE4M823j8oSRl0uLhCAhxJW6ah7/a25DjGzwa" options:0]; 39 | XCTAssertEqualObjects(data, expected); 40 | } 41 | 42 | - (void)testHasAllKeys { 43 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 44 | @"1", @"key1", 45 | [NSNumber numberWithBool:YES], @"key2", 46 | [NSNumber numberWithBool:NO], @"key3", 47 | [NSNull null], @"key4", 48 | nil]; 49 | 50 | BOOL b; 51 | b = [dict gh_hasAllKeys:nil]; 52 | XCTAssertTrue(b); 53 | b = [dict gh_hasAllKeys:@"key1", nil]; 54 | XCTAssertTrue(b); 55 | b = [dict gh_hasAllKeys:@"key1", @"key3", nil]; 56 | XCTAssertTrue(b); 57 | b = [dict gh_hasAllKeys:@"key1", @"key4", nil]; 58 | XCTAssertFalse(b); 59 | b = [dict gh_hasAllKeys:@"key1", @"key5", nil]; 60 | XCTAssertFalse(b); 61 | b = [dict gh_hasAllKeys:@"key5", @"key1", nil]; 62 | XCTAssertFalse(b); 63 | } 64 | 65 | - (void)testSubsetWithKeys { 66 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: 67 | @"1", @"key1", 68 | @"2", @"key2", 69 | nil]; 70 | 71 | NSDictionary *dictSubset = [dict gh_dictionarySubsetWithKeys:[NSArray arrayWithObject:@"key1"]]; 72 | NSDictionary *expected = [NSDictionary dictionaryWithObjectsAndKeys:@"1", @"key1", nil]; 73 | XCTAssertEqualObjects(dictSubset, expected); 74 | 75 | // Test missing key 76 | NSDictionary *dictSubset2 = [dict gh_dictionarySubsetWithKeys:[NSArray arrayWithObject:@"key3"]]; 77 | NSDictionary *expected2 = [NSDictionary dictionary]; 78 | XCTAssertEqualObjects(dictSubset2, expected2); 79 | } 80 | 81 | - (void)testCompact { 82 | NSMutableDictionary *dict = [@{@"key1": @"1", @"key2": [NSNull null]} mutableCopy]; 83 | 84 | NSMutableDictionary *expected = [@{@"key1": @"1"} mutableCopy]; 85 | 86 | NSDictionary *after = [dict gh_compactDictionary]; 87 | XCTAssertEqualObjects(after, expected); 88 | } 89 | 90 | - (void)testJSON { 91 | NSDictionary *dict = @{@"key1": @(2), @"key2": @(3.1), @"key3": @YES}; 92 | NSString *JSONString = [dict gh_toJSON:NSJSONWritingPrettyPrinted error:nil]; 93 | NSLog(@"%@", JSONString); 94 | NSDictionary *dict2 = [NSJSONSerialization JSONObjectWithData:[JSONString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; 95 | XCTAssertEqualObjects(dict, dict2); 96 | } 97 | 98 | @end -------------------------------------------------------------------------------- /Tests/NSErrorUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSErrorUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/7/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSErrorUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSErrorUtilsTest 18 | 19 | //- (void)testErrorFromException { 20 | // @try { 21 | // [NSException raise:NSGenericException format:@"my reason"]; 22 | // } @catch(NSException *e) { 23 | // NSError *error = [NSError gh_errorFromException:e]; 24 | // NSLog(@"Error: %@", error); 25 | // } 26 | //} 27 | 28 | - (void)testFullDescription { 29 | NSError *detailedError = [NSError errorWithDomain:@"Detail" code:-2 userInfo:nil]; 30 | 31 | NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:detailedError] forKey:@"NSDetailedErrors"]; 32 | NSError *error = [NSError errorWithDomain:@"Test" code:-1 userInfo:userInfo]; 33 | NSString *fullDescription = [error gh_fullDescription]; 34 | NSLog(@"Full description: %@", fullDescription); 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Tests/NSFileManagerUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSFileManager+UtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 3/9/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSFileManagerUtilsTest : XCTestCase { } 15 | @end 16 | 17 | 18 | @implementation NSFileManagerUtilsTest 19 | 20 | - (void)testEnsureDirectory { 21 | NSError *error = nil; 22 | NSString *path = [NSFileManager gh_temporaryFile:@"Test" deleteIfExists:YES error:&error]; 23 | if (error) XCTFail(@"Error: %@", error); 24 | BOOL success = [NSFileManager gh_ensureDirectoryExists:path created:nil error:&error]; 25 | if (error) XCTFail(@"Error: %@", error); 26 | XCTAssertTrue(success); 27 | } 28 | 29 | - (void)testFileSize { 30 | NSString *path = [NSFileManager gh_pathToResource:@"test.file"]; 31 | XCTAssertTrue([NSFileManager gh_exist:path]); 32 | NSNumber *fileSize = [NSFileManager gh_fileSize:path error:nil]; 33 | XCTAssertEqual([fileSize longValue], 10L); 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Tests/NSMutableArrayUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArrayUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/1/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSMutableArrayUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSMutableArrayUtilsTest 18 | 19 | - (void)testInsertObjectsAtIndex { 20 | NSArray *objects = [NSArray arrayWithObjects:@"2", @"3", nil]; 21 | 22 | NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1", nil]; 23 | [array gh_insertObjects:objects atIndex:1]; 24 | 25 | NSArray *expected = [NSArray arrayWithObjects:@"1", @"2", @"3", nil]; 26 | XCTAssertEqualObjects(array, expected); 27 | } 28 | 29 | - (void)testInsertObjectsAtIndexEmpty { 30 | NSArray *objects = [NSArray array]; 31 | 32 | NSMutableArray *array = [NSMutableArray array]; 33 | [array gh_insertObjects:objects atIndex:0]; 34 | 35 | NSArray *expected = [NSArray array]; 36 | XCTAssertEqualObjects(array, expected); 37 | } 38 | 39 | - (void)testCompact { 40 | NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1", @"2", [NSNull null], nil]; 41 | [array gh_mutableCompact]; 42 | NSMutableArray *expected = [NSMutableArray arrayWithObjects:@"1", @"2", nil]; 43 | XCTAssertEqualObjects(array, expected); 44 | 45 | NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil]; 46 | [array2 gh_mutableCompact]; 47 | NSMutableArray *expected2 = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil]; 48 | XCTAssertEqualObjects(array2, expected2); 49 | 50 | NSMutableArray *array3 = [NSMutableArray array]; 51 | [array3 gh_mutableCompact]; 52 | NSMutableArray *expected3 = [NSMutableArray array]; 53 | XCTAssertEqualObjects(array3, expected3); 54 | 55 | NSMutableArray *array4 = [NSMutableArray arrayWithObject:[NSNull null]]; 56 | [array4 gh_mutableCompact]; 57 | NSMutableArray *expected4 = [NSMutableArray array]; 58 | XCTAssertEqualObjects(array4, expected4); 59 | } 60 | 61 | - (void)testAddObjectIfNotNil { 62 | NSMutableArray *array = [NSMutableArray array]; 63 | [array gh_addObject:nil]; 64 | [array gh_addObject:@"1"]; 65 | XCTAssertTrue([array count] == 1); 66 | } 67 | 68 | - (void)testReplaceObject { 69 | NSUInteger index; 70 | 71 | NSMutableArray *emptyArray = [NSMutableArray array]; 72 | index = [emptyArray gh_replaceObject:@"1" withObject:nil]; 73 | XCTAssertEqual(index, (NSUInteger)NSNotFound); 74 | 75 | NSMutableArray *array = [NSMutableArray arrayWithObject:@"1"]; 76 | index = [array gh_replaceObject:@"1" withObject:@"2"]; 77 | XCTAssertEqual(index, (NSUInteger)0); 78 | NSMutableArray *expected1 = [NSMutableArray arrayWithObject:@"2"]; 79 | XCTAssertEqualObjects(array, expected1); 80 | 81 | index = [array gh_replaceObject:@"1" withObject:@"3"]; 82 | XCTAssertEqual(index, (NSUInteger)NSNotFound); 83 | XCTAssertEqualObjects(array, expected1); 84 | } 85 | 86 | - (void)testRemoveLastObject { 87 | NSMutableArray *array = [NSMutableArray arrayWithObject:@"1"]; 88 | id obj = [array gh_removeLastObject]; 89 | XCTAssertEqualObjects(obj, @"1"); 90 | NSMutableArray *expected = [NSMutableArray array]; 91 | XCTAssertEqualObjects(array, expected); 92 | XCTAssertNil([array gh_removeLastObject]); 93 | } 94 | 95 | @end 96 | 97 | -------------------------------------------------------------------------------- /Tests/NSMutableDictionaryUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionaryUtilsTest.m 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 7/1/10. 6 | // Copyright 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSMutableDictionaryUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSMutableDictionaryUtilsTest 18 | 19 | - (void)testMutableCompact { 20 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: 21 | @"1", @"key1", 22 | [NSNull null], @"key2", 23 | nil]; 24 | 25 | NSMutableDictionary *expected = [NSMutableDictionary dictionaryWithObjectsAndKeys: 26 | @"1", @"key1", 27 | nil]; 28 | 29 | [dict gh_mutableCompact]; 30 | XCTAssertEqualObjects(dict, expected); 31 | 32 | NSMutableDictionary *dict2 = [@{@"key2": NSNull.null, @"key1": @"1"} mutableCopy]; 33 | 34 | NSMutableDictionary *expected2 = [@{@"key1": @"1"} mutableCopy]; 35 | [dict2 gh_mutableCompact]; 36 | XCTAssertEqualObjects(dict2, expected2); 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Tests/NSNumberUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumberUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/9/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSNumberUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSNumberUtilsTest 18 | 19 | - (void)testHumanSize { 20 | long long l = 5028463290; 21 | NSNumber *n = [NSNumber numberWithLongLong:l]; 22 | NSString *sizeString = [n gh_humanSize]; 23 | XCTAssertEqualObjects(sizeString, @"4.68 GB"); 24 | 25 | long long maxll = 9223372036854775807; // LLONG_MAX 26 | NSNumber *mll = [NSNumber numberWithLongLong:maxll]; 27 | XCTAssertNotNil(mll); 28 | NSString *maxllSizeString = [mll gh_humanSize]; 29 | XCTAssertEqualObjects(maxllSizeString, @"8589934592.00 GB"); 30 | NSLog(@"maxllSizeString=%@", maxllSizeString); 31 | } 32 | 33 | - (void)testOrdinalize { 34 | XCTAssertEqualObjects([[NSNumber numberWithInteger:1] gh_ordinalize], @"1st"); 35 | XCTAssertEqualObjects([[NSNumber numberWithInteger:2] gh_ordinalize], @"2nd"); 36 | XCTAssertEqualObjects([[NSNumber numberWithInteger:3] gh_ordinalize], @"3rd"); 37 | XCTAssertEqualObjects([[NSNumber numberWithInteger:4] gh_ordinalize], @"4th"); 38 | XCTAssertEqualObjects([[NSNumber numberWithInteger:5] gh_ordinalize], @"5th"); 39 | XCTAssertEqualObjects([[NSNumber numberWithInteger:6] gh_ordinalize], @"6th"); 40 | XCTAssertEqualObjects([[NSNumber numberWithInteger:7] gh_ordinalize], @"7th"); 41 | XCTAssertEqualObjects([[NSNumber numberWithInteger:8] gh_ordinalize], @"8th"); 42 | XCTAssertEqualObjects([[NSNumber numberWithInteger:9] gh_ordinalize], @"9th"); 43 | XCTAssertEqualObjects([[NSNumber numberWithInteger:10] gh_ordinalize], @"10th"); 44 | XCTAssertEqualObjects([[NSNumber numberWithInteger:11] gh_ordinalize], @"11th"); 45 | XCTAssertEqualObjects([[NSNumber numberWithInteger:12] gh_ordinalize], @"12th"); 46 | XCTAssertEqualObjects([[NSNumber numberWithInteger:13] gh_ordinalize], @"13th"); 47 | XCTAssertEqualObjects([[NSNumber numberWithInteger:14] gh_ordinalize], @"14th"); 48 | XCTAssertEqualObjects([[NSNumber numberWithInteger:111] gh_ordinalize], @"111th"); 49 | XCTAssertEqualObjects([[NSNumber numberWithInteger:212] gh_ordinalize], @"212th"); 50 | XCTAssertEqualObjects([[NSNumber numberWithInteger:313] gh_ordinalize], @"313th"); 51 | XCTAssertEqualObjects([[NSNumber numberWithInteger:221] gh_ordinalize], @"221st"); 52 | XCTAssertEqualObjects([[NSNumber numberWithInteger:222] gh_ordinalize], @"222nd"); 53 | 54 | XCTAssertEqualObjects([NSNumber gh_ordinalize:1], @"1st"); 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Tests/NSObjectUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObjectUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 7/8/09. 6 | // Copyright 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @import GHKit; 13 | 14 | @interface NSObjectUtilsTest : XCTestCase { } 15 | @end 16 | 17 | @implementation NSObjectUtilsTest 18 | 19 | - (void)testNotNSNull { 20 | id foo = nil; 21 | XCTAssertFalse([foo gh_isNotNSNull]); 22 | foo = [NSNull null]; 23 | XCTAssertFalse([foo gh_isNotNSNull]); 24 | foo = @"1"; 25 | XCTAssertTrue([foo gh_isNotNSNull]); 26 | } 27 | 28 | @end -------------------------------------------------------------------------------- /Tests/NSStringTimeIntervalTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSStringTimeIntervalTest.m 3 | // 4 | // Created by Gabe on 6/6/08. 5 | // Copyright 2008 rel.me. Some rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @import GHKit; 12 | 13 | @interface NSStringTimeIntervalTest : XCTestCase { } 14 | @end 15 | 16 | @implementation NSStringTimeIntervalTest 17 | 18 | - (void)testStringForTimeInterval { 19 | XCTAssertEqualObjects(@"less than a minute", [NSString gh_stringForTimeInterval:29.0 includeSeconds:NO]); 20 | XCTAssertEqualObjects(@"1 minute", [NSString gh_stringForTimeInterval:30.0 includeSeconds:NO]); 21 | 22 | XCTAssertEqualObjects(@"less than 5 seconds", [NSString gh_stringForTimeInterval:4.0 includeSeconds:YES]); 23 | XCTAssertEqualObjects(@"less than 10 seconds", [NSString gh_stringForTimeInterval:5.0 includeSeconds:YES]); 24 | XCTAssertEqualObjects(@"less than 20 seconds", [NSString gh_stringForTimeInterval:19.0 includeSeconds:YES]); 25 | XCTAssertEqualObjects(@"half a minute", [NSString gh_stringForTimeInterval:29.0 includeSeconds:YES]); 26 | XCTAssertEqualObjects(@"less than a minute", [NSString gh_stringForTimeInterval:59.0 includeSeconds:YES]); 27 | XCTAssertEqualObjects(@"1 minute", [NSString gh_stringForTimeInterval:60.0 includeSeconds:YES]); 28 | 29 | XCTAssertEqualObjects(@"1 minute", [NSString gh_stringForTimeInterval:89.0 includeSeconds:YES]); 30 | XCTAssertEqualObjects(@"2 minutes", [NSString gh_stringForTimeInterval:90.0 includeSeconds:YES]); 31 | 32 | XCTAssertEqualObjects(@"44 minutes", [NSString gh_stringForTimeInterval:(44.0 * 60.0) includeSeconds:YES]); 33 | XCTAssertEqualObjects(@"about 1 hour", [NSString gh_stringForTimeInterval:(45.0 * 60.0) includeSeconds:YES]); 34 | XCTAssertEqualObjects(@"about 1 hour", [NSString gh_stringForTimeInterval:(89.0 * 60.0) includeSeconds:YES]); 35 | XCTAssertEqualObjects(@"about 2 hours", [NSString gh_stringForTimeInterval:(90.0 * 60.0) includeSeconds:YES]); 36 | XCTAssertEqualObjects(@"1 day", [NSString gh_stringForTimeInterval:(47.0 * 60.0 * 60.0) includeSeconds:YES]); 37 | XCTAssertEqualObjects(@"2 days", [NSString gh_stringForTimeInterval:(48.0 * 60.0 * 60.0) includeSeconds:YES]); 38 | XCTAssertEqualObjects(@"3 days", [NSString gh_stringForTimeInterval:(60.0 * 60.0 * 60.0) includeSeconds:YES]); 39 | XCTAssertEqualObjects(@"3 days", [NSString gh_stringForTimeInterval:(72.0 * 60.0 * 60.0) includeSeconds:YES]); 40 | XCTAssertEqualObjects(@"15 days", [NSString gh_stringForTimeInterval:(15.0 * 24.0 * 60.0 * 60.0) includeSeconds:YES]); 41 | XCTAssertEqualObjects(@"about 1 month", [NSString gh_stringForTimeInterval:(30.0 * 24.0 * 60.0 * 60.0) includeSeconds:YES]); 42 | XCTAssertEqualObjects(@"2 months", [NSString gh_stringForTimeInterval:(60.0 * 24.0 * 60.0 * 60.0) includeSeconds:YES]); 43 | 44 | XCTAssertEqualObjects(@"about 1 year", [NSString gh_stringForTimeInterval:(365.0 * 24.0 * 60.0 * 60.0) includeSeconds:YES]); 45 | XCTAssertEqualObjects(@"over 2 years", [NSString gh_stringForTimeInterval:(2.0 * 365.0 * 24.0 * 60.0 * 60.0) includeSeconds:YES]); 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Tests/NSURLUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURL+UtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel Handford on 1/13/09. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @import GHKit; 12 | 13 | @interface NSURLUtilsTest : XCTestCase { } 14 | @end 15 | 16 | @implementation NSURLUtilsTest 17 | 18 | - (void)testEncode { 19 | NSString *test1 = @"~!@#$%^&*(){}[]=:/,;?+'\"\\"; 20 | NSString *escaped1 = [NSURL gh_encode:test1]; 21 | NSString *expected1 = @"~!@#$%25%5E&*()%7B%7D%5B%5D=:/,;?+'%22%5C"; 22 | XCTAssertEqualObjects(escaped1, expected1); 23 | } 24 | 25 | - (void)testEncodeComponent { 26 | NSString *test1 = @"~!@#$%^&*(){}[]=:/,;?+'\"\\"; 27 | NSString *escaped1 = [NSURL gh_encodeComponent:test1]; 28 | NSString *expected1 = @"~!%40%23%24%25%5E%26*()%7B%7D%5B%5D%3D%3A%2F%2C%3B%3F%2B'%22%5C"; 29 | XCTAssertEqualObjects(escaped1, expected1); 30 | } 31 | 32 | - (void)testEscapeAll { 33 | NSString *test1 = @"~!@#$%^&*(){}[]=:/,;?+'\"\\~!*()'"; 34 | NSString *escaped1 = [NSURL gh_escapeAll:test1]; 35 | NSString *expected1 = @"%7E%21%40%23%24%25%5E%26%2A%28%29%7B%7D%5B%5D%3D%3A%2F%2C%3B%3F%2B%27%22%5C%7E%21%2A%28%29%27"; 36 | XCTAssertEqualObjects(escaped1, expected1); 37 | } 38 | 39 | - (void)testDictionaryToQueryString { 40 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"value1", @"key1", @"value2", @"key2", nil]; 41 | NSString *s = [NSURL gh_dictionaryToQueryString:dict sort:YES]; 42 | XCTAssertEqualObjects(s, @"key1=value1&key2=value2"); 43 | 44 | NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:@"value1", @"AAA", @"value2", @"BBB", @"value3", @"CCC", nil]; 45 | NSString *s2 = [NSURL gh_dictionaryToQueryString:dict2 sort:YES]; 46 | XCTAssertEqualObjects(s2, @"AAA=value1&BBB=value2&CCC=value3"); 47 | } 48 | 49 | - (void)testDictionaryWithObjectsToQueryString { 50 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:1], @"key1", @"[]", @"key2", nil]; 51 | NSString *s = [NSURL gh_dictionaryToQueryString:dict sort:YES]; 52 | XCTAssertEqualObjects(s, @"key1=1&key2=%5B%5D"); 53 | } 54 | 55 | - (void)testDictionaryWithNSNull { 56 | NSDictionary *dict = [NSDictionary gh_dictionaryWithKeysAndObjectsMaybeNil:@"key1", @"value1", @"key2", nil, nil]; 57 | NSString *s = [NSURL gh_dictionaryToQueryString:dict sort:YES]; 58 | XCTAssertEqualObjects(s, @"key1=value1"); 59 | } 60 | 61 | - (void)testQueryStringToDictionary { 62 | NSDictionary *dict = [NSURL gh_queryStringToDictionary:@"key1=value1&key2=value2"]; 63 | XCTAssertEqualObjects(@"value1", [dict objectForKey:@"key1"]); 64 | XCTAssertEqualObjects(@"value2", [dict objectForKey:@"key2"]); 65 | 66 | NSDictionary *dict2 = [NSURL gh_queryStringToDictionary:@"key1==value1&&key2=value2%20&key3=value3=more"]; 67 | XCTAssertEqualObjects(@"=value1", [dict2 objectForKey:@"key1"]); 68 | XCTAssertEqualObjects(@"value2 ", [dict2 objectForKey:@"key2"]); 69 | XCTAssertEqualObjects(@"value3=more", [dict2 objectForKey:@"key3"]); 70 | } 71 | 72 | - (void)testDeriveWithQuery { 73 | NSURL *URL = [NSURL URLWithString:@"http://api.yelp.com/path?key1=value1&key2=value2"]; 74 | NSURL *derivedURL = [URL gh_deriveWithQuery:@"key3=value3&key4=value4"]; 75 | XCTAssertEqualObjects([derivedURL description], @"http://api.yelp.com/path?key3=value3&key4=value4"); 76 | } 77 | 78 | - (void)testDeriveComplexWithQuery { 79 | NSURL *URL = [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?key1=value1&key2=value2#myfrag"]; 80 | NSURL *derivedURL = [URL gh_deriveWithQuery:@"key3=value3&key4=value4"]; 81 | XCTAssertEqualObjects([derivedURL description], @"https://user:pass@api.yelp.com:400/path?key3=value3&key4=value4#myfrag"); 82 | } 83 | 84 | - (void)testCanonical { 85 | NSURL *URL = [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?b=c&a=d#myfrag"]; 86 | NSURL *canonical = [URL gh_canonical]; 87 | XCTAssertEqualObjects(canonical, [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?a=d&b=c#myfrag"]); 88 | 89 | NSURL *URL2 = [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?b=c&a=d&ignore=ignored#myfrag"]; 90 | NSURL *canonical2 = [URL2 gh_canonicalWithIgnore:[NSArray arrayWithObject:@"ignore"]]; 91 | XCTAssertEqualObjects(canonical2, [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?a=d&b=c#myfrag"]); 92 | } 93 | 94 | - (void)testFilter { 95 | NSURL *URL = [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?b=c&a=d&ignore=ignored#myfrag"]; 96 | NSURL *filtered = [URL gh_filterQueryParams:[NSArray arrayWithObjects:@"ignore", @"ignore2", nil] sort:YES]; 97 | XCTAssertEqualObjects(filtered, [NSURL URLWithString:@"https://user:pass@api.yelp.com:400/path?a=d&b=c#myfrag"]); 98 | } 99 | 100 | - (void)testQueryDictionaryWithArray { 101 | NSArray *array1 = [NSArray arrayWithObjects:@"va", @"vb", @"vc", nil]; 102 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:array1, @"key1", @"value2", @"key2", nil]; 103 | NSString *s = [NSURL gh_dictionaryToQueryString:dict sort:YES]; 104 | XCTAssertEqualObjects(s, @"key1=va%2Cvb%2Cvc&key2=value2"); 105 | } 106 | 107 | - (void)testQueryDictionaryWithSet { 108 | NSSet *set1 = [NSSet setWithObjects:@"va", @"vb", nil]; 109 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:set1, @"key1", nil]; 110 | NSString *s = [NSURL gh_dictionaryToQueryString:dict sort:YES]; 111 | XCTAssertTrue([s isEqualToString:@"key1=va%2Cvb"] || [s isEqualToString:@"key1=vb%2Cva"]); 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /Tests/iOS/GHUIColorUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHUIColor+UtilsTest.m 3 | // GHKitIPhone 4 | // 5 | // Created by Gabriel Handford on 12/18/08. 6 | // Copyright 2008 Gabriel Handford 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | #import 33 | 34 | @interface GHUIColorUtilsTest : XCTestCase { } 35 | @end 36 | 37 | @implementation GHUIColorUtilsTest 38 | 39 | - (void)testHSV { 40 | UIColor *color = [[UIColor alloc] initWithRed:0.20 green:0.4 blue:0.6 alpha:1.0]; 41 | GH_HSV hsv = [color gh_hsv]; 42 | // h=0.58, s=0.67, v=0.60 43 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", hsv.hue]), @"0.58"); 44 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", hsv.saturation]), @"0.67"); 45 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", hsv.value]), @"0.60"); 46 | } 47 | 48 | 49 | - (void)testHSVRed { 50 | UIColor *color = [[UIColor alloc] initWithRed:1.0 green:0.0 blue:0.0 alpha:1.0]; 51 | GH_HSV hsv = [color gh_hsv]; 52 | // h=0.00, s=1.00, v=1.00 53 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", hsv.hue]), @"0.00"); 54 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", hsv.saturation]), @"1.00"); 55 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", hsv.value]), @"1.00"); 56 | } 57 | 58 | - (void)testGetComponents { 59 | CGFloat info[4]; 60 | [[UIColor magentaColor] gh_getComponents:info]; 61 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", info[0]]), @"1.00"); 62 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", info[1]]), @"0.00"); 63 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", info[2]]), @"1.00"); 64 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", info[3]]), @"1.00"); 65 | } 66 | 67 | - (void)testGetRGB { 68 | CGFloat red, green, blue, alpha; 69 | [[UIColor magentaColor] gh_getRed:&red green:&green blue:&blue alpha:&alpha]; 70 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", red]), @"1.00"); 71 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", green]), @"0.00"); 72 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", blue]), @"1.00"); 73 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", alpha]), @"1.00"); 74 | 75 | [[UIColor grayColor] gh_getRed:&red green:&green blue:&blue alpha:&alpha]; 76 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", red]), @"0.50"); 77 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", green]), @"0.50"); 78 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", blue]), @"0.50"); 79 | XCTAssertEqualObjects(([NSString stringWithFormat:@"%0.2f", alpha]), @"1.00"); 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Tests/iOS/GHUIImageUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // GHUIImageUtilsTest.m 3 | // GHKit 4 | // 5 | // Created by Gabriel on 1/27/16. 6 | // Copyright © 2016 rel.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GHUIImageUtilsTest : XCTestCase 12 | @end 13 | 14 | @implementation GHUIImageUtilsTest 15 | 16 | // TODO 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Tests/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | --------------------------------------------------------------------------------