使用 NSURLComponents 和 NSURLQueryItem 来进行更规范的 URL 字符串拼接

现象

在日常的编程工作中,经常需要拼接URL字符串,可能直接写成如下的形式:

1
NSString *urlStr = @"https://www.demo.com/query?name=Jack";

对于比较简短的url字符串,这种写法直观方便。但是如果需要拼接一大串的query语句的话,就会非常地长(虽然Objective-C语法本身也是非常的长),譬如:

1
2
NSString *urlStr = 
[NSString stringWithFormat:@"https://www.demo.com/query?name=%@&gender=%@&lng=%@&lat=%@&age=%@", @"Jack", @"male", @"121", @"31", @"20"];

此时如果你的显示器不是特别宽的话,应该已经自动换行了,并且占位符也比较多,看起来比较乱,后续维护也会比较麻烦。

改进

此时可以使用 NSURLComponentsNSURLQueryItem 来进行拼接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSURLComponents *components = [NSURLComponents componentsWithString:@"https://www.demo.com/query"];
NSMutableArray<NSURLQueryItem *> *queryItems = @[].mutableCopy;
NSURLQueryItem *nameItem = [NSURLQueryItem queryItemWithName:@"name" value:@"Jack"];
NSURLQueryItem *genderItem = [NSURLQueryItem queryItemWithName:@"gender" value:@"male"];
NSURLQueryItem *lngItem = [NSURLQueryItem queryItemWithName:@"lng" value:@"121"];
NSURLQueryItem *latItem = [NSURLQueryItem queryItemWithName:@"lat" value:@"31"];
NSURLQueryItem *ageItem = [NSURLQueryItem queryItemWithName:@"age" value:@"20"];
[queryItems addObject:nameItem];
[queryItems addObject:genderItem];
[queryItems addObject:lngItem];
[queryItems addObject:latItem];
[queryItems addObject:ageItem];
components.queryItems = queryItems;

NSURL *url = components.URL;
...

封装

这样写就比较清晰,并且后期维护起来也相对容易些,当然你也可以用字典简单封装一下NSURLQueryItem数组(在绝大多数的场景中适用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (NSArray<NSURLQueryItem *> *)queryItemsWithParameters:(NSDictionary<NSString *, NSString *> *)dict {
if ([dict isKindOfClass:[NSDictionary class]] && [dict count] > 0) {

NSMutableArray<NSURLQueryItem *> *arrM = @[].mutableCopy;
for (NSString *keyStr in dict.allKeys) {
NSURLQueryItem *item = [NSURLQueryItem queryItemWithName:keyStr value:dict[keyStr]];
[arrM addObject:item];
}
return arrM;
}

return nil;
}

NSDictionary *dict = @{@"name": @"Jack",
@"gender": @"male",
@"lng": @"121",
@"lat": @"31",
@"age": @"20"};

components.queryItems = [self queryItemsWithParameters:dict];