hachinoBlog

hachinobuのエンジニアライフ

条件に応じてNSPredicateを連結して使えるNSCompoundPredicate (IN使用時の注意点)

CoreDataでデータを抽出する際にNSPredicateを使用して条件を絞るがNSPredicateのpredicateWithFormatメソッドに
IN句を使う際に文字列として作成した変数を条件に入れると落ちてしまう。

//失敗例 NSInvalidArgumentException
NSString *column = @"column1";
NSArray *datas = @[@"a", @"b", @"c"];
NSString *where = [NSString stringWithFormat:@"%@ IN %@", column, datas];
NSPredicate *predicate = [NSPredicate predicateWithFormat:where]; 

//成功例
NSString *column = @"column1";
NSArray *datas = @[@"a", @"b", @"c"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K IN %@", column, datas];

上記のようにpredicateWithFormatに直接書いてやらないとINのなかの抽出条件がうまいこと展開されない。
問題は状況に応じてIN句を付与したい場合。(フラグに応じてIN条件を増やすなど)
こんな時はNSCompoundPredicateを使用すると良い。

//配列としてNSPredicateオブジェクトを格納させる
NSMutableArray *whereArray = [NSMutableArray array];

NSString *column1 = @"column1";
NSString *data1 = @"a";

//条件1を配列に格納
[whereArray addObject:[NSPredicate predicateWithFormat:@"%K = %@", column, data1]];

//フラグがTrueであればIN条件を付与する
if (flag) {
    NSString *column2 = @"column2";
    NSArray *datas = @[@"a", @"b", @"c"];
    //条件2を配列に格納
    [whereArray addObject:[NSPredicate predicateWithFormat:@"%K IN %@", column2, datas]];
}

//条件配列をNSCompoundPredicateに格納してNSPredicateをつくる
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:whereArray];

あとはいつも通りsetPredicateにセットしてあげればOK!
ちなみにNSCompoundPredicateには複数メソッドがあって
andPredicateWithSubpredicates ⇒ 引数に渡された配列のオブジェクトをand条件でつなぐ
orPredicateWithSubpredicates ⇒ 引数に渡された配列のオブジェクトをor条件でつなぐ
notPredicateWithSubpredicate ⇒ 引数に渡されたNSPredicateオブジェクトをnot条件にする(否定)

参考URL
ここが凄く分かりやすかった
http://cocoadays.blogspot.jp/2011/01/macios-nscompoundpredicate.html