I try to use fetched results controller with swift 3, but it doesn't work. I saw, that my entity was added to Core Data with my own fetch request, but frc didn't saw them. If I restart my app, new elements will appear in table.
Creation of FRC:
func getFRCForChats() -> NSFetchedResultsController<Conversation>{
var fetchedResultsController: NSFetchedResultsController<Conversation>
let fetchRequest: NSFetchRequest<Conversation> = Conversation.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "conversationID", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchedResultsController = NSFetchedResultsController<Conversation>(fetchRequest:
fetchRequest, managedObjectContext: container.viewContext, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
Using of FRC:
var fetchedResultsController: NSFetchedResultsController<Conversation>!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
coreDataService = CoreDataService()
fetchedResultsController = coreDataService.getFRCForChats()
fetchedResultsController.delegate = self
try! fetchedResultsController.performFetch()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(!fetchedResultsController.sections!.isEmpty) {
if let sectionInfo = fetchedResultsController.sections?[section]{
return sectionInfo.numberOfObjects
} else { print("Unexpected Section") }
}
return 0
}
func numberOfSections(in tableView: UITableView) -> Int {
if let count = fetchedResultsController.sections?.count {
return count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatCell", for: indexPath) as!ChatTableViewCell
//cell.name.text = cells[indexPath.row]
let conversation = fetchedResultsController.object(at: indexPath)
if let participants = conversation.participants as? Set<User> {
conversation.managedObjectContext?.performAndWait {
for user in participants{
cell.name.text = user.name
break
}
}
}
return cell
}
Adding new entity:
var k = 2
@IBAction func createConversation(_ sender: Any) {
coreDataService.insertConversation(id: k)
k += 1
}
func insertConversation(id: Int) {
container.performBackgroundTask { (context) in
let user = User.findOrInsertUser(id: id, name: "Andrew", mobilePhone: 234567, avatar: nil, inContext: context)
_ = Conversation.findOrInsertConversation(id: id + 100, summa: Double(12+id), users: [user], transactions: [], inContext: context)
context.saveThrows()
}
let request: NSFetchRequest<Conversation> = Conversation.fetchRequest()
container.viewContext.perform {
if let results = try? self.container.viewContext.fetch(request) {
print("\(results.count) TweetMs")
for result in results{
print(result.conversationID, result.summa)
}
}
}
}
Delegate:
extension ChatsViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .automatic)
}
case .insert:
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
case .move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .automatic)
}
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
case .update:
if let indexPath = indexPath {
tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex),
with: .automatic)
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex),
with: .automatic)
case .move, .update: break
}
}
}
Edit: If I call performFetch() after every editing of context, table reloaded without calling delegate.
As Apple Docs states:
automaticallyMergesChangesFromParent
A Boolean value that indicates whether the context automatically merges changes saved to its persistent store coordinator or parent context.
Try setting it to true
container.viewContext.automaticallyMergesChangesFromParent = true
At least that solved similar problem for me.