2014년 3월 6일 목요일

[iOS/OSX] CoreData #4 NSFetchedResultsController Filtering

NSFetchedResultsController 는 분명 유용하다. 하지만 몇 가지 문제가 있다. 대표적으로 동일한 Entity(Table)에 대해 여러 NSFetchedResultsController 를 만들면 문제가 발생 할 수도 있다. 두 가지 컨트롤러를 만들어 하나는 순차적으로, 하나는 특정 검색 결과만을 컨트롤 할 수 있다면 좋았을 것이다. 하지만 직접 해 보니 데이터를 제대로 읽지 못하는 등 여러가지 문제가 있었다.

만약 NSFetchedResultsController 를 이용하는 테이블에서 특정 내용 만을 검색해야 할 때는 어떻게 해야 할까?

단순하게는 수동으로 쿼리하는 방법, 즉 NSFetchRequest 와 NSPredicate 를 이용 할 수 있다. 가장 명확하고 용도에 맞게 만들기 좋은 방법이다.

하지만 이 방법 보다는 좀 더 간단하게 '필터링'을 하는 방법도 있다. 아래는 필터링을 하는 예제코드이며 이전 포스팅에서 이어지는 심볼(frc 는 NSFetchedResultsController 객체)을 사용한다.
NSPredicate *pred = [NSPredicate predicateWithFormat:@"done == YES"];
NSArray *records = [frc filteredArrayUsingPredicate:pred];
단 두 줄 만으로 검색이 가능해진다.

NSPredicate 는 여전히 질의(query)문을 작성하는데 쓰인다.

그 다음의 filteredArrayUsingPredicate: 라는 메서드를 이용해 NSPredicate 에 해당하는 내용을 걸러서(filter) 배열(NSArray)로 만들 수 있다. 만약 UITableView의 내용에서 검색하는 UI를 만든다면 유용하게 이용 할 수 있다.

다만 이 방법의 문제는 역시 정렬이다. NSFetchedResultsController 가 정의한 순서대로 정렬이 되어있다 보니 다른 기준으로 정렬해야 한다면 수동(?)으로 해야 한다. 이번에는 CoreData와는 관계 없지만 예를 들어 보자. NSArray 객체 내용물의 순서를 TestEntity의 NSDate 타입 객체 when 을 기준으로 정렬하는 예이다.

비교함수를 아래와 같은 식으로 작성했다.
NSInteger datedSort(TestEntity *a, TestEntity *b, void *reverse)
{
    if (*(BOOL *)reverse == YES) {
        return [a.when compare:b.when];
    } else {
        return [b.when compare:a.when];
    }
}
그리고 위의 필터 코드에 정렬하는 코드를 추가한다.
NSPredicate *pred = [NSPredicate predicateWithFormat:@"done == YES"];
NSArray *records = [frc filteredArrayUsingPredicate:pred];

BOOL reverse = NO;
NSArray *sortedRecords = [records sortedArrayUsingFunction:datedSort
                                                   context:&reverse];
정렬하는 방법은 여러가지가 있다. 위는 단순히 한가지 예일 뿐이다.

관련​포스트:

댓글 없음 :