iOS開発時、Safariでの遷移先URLがパラメータ付きだったりすることもあるかと思います。
パラメータがひとつだけなら特に問題ないかもしれませんが、パラメータが複数になるとミスが発生する可能性が出てきます。
例えば、下記のような感じ。
(正)
http://www.example.com/index.php?key1=value1&key2=value2
(誤)
// ?がタブってる。2つ目の?は&とするところ
http://www.example.com/index.php?key1=value1?key2=value2
パラメータ部分までをベタ書きしてたり定数やマクロで定義してたりすると起こり得るミスかなと思います。
パラメータが追加になったときについうっかりとか。
ええ、私もやらかしたことがあります…
そんな苦い経験を繰り返さないために、NSURLComponentsというクラスを使ってパラメータを設定する方法を紹介したいと思います。
ゴール
NSURLComponentsを利用して、URLのパラメータを値のみセットする。
→?と&の間違えのような、単純なミスを起こさない環境にする。
方法
- iOS8以降はqueryItemsというクエリの配列のプロパティで指定
- iOS7にも対応するには辞書でクエリセットするようにカテゴリを作成
以下NSURLComponentsについて説明していきます。
NSURLComponents
初期化
- URLを指定して初期化
+ (instancetype)componentsWithString:(NSString *)URLString
(例)
NSURLComponents *components = [NSURLComponents componentsWithString:@"http://example.com"];
何も指定せず初期化し、あとからプロパティを追加していくことも可能。
プロパティ
@property(copy) NSString * プロパティ名
※例外
@property(copy) NSArray <NSURLQueryItem *> *queryItems
→iOS8からなので注意!!
プロパティ名 | 例 | プロパティ部分 |
---|---|---|
fragment | http://www.example.com/index.html#jumpLocation | jumpLocation |
host | http://www.example.com/index.html | www.example.com |
password | http://username:password@www.example.com/index.html | password |
path | http://www.example.com/index.html | /index.html |
port | http://www.example.com:8080/index.php | 8080 |
query | http://www.example.com/index.php?key1=value1&key2=value2 | key1=value1&key2=value2 |
queryItems(iOS8以降) | 下記参照 | 下記参照 |
scheme | http://www.example.com/index.html | http |
user | http://username:password@www.example.com/index.html | username |
URL確認
(例)
NSLog(@"%@", components.URL);
パラメータの設定
「query」プロパティにセットします。
(例)
components.query = @"key1=value1&key2=value2";
複数指定の場合は「&」で接続していきますが、手入力になるので結局バグ発生の可能性がありますね。
それを回避するには先述の2つの方法が考えられます。
- iOS8以降はqueryItemsというクエリの配列のプロパティで指定
- iOS7にも対応するには辞書でクエリセットするようにカテゴリを作成
iOS8以降はNSURLQueryItemとqueryItemsが使える
- NSURLQueryItem:クエリ要素のクラス
- queryItems:NSURLComponentsのプロパティ(NSURLQueryItemの配列)
使い方
NSURLQueryItem *item1 = [NSURLQueryItem queryItemWithName:@"key1" value:@"hoge"];
NSURLQueryItem *item2 = [NSURLQueryItem queryItemWithName:@"key2" value:@"fuga"];
components.queryItems = @[item1, item2];
iOS7でも要素の指定で設定できるようにカテゴリを作成
実装
ヘッダファイル(.h)
/**
* NSURLComponentsのカテゴリクラスです。
*/
@interface NSURLComponents (Addition)
/**
* query(パラメータ等)指定を辞書型で扱うためのプロパティです。
*/
@property(nonatomic, copy) NSDictionary *queryDictionary;
@end
実装ファイル(.m)
@implementation NSURLComponents (Addition)
-(NSDictionary*)queryDictionary
{
return self.queryDictionary;
}
-(void)setQueryDictionary:(NSDictionary *)queryDictionary
{
__block NSString *queryString = nil;
[queryDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([key isKindOfClass:[NSString class]] && obj != [NSNull null])
{
id value;
if ([obj isKindOfClass:[NSString class]]) {
value = obj;
} else {
value = [obj description];
}
if (!queryString) {
queryString = @"";
} else {
queryString = [queryString stringByAppendingString:@"&"];
}
queryString = [queryString stringByAppendingString:[NSString stringWithFormat:@"%@=%@", key, value]];
}
}];
self.query = queryString;
}
@end
使い方
NSURLComponents *components = [NSURLComponents componentsWithString:@"http://example.com/path"];
components.queryDictionary = @{@"key1": @"value1", @"key2": @"value2"};
NSLog(@"%@", components.URL); // http://example.com/path?key1=value1&key2=value2
おわりに
人が何かをする以上ミスをしてしまうのは仕方のないことです。
ミスの起こりにくい環境を整えていくことがアプリの品質の向上につながるのではないかと考えています。
本投稿が少しでもそのお役に立てれば幸いです。
参考
https://developer.apple.com/library/prerelease/ios/documentation/Foundation/Reference/NSURLComponents_class/index.html
http://oopsmouse.github.io/blog/2013/11/18/title/