본문 바로가기

학습 노트/iOS (2021)

190. Predicate

Predicate


Spotlight, CoreData에서 검색조건을 나타낼 때 사용되는 Predicate는
기술의 종류, DB의 종류에 상관없이 검색조건 지정이 가능하다는 장점이 있다.
구현 자체에서 대소문자를 구분하진 않지만 키워드는 대문자로, 그 외엔 lowerCamelCase로 작성하는 것이 보통이다.

predicate를 사용하면 데이터 전체를 context에 전달하는 게 아닌
predicate의 조건에 맞는 데이터만 context에 전달하기 때문에 속도 향상을 기대할 수 있다.

 

PredicateViewController.swift

fetch()

  • employee의 데이터를 이름순으로 정렬해 list 배열에 저장
  • TableView 새로고침

searchBarSearchButtonClicked()

  • 선택한 segment에 따라 해당하는 메서드 호출
  • SearchBar의 Text를 파라미터로 전달

searchBarCancelButtonClicked()

  • SearchBar 초기화
  • 전체 데이터 표시

 

PredicateViewController.swift > fetch()

   func fetch(predicate: NSPredicate? = nil) {
      let request = NSFetchRequest<NSManagedObject>(entityName: "Employee")
	   
	   request.predicate = predicate
      
      
      let sortByName = NSSortDescriptor(key: "name", ascending: true)
      request.sortDescriptors = [sortByName]
      
      do {
         list = try DataManager.shared.mainContext.fetch(request)
         listTableView.reloadData()
      } catch {
         fatalError(error.localizedDescription)
      }
   }

segment에 해당되는 메서드들이 전달한 predicate를 사용해 fetch를 진행하기 때문에
파라미터로 전달받은 predicate를 request의 predicate 속성으로 전달한다.

 

PredicateViewController.swift > SearchByName()

   func searchByName(_ keyword: String?) {
      guard let keyword = keyword else { return }
      let predicate = NSPredicate(format: "name CONTAINS[c] $@", keyword)
	  fetch(predicate: predicate)
   }

 

 

 

NSPredicate(format:arguments:)

 

Apple Developer Documentation

 

developer.apple.com

fetch로 전달할 predicate는 NSPredicate 클래스의 생성자를 사용한다.

첫 번째 파라미터에 조건으로 사용될 Predicate Format 문자열을 전달하고,
두 번째 파라미터에 가변 인자를 전달한다.

name CONTAINS %@

해당 Format 문자열은 name 데이터에서 keyword로 전달된 문자열이 존재하는지를 검색한다.

앞서 언급했듯 Predicate 문자열 자체는 대소문자를 구별하진 않지만 검색 자체에는 대소문자를 구별한다.

name CONTAINS[c] %@

따라서 Keyword의 뒤에 배열로 옵션을 지정함으로써 대소문자의 구별을 무시할 수 있다.

 

PredicateViewController.swift > SearchByMinimumAge()

   func searchByMinimumAge(_ keyword: String?) {
      guard let keyword = keyword, let age = Int(keyword) else { return }
      
	   let predicate = NSPredicate(format: "%K >= %d", #keyPath(EmployeeEntity.age), age)
	   fetch(predicate: predicate)
      
   }
%K >= %d

%K는 파라미터로 전달된 Attr의 이름을 사용할 것을 의미한다.
앞 서 사용했던 %@는 문자열이나 참조 형식의 값을 사용할 것을 의미하기 때문에 둘은 명확히 구분해야 한다.

%d 혹은 %i는 숫자를 의미한다.

<=은 Swift의 비교 연산자와 동일하다.

지금처럼 두 개 이상의 가변 인자를 사용하는 경우 ',' 구분해 순서대로 전달한다.
따라서 #KeyPath(EmployeeEntity.age)는 %K에 age는 %d에 해당한다.

파라미터로 전달된 나이 이상의 직원을 출력하는 조건이 완성된다.

 

PredicateViewController.swift > SearchBySalaryRange()

   func searchBySalaryRange(_ keyword: String?) {
      guard let keyword = keyword else { return }
      let comps = keyword.components(separatedBy: "-")
      guard comps.count == 2, let min = Int(comps[0]), let max = Int(comps[1]) else { return }
	   
	   let predicate = NSPredicate(format: "%K BETWEEN {%d, %d}", #keyPath(EmployeeEntity.salary), min, max)
	   fetch(predicate: predicate)
   }
%K BETWEEN {%d, %d}

BETWEEN 키워드는 범위를 검색한다.
범위의 설정은 '{ }'로 묶어 순서대로 최소, 최대를 의미한다.

Text Field에 입력되는 값은 '-'을 기준으로 왼쪽이 최솟값, 오른쪽이 최댓값을 의미한다.

 

PredicateViewController.swift > SearchByDeptName()

   func searchByDeptName(_ keyword: String?) {
      guard let keyword = keyword else { return }
      
	   let predicate = NSPredicate(format: "%K BEGINSWITH[c] %@", #keyPath(EmployeeEntity.department.name), keyword)
	   fetch(predicate: predicate)
   }
%K BEGINSWITH[c] %@

BEGINSWITH 키워드는 전달받은 문자열로 시작하는 문자열이 있는지를 검색한다.
마찬가지로 대소문자를 구별하기 때문에 이를 무시할 수 있도록 Case Insensitive 옵션을 사용할 수 있다.

 

대소문자를 구별하지 않고 부서명을 검색하고 있으며,
BEGINSWITH의 특성에 맞게 문자열의 시작 부분을 비교하고 있는 것을 확인할 수 있다.

'학습 노트 > iOS (2021)' 카테고리의 다른 글

192. Expression  (0) 2022.03.30
191. Predicate Syntax  (0) 2022.03.24
187 ~ 189. Fetch Request  (0) 2022.02.25
187. Entity Hierarchy, Relationships  (0) 2022.02.18
186. Managed Object and Managed Object Context  (0) 2022.02.12