-
Ch5. NSFetchedResultsControllerRay Wenderlich/CoreData 2022. 10. 27. 22:34
NSFetchedResultsController의 세 가지 장점
- Section
- Cache
- Monitoring
이전 챕터에서 봤듯, CoreData는 UITableView와 잘 어울린다.
NSFetchedResultsController를 사용하면 UITableView와의 조합을 더 잘 활용할 수 있다.
NSFetchedResultsController는 NSFetchRequest를 감싸며 FetchResult를 내부에 갖고 있다. 그래서 fetch하는 메소드의 리턴타입이 Void다.
lazy var fetchedResultsController: NSFetchedResultsController<Team> = { let fetchRequest: NSFetchRequest<Team> = Team.fetchRequest() let zoneSort = NSSortDescriptor(key: #keyPath(Team.qualifyingZone), ascending: true) let scoreSort = NSSortDescriptor(key: #keyPath(Team.wins), ascending: false) let nameSort = NSSortDescriptor(key: #keyPath(Team.teamName), ascending: true) fetchRequest.sortDescriptors = [zoneSort, scoreSort, nameSort] let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.managedContext, sectionNameKeyPath: #keyPath(Team.qualifyingZone), cacheName: "worldCup") return fetchedResultsController }() // 리턴값을 사용하지 않음 do { try fetchedResultsController.performFetch() } catch let error as NSError { print("Fetching error: \\(error), \\(error.userInfo)") } // fetchedResultsController는 내부에 sections를 갖고 있음 // open var sections: [NSFetchedResultsSectionInfo]? { get } func numberOfSections(in tableView: UITableView) -> Int { fetchedResultsController.sections?.count ?? 0 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard let sectionInfo = fetchedResultsController.sections?[section] else { return 0 } return sectionInfo.numberOfObjects }
Modifying data
NSFetchRequest 쓸 때랑 비슷하다.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let team = fetchedResultsController.object(at: indexPath) team.wins += 1 coreDataStack.saveContext() }
Cache
on-disk 캐시다. 앱을 재실행해도 캐시의 혜택을 볼 수 있다.
NSFetchedResultsController를 선언할 때, cacheName을 지정할 수 있음.
캐시는 변화에 민감하기 때문에, 변경사항이 있다면 캐시 이름을 바꾸거나 deleteCache(withName:)를 사용하여 캐시를 삭제하자.
Grouping a set of fetched results into sections is an expensive operation. Avoid having to compute sections multiple times by specifying a cache name on your fetched results controller.
Monitoring changes
NSFetchedResultsControllerDelegate를 구현하여 변화를 감지할 수 있다. 하지만 NSFetchedResultsController를 생성할 때 사용된 ManagedObjectContext에서 발생한 변화만 감지가능하다.
extension ViewController: NSFetchedResultsControllerDelegate { func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.reloadData() } }
extension ViewController: NSFetchedResultsControllerDelegate { func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.beginUpdates() } func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { switch type { case .insert: tableView.insertRows(at: [newIndexPath!], with: .automatic) case .delete: tableView.deleteRows(at: [indexPath!], with: .automatic) case .update: let cell = tableView.cellForRow(at: indexPath!) as! TeamCell configure(cell: cell, for: indexPath!) case .move: tableView.deleteRows(at: [indexPath!], with: .automatic) tableView.insertRows(at: [newIndexPath!], with: .automatic) @unknown default: print("Unexpected NSFetchedResultsChangeType") } } func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { let indexSet = IndexSet(integer: sectionIndex) switch type { case .insert: tableView.insertSections(indexSet, with: .automatic) case .delete: tableView.deleteSections(indexSet, with: .automatic) default: break } } func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.endUpdates() } }
Inserting an underdog
let team = Team(context: self.coreDataStack.managedContext) team.teamName = nameTextField.text team.qualifyingZone = zoneTextField.text team.imageName = "wenderland-flag" self.coreDataStack.saveContext()
'Ray Wenderlich > CoreData' 카테고리의 다른 글
Ch8. Measuring & Boosting Performance (0) 2022.10.31 Ch7. Unit Testing (0) 2022.10.31 Ch6. Versioning & Migration (0) 2022.10.27 Ch2. NSManagedObject Subclasses (0) 2022.10.27