├── README ├── Mac OS X ├── ShellTask.h ├── ShellTask.m ├── NSPopUpButtonAdditions.h ├── AutomatorActionCategories.h ├── NSPopUpButtonAdditions.m ├── EscapableTextField.h ├── EscapableTextField.m ├── MacOSXCategories.h ├── AutomatorActionCategories.m ├── WorkflowCategories.h ├── MacOSXCategories.m ├── ForEach.h └── WorkflowCategories.m ├── NilCategories.h ├── NSObject+CleanUpProperties.h ├── NilCategories.m ├── VTPG_Common.h ├── SynthesizeSingleton.h ├── Categories.h ├── NSObject+CleanUpProperties.m ├── VTPG_Common.m └── Categories.m /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Mac OS X/ShellTask.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VTPG/CommonCode/HEAD/Mac OS X/ShellTask.h -------------------------------------------------------------------------------- /Mac OS X/ShellTask.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VTPG/CommonCode/HEAD/Mac OS X/ShellTask.m -------------------------------------------------------------------------------- /Mac OS X/NSPopUpButtonAdditions.h: -------------------------------------------------------------------------------- 1 | #import "NSPopUpButtonAdditions.h" 2 | 3 | @interface NSPopUpButton (VTPGAdditions) 4 | - (void) selectItemWithRepresentedObject:(id)representedObject; 5 | @end 6 | -------------------------------------------------------------------------------- /Mac OS X/AutomatorActionCategories.h: -------------------------------------------------------------------------------- 1 | // Created by Vincent Gable on 1/13/09. 2 | // Copyright 2009 Vincent Gable. All rights reserved. 3 | // 4 | 5 | @interface NSDictionary (AutomatorErrors) 6 | + (NSDictionary*) errorInfoWithMessage:(NSString*)msg errorNumber:(int)err; 7 | + (NSDictionary*) errorInfoWithMessage:(NSString*)msg; 8 | @end 9 | -------------------------------------------------------------------------------- /Mac OS X/NSPopUpButtonAdditions.m: -------------------------------------------------------------------------------- 1 | #import "NSPopUpButtonAdditions.h" 2 | 3 | 4 | @implementation NSPopUpButton (VTPGAdditions) 5 | 6 | - (void) selectItemWithRepresentedObject:(id)representedObject; 7 | { 8 | NSInteger itemIndex = [self indexOfItemWithRepresentedObject:representedObject]; 9 | if(itemIndex >= 0) 10 | [self selectItemAtIndex:itemIndex]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Mac OS X/EscapableTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // EscapableTextField.h 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 10/2/07. 6 | // Copyright 2007 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface EscapableTextField : NSTextField { 13 | 14 | } 15 | 16 | //Requires 10.3 17 | //This method will be called on esc, or cmd-. (period) 18 | - (void)cancelOperation:(id)sender; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Mac OS X/EscapableTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // EscapableTextField.m 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 10/2/07. 6 | // Copyright 2007 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import "EscapableTextField.h" 10 | 11 | 12 | @implementation EscapableTextField 13 | 14 | - (void)cancelOperation:(id)sender 15 | { 16 | if ([[self delegate] respondsToSelector:@selector(cancelOperation:)]) 17 | [[self delegate] cancelOperation:self]; 18 | } 19 | 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Mac OS X/MacOSXCategories.h: -------------------------------------------------------------------------------- 1 | // 2 | // Categories.h 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 5/28/07. 6 | // Copyright 2007 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSAppleScript (VTPGCategories) 12 | //executes the NSAppleScript, and returns the result as an NSString. 13 | - (NSString*) executeWithStringResult; 14 | 15 | ////executes the NSAppleScript, and returns the result as an NSString, if an error occurs, it's description is put in error 16 | - (NSString*) executeWithStringResultAndReturnError:(NSDictionary**)error; 17 | @end 18 | -------------------------------------------------------------------------------- /NilCategories.h: -------------------------------------------------------------------------------- 1 | // 2 | // NilCategories.h 3 | // 4 | // Created by Vincent Gable on 2009-04-21 5 | // Copyright 2009 Vincent Gable. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #pragma mark UUID 11 | @interface NSString (NilCategories) 12 | - (NSURL*) convertToURL; 13 | - (NSURL*) convertToURLRelitiveToURL:(NSURL*)baseURL; 14 | @end 15 | 16 | @interface NSArray (NilCategories) 17 | - (id) objectOrNilAtIndex:(NSUInteger)index; 18 | @end 19 | 20 | @interface NSMutableDictionary (NilCategories) 21 | - (void) setObjectToObjectForKey:(id)key inDictionary:(NSDictionary*)otherDictionary; 22 | @end; 23 | -------------------------------------------------------------------------------- /NSObject+CleanUpProperties.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+CleanUpProperties.h 3 | // 4 | // Created by Vincent Gable on 12/4/08. 5 | // vincent.gable@gmail.com 6 | // http://vincentgable.com 7 | // Copyright 2008 Vincent Gable. 8 | // You are free to use this code however you like. 9 | // But I would appreciate hearing what you use it for :-) 10 | // 11 | 12 | #import 13 | #import 14 | 15 | @interface NSObject(CleanUpProperties) 16 | - (void) setEveryObjCObjectPropertyToNil; 17 | 18 | #define PROPERTY_ONLY_OBJECT_DEALLOC \ 19 | - (void) dealloc { \ 20 | [self setEveryObjCObjectPropertyToNil]; \ 21 | [super dealloc]; \ 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Mac OS X/AutomatorActionCategories.m: -------------------------------------------------------------------------------- 1 | // Created by Vincent Gable on 1/13/09. 2 | // Copyright 2009 Vincent Gable. All rights reserved. 3 | // 4 | 5 | #import "AutomatorActionCategories.h" 6 | #import //OSAScriptErrorMessage 7 | 8 | @implementation NSDictionary (AutomatorErrors) 9 | + (NSDictionary*) errorInfoWithMessage:(NSString*)msg errorNumber:(int)err; 10 | { 11 | return [self dictionaryWithObjectsAndKeys:msg,OSAScriptErrorMessage, 12 | [NSNumber numberWithInt:errOSASystemError], OSAScriptErrorNumber, 13 | nil]; 14 | } 15 | 16 | + (NSDictionary*) errorInfoWithMessage:(NSString*)msg; 17 | { 18 | return [self errorInfoWithMessage:msg errorNumber:errOSASystemError]; 19 | } 20 | @end 21 | -------------------------------------------------------------------------------- /Mac OS X/WorkflowCategories.h: -------------------------------------------------------------------------------- 1 | // 2 | // WorkflowCategories.h 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 12/5/08. 6 | // Copyright 2008 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AMWorkflow (VTPGExtras) 13 | //TODO: Rename, this forces a URL to exist there. 14 | + (AMWorkflow*) workflowFromURL:(NSURL*) workflowURL error:(NSError**)outError; 15 | 16 | + (AMWorkflow*)workflowAtURL:(NSURL*)url error:(NSError**)outError; 17 | + (AMWorkflow*)workflowAtURL:(NSURL*)url; 18 | 19 | + (AMWorkflow*) loadOrCreateWorkflowAt:(NSURL*) workflowURL; 20 | 21 | - (void) addEveryActionToFile:(NSURL*)targetWorkflowURL; 22 | @end -------------------------------------------------------------------------------- /Mac OS X/MacOSXCategories.m: -------------------------------------------------------------------------------- 1 | // 2 | // Categories.m 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 5/28/07. 6 | // Copyright 2007 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import "MacOSXCategories.h" 10 | #import "VTPG_Common.h" 11 | 12 | @implementation NSAppleScript (VTPGCategories) 13 | 14 | - (NSString*) executeWithStringResultAndReturnError:(NSDictionary**)error { 15 | return [[self executeAndReturnError:error] stringValue]; 16 | } 17 | 18 | - (NSString*) executeWithStringResult { 19 | NSDictionary *error = nil; 20 | NSString *result =[self executeWithStringResultAndReturnError:&error]; 21 | if(error) 22 | NSLog(@"*** executeWithStringResult had an error %@", error); 23 | 24 | return result; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /NilCategories.m: -------------------------------------------------------------------------------- 1 | // 2 | // NilCategories.m 3 | // 4 | // Created by Vincent Gable on 2009-04-21 5 | // Copyright 2009 Vincent Gable. All rights reserved. 6 | // 7 | 8 | #import "NilCategories.h" 9 | #import "VTPG_Common.h" 10 | 11 | @implementation NSString (NilCategories) 12 | //builds a URL from the string 13 | // 14 | //+[NSURL URLWithString:] throws an exception for nil, but this is considered as designed. 15 | - (NSURL*) convertToURL; 16 | { 17 | return [NSURL URLWithString:self]; 18 | } 19 | 20 | //+[NSURL URLWithString:relativeToURL:] throws an exception 21 | - (NSURL*) convertToURLRelitiveToURL:(NSURL*)baseURL; 22 | { 23 | if(!baseURL) 24 | return nil; 25 | return [NSURL URLWithString:self relativeToURL:baseURL]; 26 | } 27 | 28 | @end 29 | 30 | @implementation NSArray (NilCategories) 31 | - (id) objectOrNilAtIndex:(NSUInteger)i; 32 | { 33 | if(i >= [self count] || i < 0) 34 | return nil; 35 | return [self objectAtIndex:i]; 36 | } 37 | @end 38 | 39 | @implementation NSMutableDictionary (NilCategories) 40 | 41 | - (void) setObjectToObjectForKey:(id)key inDictionary:(NSDictionary*)otherDictionary; 42 | { 43 | id obj = [otherDictionary objectForKey:key]; 44 | if(obj) 45 | [self setObject:obj forKey:key]; 46 | } 47 | 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Mac OS X/ForEach.h: -------------------------------------------------------------------------------- 1 | /* see http://mjtsai.com/blog/2006/07/15/cocoa-foreach-macro/ 2 | * You must set the "C Language Dialect" in Xcode to C99 3 | */ 4 | #define foreachGetEnumerator(c) \ 5 | ([c respondsToSelector:@selector(objectEnumerator)] ? \ 6 | [c objectEnumerator] : \ 7 | c) 8 | #define foreachGetIMP(e) \ 9 | [foreachEnum methodForSelector:@selector(nextObject)] 10 | #define foreachCallIMP(imp, e) \ 11 | ((IMP)imp)(e, @selector(nextObject)) 12 | #define foreachGetFirst(imp, e) \ 13 | (e ? foreachCallIMP(imp, e) : nil) 14 | #define foreach(object, collection) \ 15 | for ( id foreachCollection = collection, \ 16 | foreachEnum = foreachGetEnumerator(foreachCollection), \ 17 | foreachIMP = (id)foreachGetIMP(foreachEnum), \ 18 | object = foreachGetFirst(foreachIMP, foreachEnum); \ 19 | object; \ 20 | object = foreachCallIMP(foreachIMP, foreachEnum) ) 21 | #define foreacht(type, object, collection) \ 22 | for ( type foreachCollection = (id)collection, \ 23 | *foreachEnum = (id)foreachGetEnumerator((id)foreachCollection), \ 24 | *foreachIMP = (id)foreachGetIMP((id)foreachEnum), \ 25 | *object = foreachGetFirst(foreachIMP, foreachEnum); \ 26 | object; \ 27 | object = foreachCallIMP(foreachIMP, foreachEnum) ) 28 | 29 | -------------------------------------------------------------------------------- /VTPG_Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Copyright (c) 2008-2010, Vincent Gable. 3 | // vincent.gable@gmail.com 4 | 5 | //based off of http://www.dribin.org/dave/blog/archives/2008/09/22/convert_to_nsstring/ 6 | NSString * VTPG_DDToStringFromTypeAndValue(const char * typeCode, void * value); 7 | 8 | // WARNING: if NO_LOG_MACROS is #define-ed, than THE ARGUMENT WILL NOT BE EVALUATED 9 | #ifndef NO_LOG_MACROS 10 | 11 | 12 | #define LOG_EXPR(_X_) do{\ 13 | __typeof__(_X_) _Y_ = (_X_);\ 14 | const char * _TYPE_CODE_ = @encode(__typeof__(_X_));\ 15 | NSString *_STR_ = VTPG_DDToStringFromTypeAndValue(_TYPE_CODE_, &_Y_);\ 16 | if(_STR_)\ 17 | NSLog(@"%s = %@", #_X_, _STR_);\ 18 | else\ 19 | NSLog(@"Unknown _TYPE_CODE_: %s for expression %s in function %s, file %s, line %d", _TYPE_CODE_, #_X_, __func__, __FILE__, __LINE__);\ 20 | }while(0) 21 | 22 | #define LOG_NS(...) NSLog(__VA_ARGS__) 23 | #define LOG_FUNCTION() NSLog(@"%s", __func__) 24 | 25 | #else /* NO_LOG_MACROS */ 26 | 27 | #define LOG_EXPR(_X_) 28 | #define LOG_NS(...) 29 | #define LOG_FUNCTION() 30 | #endif /* NO_LOG_MACROS */ 31 | 32 | 33 | 34 | // http://www.wilshipley.com/blog/2005/10/pimp-my-code-interlude-free-code.html 35 | static inline BOOL IsEmpty(id thing) { 36 | return thing == nil || 37 | ([thing respondsToSelector:@selector(length)] && [(NSData *)thing length] == 0) || 38 | ([thing respondsToSelector:@selector(count)] && [(NSArray *)thing count] == 0); 39 | } 40 | -------------------------------------------------------------------------------- /SynthesizeSingleton.h: -------------------------------------------------------------------------------- 1 | // 2 | // SynthesizeSingleton.h 3 | // 4 | // Created by Matt Gallagher on 20/10/08. 5 | // 6 | // http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html 7 | // 8 | // 2009-01-04: Vincent Gable, vincent.gable@gmail.com , added 9 | // SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_SHARED_INSTANCE_NAME 10 | // for more flexibility defining class names 11 | 12 | 13 | 14 | #define SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_SHARED_INSTANCE_NAME(classname,sharedInstanceName) \ 15 | \ 16 | static classname *sharedInstanceName = nil; \ 17 | \ 18 | + (classname *) sharedInstanceName \ 19 | { \ 20 | @synchronized(self) \ 21 | { \ 22 | if (sharedInstanceName == nil) \ 23 | { \ 24 | [[self alloc] init]; \ 25 | } \ 26 | } \ 27 | \ 28 | return sharedInstanceName; \ 29 | } \ 30 | \ 31 | + (id)allocWithZone:(NSZone *)zone \ 32 | { \ 33 | @synchronized(self) \ 34 | { \ 35 | if (sharedInstanceName == nil) \ 36 | { \ 37 | sharedInstanceName = [super allocWithZone:zone]; \ 38 | return sharedInstanceName; \ 39 | } \ 40 | } \ 41 | \ 42 | return nil; \ 43 | } \ 44 | \ 45 | - (id)copyWithZone:(NSZone *)zone \ 46 | { \ 47 | return self; \ 48 | } \ 49 | \ 50 | - (id)retain \ 51 | { \ 52 | return self; \ 53 | } \ 54 | \ 55 | - (NSUInteger)retainCount \ 56 | { \ 57 | return NSUIntegerMax; \ 58 | } \ 59 | \ 60 | - (void)release \ 61 | { \ 62 | } \ 63 | \ 64 | - (id)autorelease \ 65 | { \ 66 | return self; \ 67 | } 68 | 69 | 70 | #define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_SHARED_INSTANCE_NAME(classname, shared##classname) -------------------------------------------------------------------------------- /Mac OS X/WorkflowCategories.m: -------------------------------------------------------------------------------- 1 | // 2 | // WorkflowCategories.m 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 12/5/08. 6 | // Copyright 2008 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import "WorkflowCategories.h" 10 | #import //OSAScriptErrorMessage 11 | 12 | @implementation AMWorkflow (VTPGExtras) 13 | 14 | + (AMWorkflow*)workflowAtURL:(NSURL*)url error:(NSError**)outError; 15 | { 16 | if(!url) { 17 | NSLog(@"nil passed to %s", __FUNCTION__); 18 | return nil; 19 | } 20 | return [[[AMWorkflow alloc] initWithContentsOfURL:url error:(id*)outError] autorelease]; 21 | } 22 | 23 | + (AMWorkflow*)workflowAtURL:(NSURL*)url 24 | { 25 | if(!url) { 26 | NSLog(@"nil passed to %s", __FUNCTION__); 27 | return nil; 28 | } 29 | id error = nil; 30 | AMWorkflow *workflow = [[self class] workflowAtURL:url error:&error]; 31 | if(error) 32 | NSLog(@"%s got error %@ loading %@",__FUNCTION__, error, url); 33 | return workflow; 34 | } 35 | 36 | + (AMWorkflow*) workflowFromURL:(NSURL*) workflowURL error:(NSError**)outError{ 37 | id error = nil; 38 | AMWorkflow *aWorkflow = [[self class] workflowAtURL:workflowURL error:&error]; 39 | 40 | //create the workflow file if it has not been created yet. 41 | if([[error domain] isEqualTo:NSCocoaErrorDomain] && [error code] == 4) //file not found error 42 | { 43 | //try to make an empty workflow file 44 | 45 | error = nil; //clear the error file-not-found error 46 | aWorkflow = [[[AMWorkflow alloc] init] autorelease]; 47 | [aWorkflow writeToURL:workflowURL error:&error]; 48 | } 49 | 50 | if(outError) 51 | *outError = error; 52 | 53 | return aWorkflow; 54 | } 55 | 56 | + (AMWorkflow*) loadOrCreateWorkflowAt:(NSURL*) workflowURL; 57 | { 58 | return [[self class] workflowFromURL:workflowURL error:nil]; 59 | } 60 | 61 | - (void) addEveryActionToFile:(NSURL*)targetWorkflowURL; 62 | { 63 | AMWorkflow *targetWorkflow = [AMWorkflow loadOrCreateWorkflowAt:targetWorkflowURL]; 64 | for(AMAction *anAction in [self actions]) 65 | [targetWorkflow addAction:anAction]; 66 | 67 | id error = nil; 68 | [targetWorkflow writeToURL:targetWorkflowURL error:&error]; 69 | if(error) 70 | NSLog(@"***WARNING: Could not write to url %@, error %@", targetWorkflowURL, error); 71 | } 72 | 73 | @end -------------------------------------------------------------------------------- /Categories.h: -------------------------------------------------------------------------------- 1 | // 2 | // Categories.h 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 5/28/07. 6 | // Copyright 2007 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #pragma mark UUID 12 | @interface NSString (UUID) 13 | //returns a new string built from a new UUID. 14 | + (NSString*) stringWithUUID; 15 | @end 16 | 17 | @interface NSString (RangeAvoidance) 18 | - (BOOL) hasSubstring:(NSString*)substring; 19 | - (NSString*) substringAfterSubstring:(NSString*)substring; 20 | 21 | //Note: -isCaseInsensitiveLike is probably a better alternitive if it's avalible. 22 | - (BOOL) isEqualToStringIgnoringCase:(NSString*)otherString; 23 | @end 24 | 25 | #pragma mark Misc 26 | @interface NSObject (VTPGExtensions) 27 | + (id) make; 28 | @end 29 | @interface NSString (IndempotentPercentEscapes) 30 | //uses UTF8 encoding, behavior is undefined if for other encodings. 31 | - (NSString*) stringByAddingPercentEscapesOnce; 32 | - (NSString*) stringByReplacingPercentEscapesOnce; 33 | @end 34 | 35 | @interface NSMutableArray(ArrayQueue) 36 | - (id)takeObject; 37 | @end 38 | @interface NSMutableArray (ArrayStack) 39 | - (id) pop; 40 | @end 41 | 42 | @interface NSURL (IsEqualTesting) 43 | - (BOOL) isEqualToURL:(NSURL*)otherURL; 44 | @end 45 | 46 | @interface NSBundle (VTPGExtensions) 47 | - (NSString*) bundleVersion; 48 | - (NSURL*) URLForResource:(NSString *)name ofType:(NSString *)ext; 49 | @end 50 | 51 | #pragma mark HigherOrderOperations 52 | @interface NSObject (HigherOrderOperations) 53 | 54 | //implements the visitor pattern 55 | //returns self, so it can be chained 56 | - (id) performSelector:(SEL)oneArgumentSelector withEachItemOf:(id)container; 57 | 58 | //analogus to map 59 | - (NSArray*) nonNilResultsOfPerformingSelector:(SEL)oneArgumentSelector withEachItemOf:(id)container; 60 | - (NSMutableArray*) mutableNonNilResultsOfPerformingSelector:(SEL)oneArgumentSelector withEachItemOf:(id)container; 61 | 62 | @end 63 | @interface NSArray (HigherOrderOperations) 64 | - (NSMutableArray*) mutableNonNilResultsOfApplyingSelector:(SEL)sel; 65 | - (NSMutableArray*) mutableNonNilResultsOfApplyingSelector:(SEL)sel withObject:arg; 66 | 67 | - (NSMutableArray*) mutableResultsOfApplyingSelector:(SEL)sel withObject:arg; 68 | - (NSArray*) resultsOfApplyingSelector:(SEL)sel withObject:arg; 69 | - (NSArray*) resultsOfApplyingSelector:(SEL)sel; 70 | @end 71 | 72 | @interface NSDictionary (HigherOrderOperations) 73 | - (NSMutableDictionary*) mutableResultsOfApplyingSelector:(SEL)sel withObject:arg; 74 | - (NSDictionary*) resultsOfApplyingSelector:(SEL)sel withObject:arg; 75 | @end 76 | 77 | #pragma mark NSFileManager Extensions 78 | @interface NSFileManager (VTPGExtensions) 79 | - (NSDate*) modificationDateOfItemAtURL:(NSURL*)fileURL; 80 | @end 81 | 82 | @interface NSDate (VTPGDateComparison) 83 | - (BOOL) isBefore:(NSDate*)otherDate; 84 | - (BOOL) isAfter:(NSDate*)otherDate; 85 | @end 86 | -------------------------------------------------------------------------------- /NSObject+CleanUpProperties.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+CleanUpProperties.m 3 | // 4 | // Created by Vincent Gable on 12/4/08. 5 | // vincent.gable@gmail.com 6 | // http://vincentgable.com 7 | // Copyright 2008 Vincent Gable. 8 | // You are free to use this code however you like. 9 | // But I would appreciate hearing what you use it for :-) 10 | // 11 | 12 | #import "NSObject+CleanUpProperties.h" 13 | 14 | 15 | @implementation NSObject(CleanUpProperties) 16 | 17 | //syntax is: 18 | //,S(methodNameHere):, 19 | //for example: 20 | //,SintSetFoo:, 21 | //This would be even easier with regex. 22 | //for more information on attribute strings see: 23 | // http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_8_section_4.html#//apple_ref/doc/uid/TP40008048-CH101-SW5 24 | static NSString* SetterNameFromAttributes(const char *propertyAttrsCString) { 25 | NSString *propertyAttributes = [NSString stringWithUTF8String:propertyAttrsCString]; 26 | for(NSString *property in [propertyAttributes componentsSeparatedByString:@","]) 27 | if([property hasPrefix:@"S"] && [property hasSuffix:@":"]) 28 | return [property substringFromIndex:1]; 29 | 30 | return nil; 31 | } 32 | 33 | static NSString *SetterNameFromPropertyName(const char* propertyNameCStr) { 34 | NSString *propertyName = [NSString stringWithUTF8String:propertyNameCStr]; 35 | NSString *firstLetterCapitalized = [[propertyName substringToIndex:1] capitalizedString]; 36 | NSString *propertyNameTail = [propertyName substringFromIndex:1]; 37 | return [NSString stringWithFormat:@"set%@%@:",firstLetterCapitalized, propertyNameTail]; 38 | } 39 | 40 | //for more information on attribute strings see: 41 | // http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_8_section_4.html#//apple_ref/doc/uid/TP40008048-CH101-SW5 42 | static BOOL PropertyIsObjCObject(const char* propertyAttributeString) { 43 | return propertyAttributeString && 44 | propertyAttributeString[0] == 'T' && 45 | propertyAttributeString[1] == '@'; //all other pointers have a '^' here 46 | } 47 | 48 | //Readonly properties have 49 | //",R," 50 | //in their attribute strings 51 | //for more information on attribute strings see: 52 | // http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_8_section_4.html#//apple_ref/doc/uid/TP40008048-CH101-SW5 53 | static BOOL PropertyIsSettable(const char* propertyAttributeString) { 54 | return NULL == strstr(propertyAttributeString, ",R,"); 55 | } 56 | 57 | - (void) setEveryObjCObjectPropertyToNil; 58 | { 59 | unsigned int i, propertyCount = 0; 60 | objc_property_t *propertyList = class_copyPropertyList([self class], &propertyCount); 61 | if(propertyList){ 62 | for(i = 0; i < propertyCount; i++){ 63 | const char *propertyAttrs = property_getAttributes(propertyList[i]); 64 | if(PropertyIsObjCObject(propertyAttrs) && PropertyIsSettable(propertyAttrs)) { 65 | NSString *setterName = SetterNameFromAttributes(propertyAttrs); 66 | if(!setterName) 67 | setterName = SetterNameFromPropertyName(property_getName(propertyList[i])); 68 | [self performSelector:NSSelectorFromString(setterName) withObject:nil]; 69 | } 70 | } 71 | free(propertyList); 72 | } 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /VTPG_Common.m: -------------------------------------------------------------------------------- 1 | #import "VTPG_Common.h" 2 | // Copyright (c) 2008-2010, Vincent Gable. 3 | // http://vincentgable.com 4 | // 5 | //based off http://www.dribin.org/dave/blog/archives/2008/09/22/convert_to_nsstring/ 6 | // 7 | static BOOL TypeCodeIsCharArray(const char *typeCode){ 8 | size_t len = strlen(typeCode); 9 | if(len <= 2) 10 | return NO; 11 | size_t lastCharOffset = len - 1; 12 | size_t secondToLastCharOffset = lastCharOffset - 1 ; 13 | 14 | BOOL isCharArray = typeCode[0] == '[' && 15 | typeCode[secondToLastCharOffset] == 'c' && typeCode[lastCharOffset] == ']'; 16 | for(int i = 1; i < secondToLastCharOffset; i++) 17 | isCharArray = isCharArray && isdigit(typeCode[i]); 18 | return isCharArray; 19 | } 20 | 21 | //since BOOL is #defined as a signed char, we treat the value as 22 | //a BOOL if it is exactly YES or NO, and a char otherwise. 23 | static NSString* VTPGStringFromBoolOrCharValue(BOOL boolOrCharvalue) { 24 | if(boolOrCharvalue == YES) 25 | return @"YES"; 26 | if(boolOrCharvalue == NO) 27 | return @"NO"; 28 | return [NSString stringWithFormat:@"'%c'", boolOrCharvalue]; 29 | } 30 | 31 | static NSString *VTPGStringFromFourCharCodeOrUnsignedInt32(FourCharCode fourcc) { 32 | return [NSString stringWithFormat:@"%u ('%c%c%c%c')", 33 | fourcc, 34 | (fourcc >> 24) & 0xFF, 35 | (fourcc >> 16) & 0xFF, 36 | (fourcc >> 8) & 0xFF, 37 | fourcc & 0xFF]; 38 | } 39 | 40 | static NSString *StringFromNSDecimalWithCurrentLocal(NSDecimal dcm) { 41 | return NSDecimalString(&dcm, [NSLocale currentLocale]); 42 | } 43 | 44 | NSString * VTPG_DDToStringFromTypeAndValue(const char * typeCode, void * value) { 45 | #define IF_TYPE_MATCHES_INTERPRET_WITH(typeToMatch,func) \ 46 | if (strcmp(typeCode, @encode(typeToMatch)) == 0) \ 47 | return (func)(*(typeToMatch*)value) 48 | 49 | #if TARGET_OS_IPHONE 50 | IF_TYPE_MATCHES_INTERPRET_WITH(CGPoint,NSStringFromCGPoint); 51 | IF_TYPE_MATCHES_INTERPRET_WITH(CGSize,NSStringFromCGSize); 52 | IF_TYPE_MATCHES_INTERPRET_WITH(CGRect,NSStringFromCGRect); 53 | #else 54 | IF_TYPE_MATCHES_INTERPRET_WITH(NSPoint,NSStringFromPoint); 55 | IF_TYPE_MATCHES_INTERPRET_WITH(NSSize,NSStringFromSize); 56 | IF_TYPE_MATCHES_INTERPRET_WITH(NSRect,NSStringFromRect); 57 | #endif 58 | IF_TYPE_MATCHES_INTERPRET_WITH(NSRange,NSStringFromRange); 59 | IF_TYPE_MATCHES_INTERPRET_WITH(Class,NSStringFromClass); 60 | IF_TYPE_MATCHES_INTERPRET_WITH(SEL,NSStringFromSelector); 61 | IF_TYPE_MATCHES_INTERPRET_WITH(BOOL,VTPGStringFromBoolOrCharValue); 62 | IF_TYPE_MATCHES_INTERPRET_WITH(NSDecimal,StringFromNSDecimalWithCurrentLocal); 63 | 64 | #define IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(typeToMatch,formatString) \ 65 | if (strcmp(typeCode, @encode(typeToMatch)) == 0) \ 66 | return [NSString stringWithFormat:(formatString), (*(typeToMatch*)value)] 67 | 68 | 69 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(CFStringRef,@"%@"); //CFStringRef is toll-free bridged to NSString* 70 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(CFArrayRef,@"%@"); //CFArrayRef is toll-free bridged to NSArray* 71 | IF_TYPE_MATCHES_INTERPRET_WITH(FourCharCode, VTPGStringFromFourCharCodeOrUnsignedInt32); 72 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(long long,@"%lld"); 73 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(unsigned long long,@"%llu"); 74 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(float,@"%f"); 75 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(double,@"%f"); 76 | #if __has_feature(objc_arc) 77 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(__unsafe_unretained id,@"%@"); 78 | #else /* not __has_feature(objc_arc) */ 79 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(id,@"%@"); 80 | #endif 81 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(short,@"%hi"); 82 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(unsigned short,@"%hu"); 83 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(int,@"%i"); 84 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(unsigned, @"%u"); 85 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(long,@"%i"); 86 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(long double,@"%Lf"); //WARNING on older versions of OS X, @encode(long double) == @encode(double) 87 | 88 | //C-strings 89 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(char*, @"%s"); 90 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(const char*, @"%s"); 91 | if(TypeCodeIsCharArray(typeCode)) 92 | return [NSString stringWithFormat:@"%s", (char*)value]; 93 | 94 | IF_TYPE_MATCHES_INTERPRET_WITH_FORMAT(void*,@"(void*)%p"); 95 | 96 | //This is a hack to print out CLLocationCoordinate2D, without needing to #import 97 | //A CLLocationCoordinate2D is a struct made up of 2 doubles. 98 | //We detect it by hard-coding the result of @encode(CLLocationCoordinate2D). 99 | //We get at the fields by treating it like an array of doubles, which it is identical to in memory. 100 | if(strcmp(typeCode, "{?=dd}")==0)//@encode(CLLocationCoordinate2D) 101 | return [NSString stringWithFormat:@"{latitude=%g,longitude=%g}",((double*)value)[0],((double*)value)[1]]; 102 | 103 | //we don't know how to convert this typecode into an NSString 104 | return nil; 105 | } 106 | -------------------------------------------------------------------------------- /Categories.m: -------------------------------------------------------------------------------- 1 | // 2 | // Categories.m 3 | // IMLocation 4 | // 5 | // Created by Vincent Gable on 5/28/07. 6 | // Copyright 2007 Vincent Gable. All rights reserved. 7 | // 8 | 9 | #import "Categories.h" 10 | #import "VTPG_Common.h" 11 | 12 | @implementation NSString (UUID) 13 | + (NSString*) stringWithUUID { 14 | CFUUIDRef uuidObj = CFUUIDCreate(nil); 15 | NSString *UUIDstring = (NSString*)CFUUIDCreateString(nil, uuidObj); 16 | CFRelease(uuidObj); 17 | return [UUIDstring autorelease]; 18 | } 19 | @end 20 | 21 | @implementation NSString (RangeAvoidance) 22 | - (BOOL) hasSubstring:(NSString*)substring; 23 | { 24 | if(IsEmpty(substring)) 25 | return NO; 26 | NSRange substringRange = [self rangeOfString:substring]; 27 | return substringRange.location != NSNotFound && substringRange.length > 0; 28 | } 29 | 30 | - (NSString*) substringAfterSubstring:(NSString*)substring; 31 | { 32 | if([self hasSubstring:substring]) 33 | return [self substringFromIndex:NSMaxRange([self rangeOfString:substring])]; 34 | return nil; 35 | } 36 | 37 | //Note: -isCaseInsensitiveLike should work when avalible! 38 | - (BOOL) isEqualToStringIgnoringCase:(NSString*)otherString; 39 | { 40 | if(!otherString) 41 | return NO; 42 | return NSOrderedSame == [self compare:otherString options:NSCaseInsensitiveSearch + NSWidthInsensitiveSearch]; 43 | } 44 | @end 45 | 46 | @implementation NSObject (VTPGExtensions) 47 | + (id) make; 48 | { 49 | return [[[[self class] alloc] init] autorelease]; 50 | } 51 | 52 | @end 53 | 54 | 55 | @implementation NSString (IndempotentPercentEscapes) 56 | - (NSString*) stringByReplacingPercentEscapesOnce; 57 | { 58 | NSString *unescaped = [self stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 59 | //self may be a string that looks like an invalidly escaped string, 60 | //eg @"100%", in that case it clearly wasn't escaped, 61 | //so we return it as our unescaped string. 62 | return unescaped ? unescaped : self; 63 | } 64 | - (NSString*) stringByAddingPercentEscapesOnce; 65 | { 66 | return [[self stringByReplacingPercentEscapesOnce] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 67 | } 68 | @end 69 | 70 | @implementation NSURL (IsEqualTesting) 71 | //http://vgable.com/blog/2009/04/22/nsurl-isequal-gotcha/ 72 | - (BOOL) isEqualToURL:(NSURL*)otherURL; 73 | { 74 | return [[self absoluteURL] isEqual:[otherURL absoluteURL]] || 75 | ([self isFileURL] && [otherURL isFileURL] && [[self path] isEqual:[otherURL path]]); 76 | } 77 | @end 78 | 79 | @implementation NSBundle (VTPGExtensions) 80 | - (NSString*) bundleVersion; 81 | { 82 | return [self objectForInfoDictionaryKey:@"CFBundleVersion"]; 83 | } 84 | 85 | - (NSURL*) URLForResource:(NSString *)name ofType:(NSString *)ext; 86 | { 87 | NSString *path = [self pathForResource:name ofType:ext]; 88 | if(!path) 89 | return nil;//fileURLWithPath: will throw an exception if path is nil. 90 | return [NSURL fileURLWithPath:path]; 91 | } 92 | 93 | @end 94 | 95 | @implementation NSDate (VTPGDateComparison) 96 | 97 | - (BOOL) isBefore:(NSDate*)otherDate; 98 | { 99 | return [self timeIntervalSinceDate:otherDate] < 0; 100 | } 101 | 102 | - (BOOL) isAfter:(NSDate*)otherDate; 103 | { 104 | return [self timeIntervalSinceDate:otherDate] > 0; 105 | } 106 | @end 107 | 108 | @implementation NSFileManager (VTPGExtensions) 109 | 110 | - (NSDate*) modificationDateOfItemAtURL:(NSURL*)fileURL { 111 | return [[self attributesOfItemAtPath:[fileURL path] error:nil] fileModificationDate]; 112 | } 113 | @end 114 | 115 | @implementation NSMutableArray(ArrayQueue) 116 | - (id) takeObject { 117 | id object=nil; 118 | if ([self count]) { 119 | object=[self objectAtIndex:0]; 120 | [[object retain] autorelease]; 121 | [self removeObjectAtIndex:0]; 122 | } 123 | return object; 124 | } 125 | @end 126 | 127 | @implementation NSMutableArray (ArrayStack) 128 | - (id) pop; 129 | { 130 | id obj = nil; 131 | if([self count]){ 132 | obj = [self lastObject]; 133 | [[obj retain] autorelease]; 134 | [self removeLastObject]; 135 | } 136 | return obj; 137 | } 138 | @end 139 | 140 | #pragma mark HigherOrderOperations 141 | @implementation NSObject (HigherOrderOperations) 142 | //returns self 143 | - (id) performSelector:(SEL)oneArgumentSelector withEachItemOf:(id)container; 144 | { 145 | for(id obj in container) 146 | [self performSelector:oneArgumentSelector withObject:obj]; 147 | return self; 148 | } 149 | 150 | - (NSMutableArray*) mutableNonNilResultsOfPerformingSelector:(SEL)oneArgumentSelector withEachItemOf:(id)container; 151 | { 152 | NSMutableArray *results = [NSMutableArray array]; 153 | for(id obj in container){ 154 | id aResult = [self performSelector:oneArgumentSelector withObject:obj]; 155 | if(aResult) 156 | [results addObject:aResult]; 157 | } 158 | return results; 159 | } 160 | 161 | 162 | - (NSArray*) nonNilResultsOfPerformingSelector:(SEL)oneArgumentSelector withEachItemOf:(id)container 163 | { 164 | return [self mutableNonNilResultsOfPerformingSelector:oneArgumentSelector withEachItemOf:container]; 165 | } 166 | @end 167 | 168 | 169 | 170 | @implementation NSArray (HigherOrderOperations) 171 | 172 | //foldl op r [a, b, c] => ((r `op` a) `op` b) `op` c 173 | - (id) foldlWithSelector:(SEL)sel initialObject:(id)firstObject 174 | { 175 | id target = firstObject; 176 | for(id obj in self) 177 | target = [target performSelector:sel withObject:obj]; 178 | return target; 179 | } 180 | 181 | - (NSMutableArray*) mutableResultsOfApplyingSelector:(SEL)sel withObject:arg; 182 | { 183 | NSMutableArray *results = [NSMutableArray arrayWithCapacity:[self count]]; 184 | for(id object in self) { 185 | id result = [object performSelector:sel withObject:arg]; 186 | [results addObject:result]; 187 | } 188 | return results; 189 | } 190 | 191 | - (NSMutableArray*) mutableNonNilResultsOfApplyingSelector:(SEL)sel withObject:arg; 192 | { 193 | NSMutableArray *results = [NSMutableArray arrayWithCapacity:[self count]]; 194 | for(id object in self) { 195 | id result = [object performSelector:sel withObject:arg]; 196 | if(result) 197 | [results addObject:result]; 198 | } 199 | return results; 200 | } 201 | 202 | - (NSMutableArray*) mutableNonNilResultsOfApplyingSelector:(SEL)sel; 203 | { 204 | return [self mutableNonNilResultsOfApplyingSelector:sel withObject:nil]; 205 | } 206 | 207 | - (NSArray*) resultsOfApplyingSelector:(SEL)sel withObject:arg; 208 | { 209 | return [self mutableResultsOfApplyingSelector:sel withObject:arg]; 210 | } 211 | 212 | - (NSArray*) resultsOfApplyingSelector:(SEL)sel; 213 | { 214 | return [self resultsOfApplyingSelector:sel withObject:nil]; 215 | } 216 | 217 | @end 218 | 219 | @implementation NSDictionary (HigherOrderOperations) 220 | 221 | - (NSMutableDictionary*) mutableResultsOfApplyingSelector:(SEL)sel withObject:arg 222 | { 223 | NSMutableDictionary *results = [NSMutableDictionary dictionaryWithCapacity:[self count]]; 224 | 225 | for(id key in [self allKeys]) 226 | { 227 | id result = [[self objectForKey:key] performSelector:sel withObject:arg]; 228 | if(result) 229 | [results setObject:result forKey:key]; 230 | } 231 | 232 | return results; 233 | } 234 | 235 | - (NSDictionary*) resultsOfApplyingSelector:(SEL)sel withObject:arg 236 | { 237 | return [NSDictionary dictionaryWithDictionary:[self mutableResultsOfApplyingSelector:sel withObject:arg]]; 238 | } 239 | @end 240 | --------------------------------------------------------------------------------