Skip to content

Commit

Permalink
Allow handlers to opt-out of escaping parameters
Browse files Browse the repository at this point in the history
Useful chiefly for the INKBrowserHandler, currently, where we don't want
to escape URLs.
  • Loading branch information
lazerwalker committed Apr 21, 2014
1 parent c61447e commit 0a8e34f
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 22 deletions.
11 changes: 9 additions & 2 deletions IntentKit/Core/Helpers/NSString+Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// Copyright (c) 2013 Mike Walker. All rights reserved.
//

NSString *(^urlEncode)(NSString *);

@interface NSString (Helpers)

/** If the string is URL-like, will return a string containing the URL scheme and trailing :// */
Expand All @@ -16,14 +18,19 @@
For example, calling this on the string `@"Hi, {name}!"` with the dictionary `@{@"name":@"Mom"}` will return `@"Hi, Mom!"`.
@param data A dictionary mapping template variable names to replacement values
@param escape If YES, all template responses will be URL-escaped
@return A copy of the current string, with all instances of variable names wrapped in curly braces replaced with the appropriate data from the dictionary.*/
- (NSString *)stringByEvaluatingTemplateWithData:(NSDictionary *)data;
- (NSString *)stringByEvaluatingTemplateWithData:(NSDictionary *)data
escape:(BOOL)escape;

/** Creates a query param string from a templated dictionary, and appends it onto the given string.
@param params A dictionary mapping from a param's user-readable name to the actual templated parameter code to use
@param data A dictionary mapping template variable names to replacement values
@param escape If YES, all template responses will be URL-escaped
@return A copy of the current string with a well-formed query params string appended at the end, with all instances of variable names wrapped in curly braces replaced with the appropriate data. */
- (NSString *)stringWithTemplatedQueryParams:(NSDictionary *)params data:(NSDictionary *)data;
- (NSString *)stringWithTemplatedQueryParams:(NSDictionary *)params
data:(NSDictionary *)data
escape:(BOOL)escape;

/** An alias to [NSString stringWithFormat:] that uses an array of arguments rather than a varargs list.
Expand Down
12 changes: 7 additions & 5 deletions IntentKit/Core/Helpers/NSString+Helpers.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
kCFAllocatorDefault,
(CFStringRef)input,
NULL,
(CFStringRef)@"!*'();:@&=+$?%#[]",
(CFStringRef)@"!*'();:@&=+$?%#/[]",
kCFStringEncodingUTF8);
return (__bridge NSString *)urlString;
};
Expand All @@ -27,25 +27,27 @@ + (id)stringWithFormat:(NSString *)format array:(NSArray*)arguments; {
return result;
}

- (NSString *)stringByEvaluatingTemplateWithData:(NSDictionary *)data {
- (NSString *)stringByEvaluatingTemplateWithData:(NSDictionary *)data escape:(BOOL)escape {
NSString *tempString = [self copy];
for (NSString *key in data) {
NSString *handlebarKey = [NSString stringWithFormat:@"{%@}", key];
tempString = [tempString stringByReplacingOccurrencesOfString:handlebarKey withString:urlEncode(data[key])];
NSString *value = (escape? urlEncode(data[key]) : data[key]);
tempString = [tempString stringByReplacingOccurrencesOfString:handlebarKey withString:value];
}
return tempString;
}

- (NSString *)stringWithTemplatedQueryParams:(NSDictionary *)params data:(NSDictionary *)data {
- (NSString *)stringWithTemplatedQueryParams:(NSDictionary *)params data:(NSDictionary *)data escape:(BOOL)escape {
NSMutableArray *evaluatedParams = [NSMutableArray array];

for (NSString *key in data) {
NSString *handlebarKey = [NSString stringWithFormat:@"{%@}", key];

NSString *optionalParam = params[key];
if (optionalParam) {
NSString *value = (escape? urlEncode(data[key]) : data[key]);
NSString *optionalString = [optionalParam stringByReplacingOccurrencesOfString:handlebarKey
withString:urlEncode(data[key])];
withString:value];
[evaluatedParams addObject:optionalString];
}
}
Expand Down
11 changes: 8 additions & 3 deletions IntentKit/Core/INKActivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
as `name`. */
@property (readonly) NSString *localizedName;

/** If the handler shows a view controller rather than opening a URL, this is
/** If the handler shows a view controller rather than opening a URL, this is
an INKPresentable object to perform actions on. */
@property (readonly) id<INKPresentable> presenter;

Expand All @@ -41,6 +41,10 @@
/** The NSBundle to load the app icon from */
@property (strong, nonatomic) NSBundle *bundle;

/** If YES, all parameters passed in to URL-based activities will be URL-escaped
before template interpolation. The default is YES. */
@property (assign, nonatomic) BOOL escapeParameters;

/** Returns an initialized `INKActivity` object.
@param presenter An INKPresentable object that will have `performAction:params:inViewController:` called on it when it is time to perform an action.
Expand All @@ -63,13 +67,14 @@
@param names A dictionary of localized app names. The keys are short locale names (e.g. `en`, `fr`), the values are strings representing the name for the given locale.
@param application The UIApplication to use when calling `[UIApplication openURL:]` and `[UIApplication canOpenURL:]`. You probably want this to be `[UIApplication sharedApplication]`, but it is injected here for test purposes.
@param bundle the NSBundle to fetch the app icon from.
@param escapeParams If YES, will URL-escape all params when doing template interpolation
@return an initialized `INKActivity` object. */
- (instancetype)initWithActions:(NSDictionary *)actions
optionalParams:(NSDictionary *)optionalParams
names: (NSDictionary *)names
application:(UIApplication *)application
bundle:(NSBundle *)bundle;
bundle:(NSBundle *)bundle
escapeParams:(BOOL)escapeParams;

/** Checks whether or not the third-party app can accept a custom URL corresponding to a given command
Expand Down
10 changes: 7 additions & 3 deletions IntentKit/Core/INKActivity.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ - (instancetype)initWithActions:(NSDictionary *)actions
optionalParams:(NSDictionary *)optionalParams
names:(NSDictionary *)names
application:(UIApplication *)application
bundle:(NSBundle *)bundle {
bundle:(NSBundle *)bundle
escapeParams:(BOOL)escapeParams {
self = [super init];
if (!self) return nil;

Expand All @@ -61,6 +62,7 @@ - (instancetype)initWithActions:(NSDictionary *)actions
self.application = application;
self.intentKit = [IntentKit sharedInstance];
self.bundle = bundle;
self.escapeParameters = escapeParams;

return self;
}
Expand Down Expand Up @@ -148,9 +150,11 @@ - (void)performActivityInViewController:(UIViewController *)presentingViewContro
inViewController:presentingViewController];
} else {
NSString *urlString = self.actions[self.activityCommand];
urlString = [urlString stringByEvaluatingTemplateWithData:self.activityArguments];
urlString = [urlString stringByEvaluatingTemplateWithData:self.activityArguments
escape:self.escapeParameters];
urlString = [urlString stringWithTemplatedQueryParams:self.optionalParams
data:self.activityArguments];
data:self.activityArguments
escape:self.escapeParameters];

NSURL *url = [NSURL URLWithString:urlString];
[self.application openURL:url];
Expand Down
3 changes: 2 additions & 1 deletion IntentKit/Core/INKApplicationList.m
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ - (NSArray *)activities {
optionalParams:dict[@"optional"]
names:names
application:self.application
bundle: bundle];
bundle: bundle
escapeParams:[self.handlerClass escapeParameters]];
}

if (activity) [activities addObject:activity];
Expand Down
4 changes: 4 additions & 0 deletions IntentKit/Core/INKHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ user has not provided one.
@return A `INKActivityPresenter` object to present. */
- (INKActivityPresenter *)performCommand:(NSString *)command withArguments:(NSDictionary *)args;

/** If YES, all parameters passed in to URL-based activities will be URL-escaped
before template interpolation. The default is YES. */
+ (BOOL)escapeParameters;

/** The category of activity handled by the handler.
This is used to group handlers in INKDefaultsViewController */
+ (INKHandlerCategory) category;
Expand Down
7 changes: 6 additions & 1 deletion IntentKit/Core/INKHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ + (NSString *)name {
@throw @"Method not implemented";
}

+ (BOOL)escapeParameters {
return YES;
}

- (NSString *)defaultApp {
return [self.defaultsManager defaultApplicationForHandler:self.class
allowSystemDefault:self.useSystemDefault];
Expand Down Expand Up @@ -69,7 +73,8 @@ - (INKActivityPresenter *)performCommand:(NSString *)command withArguments:(NSDi
if (self.useFallback && [availableApps count] < 1) {
NSString *fallbackUrl = [self.appList fallbackUrlForCommand:command];
if (fallbackUrl) {
fallbackUrl = [fallbackUrl stringByEvaluatingTemplateWithData:args];
fallbackUrl = [fallbackUrl stringByEvaluatingTemplateWithData:args
escape:INKBrowserHandler.class.escapeParameters];
NSURL *url = [NSURL URLWithString:fallbackUrl];
INKBrowserHandler *browserHandler = [[INKBrowserHandler alloc] init];
return [browserHandler openURL:url];
Expand Down
9 changes: 7 additions & 2 deletions IntentKit/Handlers/INKBrowserHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import "INKBrowserHandler.h"
#import "INKActivity.h"
#import "NSString+Helpers.h"

@implementation INKBrowserHandler

Expand All @@ -19,6 +20,10 @@ + (INKHandlerCategory)category {
return INKHandlerCategoryUtility;
}

+ (BOOL)escapeParameters {
return NO;
}

- (INKActivityPresenter *)openURL:(NSURL *)url {
NSString *strippedUrl = [url.resourceSpecifier stringByReplacingOccurrencesOfString:@"//" withString:@"" options:0 range:NSMakeRange(0, 2)];
NSDictionary *args = @{@"url": strippedUrl};
Expand All @@ -35,8 +40,8 @@ - (INKActivityPresenter *)openURL:(NSURL *)url withCallback:(NSURL *)callback {
NSString *appName = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
NSString *targetURL = url.absoluteString;
NSString *callbackURL = callback.absoluteString;
NSDictionary *args = @{@"url": targetURL,
@"callback": callbackURL,
NSDictionary *args = @{@"url": urlEncode(targetURL),
@"callback": urlEncode(callbackURL),
@"source": appName};

return [self performCommand:command withArguments:args];
Expand Down
2 changes: 1 addition & 1 deletion IntentKitDemo/INKViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ - (NSArray *)content {
INKBrowserHandler *handler = [[INKBrowserHandler alloc] init];
handler.alwaysShowActivityView = self.activitySwitch.on;
handler.useSystemDefault = self.useSystemDefaultSwitch.on;
NSURL *url = [NSURL URLWithString:@"http://google.com"];
NSURL *url = [NSURL URLWithString:@"http://babycastles.com/website"];
return [handler openURL:url];
}
},
Expand Down
11 changes: 8 additions & 3 deletions IntentKitTests/INKActivitySpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ @interface INKActivity (Spec)
optionalParams:dict[@"optional"]
names:@{@"en":@"Chrome"}
application:app
bundle:nil];
bundle:nil
escapeParams:YES];
});

describe(@"activityTitle", ^{
Expand All @@ -65,7 +66,9 @@ @interface INKActivity (Spec)
names:@{@"en": @"English",
@"de": @"Deutsch",
@"fr": @"Français"}
application:app bundle:bundle];
application:app
bundle:bundle
escapeParams:YES];
activity.intentKit = mock([IntentKit class]);
});

Expand Down Expand Up @@ -95,7 +98,9 @@ @interface INKActivity (Spec)
optionalParams:dict[@"optional"]
names:@{@"de": @"Deutsch",
@"fr": @"Français"}
application:app bundle:nil];
application:app
bundle:nil
escapeParams:YES];
activity.intentKit = mock([IntentKit class]);

[given([activity.intentKit preferredLanguages]) willReturn:@[@"ja"]];
Expand Down
2 changes: 1 addition & 1 deletion IntentKitTests/INKHandlerSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ @interface INKHandler (Spec)

describe(@"addDefault:", ^{
it(@"should add a default", ^{
INKActivity *activity = [[INKActivity alloc] initWithActions:nil optionalParams:nil names:@{@"en":@"Fart.app"} application:nil bundle:nil];
INKActivity *activity = [[INKActivity alloc] initWithActions:nil optionalParams:nil names:@{@"en":@"Fart.app"} application:nil bundle:nil escapeParams:YES];
[handler addDefault:activity];
[verify(handler.defaultsManager) addDefault:@"Fart.app" forHandler:[INKHandler class]];
});
Expand Down

0 comments on commit 0a8e34f

Please sign in to comment.