On 2009 Jan 28, at 20:42, Seth Willits wrote:

I can put together my own solution, but I'm *sure* there has to be some real way of doing this built-in *somewhere*, so I have to ask...

24 hours with no reply probably means that you haven't missed any built-in API.

My app has a URL handler, and I want the user to be able to click a link to save some time to things. The URL would have the form: myapp://action?key1=valueNoSpaces&key2=value+with+space&key3=value +with%0Apercent%0Aescapes

Does anyone know of a couple of methods that can simply grab the action and KV pairs decoded without a lot of hassle?

Well, once you start getting into all the encoding issues, it gets a little complicated. A couple months ago I collected all my methods related to this in a category on NSString. I've done some in-house testing on it, but there still may be bugs. If you use it, and find any bugs, let me know.

It looks like the last method in the header decodes a query into a dictionary, which is I believe what you're looking for.

************** NSString+URIQuery.h ******************************************

#import <Cocoa/Cocoa.h>


@interface NSString (URIQuery)

// NSString has a method for decoding percent escapes but none for encoding
// So, here they are:
- (NSString*)encodePercentEscapesPerRFC2396 ;
- (NSString*)encodePercentEscapesStrictlyPerRFC2396 ;
// Decodes any existing percent escapes which should not be encoded per RFC 2396 sec. 2.4.3 // Encodes any characters which should be encoded per RFC 2396 sec. 2.4.3. - (NSString*)encodePercentEscapesPerRFC2396ButNot:(NSString*)butNot butAlso:(NSString*)butAlso ;

// butNot and/or butAlso may be nil
// I did an experiment to find out which ASCII characters are encoded,
// by encoding a string with all the nonalphanumeric characters available on the // Macintosh keyboard, with and without the shift key down. There were fourteen:
//       ` # % ^ [ ] { } \ | " < >
// You only see thirteen because the fourtheenth one is the space character, " ". // This agrees with the lists of "space" "delims" and "unwise" in by RFC 2396 sec. 2.4.3 // Also, I found that all of the non-ASCII characters available on the Macintosh // keyboard by using option or shift+option are also encoded. Some of these have
// two bytes of unicode to encode, for example %C2%A4 for 0xC2A4

/*!
 @brief    Returns a string of the form "key0=value0&key1=value1&...".
 All keys and values are percent-escape encoded

 @details  For compatibility with POST, does not prepend a "?"
 All keys and all values must be NSString objects
@param The dictionary of keys and values to be encoded into the string
 */
+ stringWithQueryDictionary:(NSDictionary*)dictionary ;

/* Not sure how this is different than - stringByReplacingPercentEscapesUsingEncoding: Performing test in implementation to see if I can use that instead of this.
 */
- (NSString*)decodeAllPercentEscapes ;

/*!
@brief Assuming that the receiver is a query string of key=value pairs, of the form "key0=value0&key1=value1&...", with keys and values percent-escape
 encoded per RFC 2396, returns a dictionary of the keys and values.

@details Supports both ampersand "&" and semicolon ";" to delimit key-value
 pairs.  The latter is recommended here:
 http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#h-B.2.2
 */
- (NSDictionary*)queryDictionaryUsingEncoding: (NSStringEncoding)encoding ;

@end



************** NSString+URIQuery.m ******************************************

#import "NSString+URIQuery.h"


@implementation NSString (URIQuery)

- (NSString*)encodePercentEscapesPerRFC2396 {
return (NSString*) [(NSString*)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)self, NULL, NULL, kCFStringEncodingUTF8) autorelease] ;
}

- (NSString*)encodePercentEscapesStrictlyPerRFC2396 {

CFStringRef decodedString = (CFStringRef)[self decodeAllPercentEscapes] ; // The above may return NULL if url contains invalid escape sequences like %E8me, %E8fe, %E800 or %E811, // because CFURLCreateStringByReplacingPercentEscapes() isn't smart enough to ignore them. CFStringRef recodedString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, decodedString, NULL, NULL, kCFStringEncodingUTF8); // And then, if decodedString is NULL, recodedString will be NULL too. // So, we recover from this rare but possible error by returning the original self
    // because it's "better than nothing".
NSString* answer = (recodedString != NULL) ? [(NSString*)recodedString autorelease] : self ; // Note that if recodedString is NULL, we don't need to CFRelease() it. // Actually, CFRelease(NULL) causes a crash. That's kind of stupid, Apple.
    return answer ;
}

- (NSString*)encodePercentEscapesPerRFC2396ButNot:(NSString*)butNot butAlso:(NSString*)butAlso { return (NSString*) [(NSString*)CFURLCreateStringByAddingPercentEscapes( NULL , (CFStringRef )self, (CFStringRef )butNot, (CFStringRef )butAlso,
                                                                          
kCFStringEncodingUTF8
                       ) autorelease] ;
}

+ stringWithQueryDictionary:(NSDictionary*)dictionary {
    NSMutableString* string = [NSMutableString string] ;
    NSUInteger countdown = [dictionary count] ;
    NSString* additionsToRFC2396 = @"+=;" ;
    for (NSString* key in dictionary) {
        [string appendFormat:@"%...@=%@",
         [key encodePercentEscapesPerRFC2396ButNot:nil
                                           butAlso:additionsToRFC2396],
[[dictionary valueForKey:key] encodePercentEscapesPerRFC2396ButNot:nil butAlso:additionsToRFC2396]
        ] ;
        countdown-- ;
        if (countdown > 0) {
            [string appendString:@"&"] ;
        }
    }
    return [NSString stringWithString:string] ;
}



- (NSString*)decodeAllPercentEscapes {
// Unfortunately, CFURLCreateStringByReplacingPercentEscapes() seems to only replace %[NUMBER] escapes NSString* cfWay = (NSString*) [(NSString *)CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, CFSTR("")) autorelease] ; NSString* cocoaWay = [self stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] ;
    if (![cfWay isEqualToString:cocoaWay]) {
        NSBeep() ;
NSLog(@"[%@ %s]: CF and Cocoa different for %@", [self class], _cmd, self) ;
    }

    return cfWay ;
}

- (NSDictionary*)queryDictionaryUsingEncoding: (NSStringEncoding)encoding { NSCharacterSet* delimiterSet = [NSCharacterSet characterSetWithCharactersInString:@"&;"] ;
    NSMutableDictionary* pairs = [NSMutableDictionary dictionary] ;
    NSScanner* scanner = [[NSScanner alloc] initWithString:self] ;
    while (![scanner isAtEnd]) {
        NSString* pairString ;
        [scanner scanUpToCharactersFromSet:delimiterSet
                                intoString:&pairString] ;
        [scanner scanCharactersFromSet:delimiterSet intoString:NULL] ;
NSArray* kvPair = [pairString componentsSeparatedByString:@"="] ;
        if ([kvPair count] == 2) {
NSString* key = [[kvPair objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:encoding] ; NSString* value = [[kvPair objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:encoding] ;
            [pairs setObject:value forKey:key] ;
        }
    }

    return [NSDictionary dictionaryWithDictionary:pairs] ;
}

@end

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to