LoginSignup
8
8

More than 5 years have passed since last update.

パラメータ付きURLでの凡ミスを回避する方法(NSURLComponents)

Last updated at Posted at 2015-12-13

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/

8
8
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8