From 11f1e7443737363ff52c254f44a21c0c98dd8eb4 Mon Sep 17 00:00:00 2001 From: joehewitt Date: Wed, 8 Jul 2009 04:41:23 -0700 Subject: [PATCH] * Convenience methods added to string for opening/loading URLs * Allow URL opener to specify a parent URL, overriding the pattern --- src/NSStringAdditions.m | 13 ++++ src/TTAppMap.m | 94 ++++++++++++++++--------- src/TTURLPattern.h | 2 +- src/TTURLPattern.m | 13 ++-- src/TTWebController.m | 12 ++++ src/Three20/NSStringAdditions.h | 12 +++- src/Three20/TTAppMap.h | 21 ++++-- src/Three20/UIViewControllerAdditions.h | 4 +- src/UIViewControllerAdditions.m | 6 +- 9 files changed, 125 insertions(+), 52 deletions(-) diff --git a/src/NSStringAdditions.m b/src/NSStringAdditions.m index d1b14e5853..c074169a20 100644 --- a/src/NSStringAdditions.m +++ b/src/NSStringAdditions.m @@ -119,4 +119,17 @@ - (void)openURL { TTOpenURL(self); } +- (id)objectValue { + return [[TTAppMap sharedMap] objectForURL:self]; +} + +- (NSString*)objectURL:(id)object { + if ([object conformsToProtocol:@protocol(TTURLObject)]) { + id URLObject = object; + return [URLObject formatURL:self]; + } else { + return self; + } +} + @end diff --git a/src/TTAppMap.m b/src/TTAppMap.m index 677f8e0cbf..96829f72db 100644 --- a/src/TTAppMap.m +++ b/src/TTAppMap.m @@ -101,18 +101,18 @@ - (TTURLPattern*)matchPattern:(NSURL*)URL { return _defaultPattern; } -- (id)objectForURL:(NSURL*)URL outPattern:(TTURLPattern**)outPattern { - TTURLPattern* pattern = [self matchPattern:URL]; - if (pattern) { - if (_bindings) { - // XXXjoe Normalize the URL first - NSString* URLString = [URL absoluteString]; - UIViewController* controller = [_bindings objectForKey:URLString]; - if (controller) { - return controller; - } +- (id)objectForURL:(NSString*)URL theURL:(NSURL*)theURL params:(NSDictionary*)params + outPattern:(TTURLPattern**)outPattern { + if (_bindings) { + // XXXjoe Normalize the URL first + id object = [_bindings objectForKey:URL]; + if (object) { + return object; } + } + TTURLPattern* pattern = [self matchPattern:theURL]; + if (pattern) { id target = nil; UIViewController* controller = nil; @@ -123,13 +123,13 @@ - (id)objectForURL:(NSURL*)URL outPattern:(TTURLPattern**)outPattern { } if (pattern.selector) { - controller = [pattern invokeSelectorForTarget:target withURL:URL]; + controller = [pattern invoke:target withURL:theURL params:params]; } else if (pattern.targetClass) { controller = [target init]; } if (pattern.displayMode == TTDisplayModeShare && controller) { - [self bindObject:controller toURL:[URL absoluteString]]; + [self bindObject:controller toURL:URL]; } [target autorelease]; @@ -144,10 +144,11 @@ - (id)objectForURL:(NSURL*)URL outPattern:(TTURLPattern**)outPattern { } - (UIViewController*)parentControllerForController:(UIViewController*)controller - withPattern:(TTURLPattern*)pattern { + parent:(NSURL*)parentURL { UIViewController* parentController = nil; - if (pattern.parentURL) { - parentController = [self objectForURL:pattern.parentURL outPattern:nil]; + if (parentURL) { + parentController = [self objectForURL:parentURL.absoluteString theURL:parentURL params:nil + outPattern:nil]; } // If this is the first controller, and it is not a "container", forcibly put @@ -192,36 +193,40 @@ - (void)presentController:(UIViewController*)controller } - (void)presentController:(UIViewController*)controller forURL:(NSURL*)URL - withPattern:(TTURLPattern*)pattern animated:(BOOL)animated { + parent:(NSString*)parentURL withPattern:(TTURLPattern*)pattern animated:(BOOL)animated { + NSURL* parent = parentURL ? [NSURL URLWithString:parentURL] : pattern.parentURL; UIViewController* parentController = [self parentControllerForController:controller - withPattern:pattern]; + parent:parent]; [self presentController:controller parent:parentController modal:pattern.displayMode == TTDisplayModeModal animated:animated]; } -- (UIViewController*)openControllerWithURL:(NSString*)URL display:(BOOL)display - animated:(BOOL)animated { - if ([_delegate respondsToSelector:@selector(appMap:shouldOpenURL:)]) { - if ([_delegate appMap:self shouldOpenURL:URL]) { +- (UIViewController*)openControllerWithURL:(NSString*)URL parent:(NSString*)parentURL + params:(NSDictionary*)params display:(BOOL)display animated:(BOOL)animated { + NSURL* theURL = [NSURL URLWithString:URL]; + + if (display && [_delegate respondsToSelector:@selector(appMap:shouldOpenURL:)]) { + if (![_delegate appMap:self shouldOpenURL:theURL]) { return nil; } } - NSURL* theURL = [NSURL URLWithString:URL]; TTURLPattern* pattern = nil; - UIViewController* controller = [self objectForURL:theURL outPattern:&pattern]; + UIViewController* controller = [self objectForURL:URL theURL:theURL params:params + outPattern:&pattern]; if (controller) { - if ([_delegate respondsToSelector:@selector(appMap:wilOpenURL:inViewController:)]) { - [_delegate appMap:self willOpenURL:URL inViewController:controller]; + if (display && [_delegate respondsToSelector:@selector(appMap:wilOpenURL:inViewController:)]) { + [_delegate appMap:self willOpenURL:theURL inViewController:controller]; } controller.appMapURL = URL; if (display) { - [self presentController:controller forURL:theURL withPattern:pattern animated:animated]; + [self presentController:controller forURL:theURL parent:parentURL withPattern:pattern + animated:animated]; } - } else if (_openExternalURLs) { + } else if (display && _openExternalURLs) { if ([_delegate respondsToSelector:@selector(appMap:wilOpenURL:inViewController:)]) { - [_delegate appMap:self willOpenURL:URL inViewController:nil]; + [_delegate appMap:self willOpenURL:theURL inViewController:nil]; } [[UIApplication sharedApplication] openURL:theURL]; @@ -255,7 +260,8 @@ - (BOOL)restoreControllersStartingWithURL:(NSString*)startURL { return NO; } - UIViewController* controller = [self openControllerWithURL:URL display:YES animated:NO]; + UIViewController* controller = [self openControllerWithURL:URL parent:nil params:nil + display:YES animated:NO]; controller.frozenState = state; if (_persistenceMode == TTAppMapPersistenceModeTop && pathIndex++ == 1) { @@ -351,19 +357,37 @@ - (UIViewController*)visibleViewController { } - (UIViewController*)openURL:(NSString*)URL { - return [self openURL:URL animated:YES]; + return [self openURL:URL parent:nil params:nil animated:YES]; +} + +- (UIViewController*)openURL:(NSString*)URL params:(NSDictionary*)params { + return [self openURL:URL parent:nil params:params animated:YES]; } - (UIViewController*)openURL:(NSString*)URL animated:(BOOL)animated { + return [self openURL:URL parent:nil params:nil animated:animated]; +} + +- (UIViewController*)openURL:(NSString*)URL parent:(NSString*)parentURL animated:(BOOL)animated { + return [self openURL:URL parent:parentURL params:nil animated:animated]; +} + +- (UIViewController*)openURL:(NSString*)URL params:(NSDictionary*)params animated:(BOOL)animated { + return [self openURL:URL parent:nil params:nil animated:animated]; +} + +- (UIViewController*)openURL:(NSString*)URL parent:(NSString*)parentURL params:(NSDictionary*)params + animated:(BOOL)animated { if (!_mainViewController && _persistenceMode && [self restoreControllersStartingWithURL:URL]) { return _mainViewController; } else { - return [self openControllerWithURL:URL display:YES animated:animated]; + return [self openControllerWithURL:URL parent:parentURL params:params display:YES + animated:animated]; } } - (id)objectForURL:(NSString*)URL { - return [self openControllerWithURL:URL display:NO animated:NO]; + return [self openControllerWithURL:URL parent:nil params:nil display:NO animated:NO]; } - (TTDisplayMode)displayModeForURL:(NSString*)URL { @@ -460,7 +484,7 @@ - (void)removeBindingForObject:(id)object { // XXXjoe IMPLEMENT ME } -- (void)removePersistedControllers { +- (void)resetDefaults { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults removeObjectForKey:@"TTAppMapNavigation"]; [defaults synchronize]; @@ -484,6 +508,6 @@ - (void)persistController:(UIViewController*)controller path:(NSMutableArray*)pa /////////////////////////////////////////////////////////////////////////////////////////////////// // global -void TTOpenURL(NSString* URL) { - [[TTAppMap sharedMap] openURL:URL]; +UIViewController* TTOpenURL(NSString* URL) { + return [[TTAppMap sharedMap] openURL:URL]; } diff --git a/src/TTURLPattern.h b/src/TTURLPattern.h index c579c05f54..6e615ca2a7 100644 --- a/src/TTURLPattern.h +++ b/src/TTURLPattern.h @@ -30,6 +30,6 @@ - (BOOL)matchURL:(NSURL*)URL; -- (id)invokeSelectorForTarget:(id)target withURL:(NSURL*)URL; +- (id)invoke:(id)target withURL:(NSURL*)URL params:(NSDictionary*)params; @end diff --git a/src/TTURLPattern.m b/src/TTURLPattern.m index 464d67e361..8018daf489 100644 --- a/src/TTURLPattern.m +++ b/src/TTURLPattern.m @@ -191,7 +191,6 @@ - (void)deduceSelector { if (parts.count) { NSString* selectorName = [[parts componentsJoinedByString:@":"] stringByAppendingString:@":"]; SEL selector = sel_registerName(selectorName.UTF8String); - Class cls = _targetClass ? _targetClass : [_targetObject class]; _selector = selector; } } @@ -279,7 +278,8 @@ - (BOOL)setArgument:(NSString*)text pattern:(id)patternText return NO; } -- (void)setArgumentsFromURL:(NSURL*)URL forInvocation:(NSInvocation*)invocation { +- (void)setArgumentsFromURL:(NSURL*)URL forInvocation:(NSInvocation*)invocation + params:(NSDictionary*)params { NSInteger remainingArguments = _argumentCount; NSArray* pathComponents = URL.path.pathComponents; @@ -293,7 +293,7 @@ - (void)setArgumentsFromURL:(NSURL*)URL forInvocation:(NSInvocation*)invocation NSDictionary* query = [URL.query queryDictionaryUsingEncoding:NSUTF8StringEncoding]; if (query.count) { - NSMutableDictionary* unmatched = nil; + NSMutableDictionary* unmatched = params ? [[params mutableCopy] autorelease] : nil; for (NSString* name in [query keyEnumerator]) { id patternText = [_query objectForKey:name]; @@ -429,7 +429,7 @@ - (BOOL)matchURL:(NSURL*)URL { return YES; } -- (id)invokeSelectorForTarget:(id)target withURL:(NSURL*)URL { +- (id)invoke:(id)target withURL:(NSURL*)URL params:(NSDictionary*)params { id returnValue = nil; NSMethodSignature *sig = [target methodSignatureForSelector:self.selector]; @@ -439,8 +439,11 @@ - (id)invokeSelectorForTarget:(id)target withURL:(NSURL*)URL { [invocation setSelector:self.selector]; if (self.isUniversal) { [invocation setArgument:&URL atIndex:2]; + if (params) { + [invocation setArgument:¶ms atIndex:3]; + } } else { - [self setArgumentsFromURL:URL forInvocation:invocation]; + [self setArgumentsFromURL:URL forInvocation:invocation params:params]; } [invocation invoke]; diff --git a/src/TTWebController.m b/src/TTWebController.m index f6b911d030..7cc6692abd 100644 --- a/src/TTWebController.m +++ b/src/TTWebController.m @@ -44,6 +44,18 @@ - (id)initWithURL:(NSURL*)URL { return self; } +- (id)initWithURL:(NSURL*)URL params:(NSDictionary*)params { + if (self = [super init]) { + NSURLRequest* request = [params objectForKey:@"request"]; + if (request) { + [self openRequest:request]; + } else { + [self openURL:URL]; + } + } + return self; +} + - (id)init { if (self = [super init]) { _delegate = nil; diff --git a/src/Three20/NSStringAdditions.h b/src/Three20/NSStringAdditions.h index b7812b5bd8..d8aaf9cabd 100644 --- a/src/Three20/NSStringAdditions.h +++ b/src/Three20/NSStringAdditions.h @@ -15,8 +15,18 @@ - (NSString*)stringByRemovingHTMLTags; /** - * Loads a URL with the string using TTAppMap. + * Opens a URL with the string using TTAppMap. */ - (void)openURL; +/** + * Converts the string to an object using TTAppMap. + */ +- (id)objectValue; + +/** + * Formats a URL using an object that conforms to the TTURLObject protocol. + */ +- (NSString*)objectURL:(id)object; + @end diff --git a/src/Three20/TTAppMap.h b/src/Three20/TTAppMap.h index 0798089737..7649c54d9f 100644 --- a/src/Three20/TTAppMap.h +++ b/src/Three20/TTAppMap.h @@ -18,6 +18,12 @@ typedef enum { TTDisplayModeModal, // new controller is created and displayed modally } TTDisplayMode; +@protocol TTURLObject + +- (NSString*)formatURL:(NSString*)URLFormat; + +@end + @interface TTAppMap : NSObject { id _delegate; UIWindow* _mainWindow; @@ -75,7 +81,12 @@ typedef enum { * a keyWindow, a UIWindow will be created and displayed. */ - (UIViewController*)openURL:(NSString*)URL; +- (UIViewController*)openURL:(NSString*)URL params:(NSDictionary*)params; - (UIViewController*)openURL:(NSString*)URL animated:(BOOL)animated; +- (UIViewController*)openURL:(NSString*)URL parent:(NSString*)parentURL animated:(BOOL)animated; +- (UIViewController*)openURL:(NSString*)URL params:(NSDictionary*)params animated:(BOOL)animated; +- (UIViewController*)openURL:(NSString*)URL parent:(NSString*)parentURL + params:(NSDictionary*)params animated:(BOOL)animated; /** * Gets or creates the object with a pattern that matches the URL. @@ -174,9 +185,9 @@ typedef enum { - (void)removeBindingForObject:(id)object; /** - * Erases all persisted controller data from preferences. + * Erases all data stored in user defaults. */ -- (void)removePersistedControllers; +- (void)resetDefaults; /** * Persists a controller's state and recursively persists the next controller after it. @@ -194,14 +205,14 @@ typedef enum { /** * Asks if the URL should be opened and allows the delegate to stop it. */ -- (BOOL)appMap:(TTAppMap*)appMap shouldOpenURL:(NSString*)URL; +- (BOOL)appMap:(TTAppMap*)appMap shouldOpenURL:(NSURL*)URL; /** * The URL is about to be opened in a controller. * * If the controller argument is nil, the URL will be opened externally. */ -- (void)appMap:(TTAppMap*)appMap willOpenURL:(NSString*)URL +- (void)appMap:(TTAppMap*)appMap willOpenURL:(NSURL*)URL inViewController:(UIViewController*)controller; @end @@ -212,4 +223,4 @@ typedef enum { /** * Shortcut for calling [[TTAppMap sharedMap] openURL:] */ -void TTOpenURL(NSString* URL); +UIViewController* TTOpenURL(NSString* URL); diff --git a/src/Three20/UIViewControllerAdditions.h b/src/Three20/UIViewControllerAdditions.h index 7db6e441b6..a0cca0fc3c 100644 --- a/src/Three20/UIViewControllerAdditions.h +++ b/src/Three20/UIViewControllerAdditions.h @@ -52,12 +52,12 @@ */ - (BOOL)isContainerController; +- (void)persistNavigationPath:(NSMutableArray*)path; + - (void)persistView:(NSMutableDictionary*)state; - (void)restoreView:(NSDictionary*)state; -- (void)persistNavigationPath:(NSMutableArray*)path; - /** * Shows a UIAlertView with a message and title. * diff --git a/src/UIViewControllerAdditions.m b/src/UIViewControllerAdditions.m index 194862b6bd..08be8cfdbf 100644 --- a/src/UIViewControllerAdditions.m +++ b/src/UIViewControllerAdditions.m @@ -102,13 +102,13 @@ - (BOOL)isContainerController { return NO; } -- (void)persistView:(NSMutableDictionary*)state { +- (void)persistNavigationPath:(NSMutableArray*)path { } -- (void)restoreView:(NSDictionary*)state { +- (void)persistView:(NSMutableDictionary*)state { } -- (void)persistNavigationPath:(NSMutableArray*)path { +- (void)restoreView:(NSDictionary*)state { } - (void)alert:(NSString*)message title:(NSString*)title delegate:(id)delegate {