A clean and robust way to parse URL strings in Objective C

时间:2016-04-12 00:51:06

标签: ios objective-c cocoa-touch url

I have a requirement to take a string that represents a URL that can be in many formats and standardise it so it conforms with the URL spec.

If the URL string does not have a scheme, or it has a scheme that is not 'http' or 'https', it should use a default scheme.

I wanted to use NSURLComponents but if a scheme is not provided it parses the host as a path

NSURLComponents *components = [NSURLComponents componentsWithString:@"www.google.com.au"];
components.scheme = @"http";
NSLog(@"1: %@", components.path);
NSLog(@"2: %@", components.host);
NSLog(@"3: %@", components.string);

testtest[2619:869020] 1: www.google.com.au
testtest[2619:869020] 2: ((null))
testtest[2619:869020] 3: http:www.google.com.au <-- Invalid

Therefore I ended up with this category on NSString

#define DEFAULT_SCHEME @"http"

@implementation NSString (standardiseUrlFormat)

- (NSString*)standardiseUrlFormat {
    NSURLComponents *components = [NSURLComponents componentsWithString:self];
    BOOL hasScheme = components.scheme != nil;

    // If no scheme or an invalid scheme is provided, default to http
    if (!hasScheme) {
        // We have to use string concatenation here because NSURLComponents will
        // put the hostname as the path if there is no scheme
        return [NSString stringWithFormat:@"%@://%@", DEFAULT_SCHEME, self];
    }

    // Now we know that a scheme exists, check if it is a correct scheme
    if (![components.scheme isEqualToString:@"http"] &&
        ![components.scheme isEqualToString:@"https"]) {
        // Overwrite scheme if not supported
        components.scheme = DEFAULT_SCHEME;
    }

    return [components string];
}

@end

With the following output

NSLog(@"1: %@", [@"http://www.google.com" standardiseUrlFormat]);
NSLog(@"2: %@", [@"www.google.com" standardiseUrlFormat]);
NSLog(@"3: %@", [@"https://www.google.com" standardiseUrlFormat]);
NSLog(@"4: %@", [@"https://www.google.com/some_path" standardiseUrlFormat]);
NSLog(@"5: %@", [@"www.google.com/some_path" standardiseUrlFormat]);

testtest[7411:944022] 1: http://www.google.com
testtest[7411:944022] 2: http://www.google.com
testtest[7411:944022] 3: https://www.google.com
testtest[7411:944022] 4: https://www.google.com/some_path
testtest[7411:944022] 5: http://www.google.com/some_path

Can anyone suggest a cleaner solution that doesn't use two methods (NSURLComponents and string concatenation) to construct the string?

1 个答案:

答案 0 :(得分:1)

根本不要使用字符串连接。使用NSURLComponents 形成所需的NSURL;这就是它的用途。例如,如果您不喜欢scheme的内容,将<{em> scheme设置为您想要的内容。

编辑我想我以为我发现这是一个无主机的URL,你会手动重新调整它,例如。

let s = "www.apple.com/whatever" as NSString
let arr = s.pathComponents
let c = NSURLComponents()
c.scheme = "http"
c.host = arr[0]
c.path = "/" + (Array(arr.dropFirst()) as NSArray).componentsJoinedByString("/")

但也许这不可能做到,问题实际上是没有方案的URL或多或少不是URL。