I have an entity named ToDoItem. Which has three properties:
#NSManaged var toDoString: String?
#NSManaged var creationDate: NSDate?
#NSManaged var isComplete: NSNumber?
I'm trying to create a NSFetchedResultsController that will display with two section depending on the isComplete variable. I'm able to do that successfully buy doing this:
lazy var fetchedResultsController: NSFetchedResultsController = {
let questionAnswerFetchRequest = NSFetchRequest(entityName: "ToDoItem")
let questionAnswerFetchSortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
questionAnswerFetchRequest.sortDescriptors = [questionAnswerFetchSortDescriptor]
let frc = NSFetchedResultsController(
fetchRequest: questionAnswerFetchRequest,
managedObjectContext: (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext,
sectionNameKeyPath:"isComplete",
cacheName: nil)
frc.delegate = self
return frc
}()
I've also implemented the delete methods like this:
//
// MARK: Fetched Results Controller Delegate Methods
//
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates()
}
func controller(controller: NSFetchedResultsController,
didChange sectionInfo: NSFetchedResultsSectionInfo,
atSectionIndex sectionIndex: Int,
for type: NSFetchedResultsChangeType) {
switch (type) {
case .Insert:
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
break
case .Delete:
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
break
default:
break
}
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch (type) {
case .Insert:
if let indexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
break;
case .Delete:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
break;
case .Update:
if let indexPath = indexPath {
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
break;
case .Move:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
}
break;
}
}
The problem is that when I update the isComplete property I get this error:
CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after
the update (1) must be equal to the number of sections contained in
the table view before the update (0), plus or minus the number of
sections inserted or deleted (0 inserted, 0 deleted). with userInfo
(null)
Which I understand what this is saying, I just don't understand why it causing that error. I've implemented the delegate methods per the documentation.....
I've updated my NSFetchedResultsController creation to add another sortDescriptor per #wain's answer like so:
lazy var fetchedResultsController: NSFetchedResultsController = {
let questionAnswerFetchRequest = NSFetchRequest(entityName: "ToDoItem")
let isCompleteSortDescriptor = NSSortDescriptor(key: "isComplete", ascending: false)
let creationDateSortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
questionAnswerFetchRequest.sortDescriptors = [isCompleteSortDescriptor, creationDateSortDescriptor]
let frc = NSFetchedResultsController(
fetchRequest: questionAnswerFetchRequest,
managedObjectContext: (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext,
sectionNameKeyPath:"isComplete",
cacheName: nil)
frc.delegate = self
return frc
}()
I've added some breakpoints and it turns out that this delegate method is never being called:
func controller(controller: NSFetchedResultsController,
didChange sectionInfo: NSFetchedResultsSectionInfo,
atSectionIndex sectionIndex: Int,
for type: NSFetchedResultsChangeType) {
switch (type) {
case .Insert:
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
break
case .Delete:
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
break
default:
break
}
}
Which is leading to the same error as posted above. Any ideas on why that would never be called??
You need to add a sort descriptor for isComplete. It should be the first sort descriptor, then have your date one.
Basically, the sort and the sections need to be compatible, and yours currently aren't.
Related
I'm fetching some items (NSManagedObject) in viewDidLoad, and i'm using the NSFetchedResultsController delegates to update tableView cells in swift ios (the delegates from Apple docs for insertion, deletion etc.), the issue is that when I add an item to core data of the same ManagedObject type, i expect the table to update with the new item, but instead the last item is removed from the tableview and the new one isn't added. I only see the new item when I load the screen again... what could be wrong?
Here is my fetchedResultsController:
lazy var fetchedResultsController: NSFetchedResultsController<Message> = {
let context = CoreDataManager.shared.persistentContainer.viewContext
let request: NSFetchRequest<Message> = Message.fetchRequest()
request.sortDescriptors = [
NSSortDescriptor(key: "createdAt", ascending: true)
]
request.fetchLimit = 40
let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
// fetch only messages with that recipient id of the chat.
let recipientId = "\(chat!.recipientId)"
request.predicate = NSPredicate(format: "recipientId == %#", recipientId)
return frc
}()
and here is how i'm creating and adding a new message
func createNewTextMessage(recipientId: String, content: String) {
...
guard let id = Int32(recipientId) else { return }
guard let chat = fetchSingleChat(recipientId: id) else { return }
let context = persistentContainer.viewContext
let message = Message(context: context)
let date = Date()
message.chatId = chat.id
message.createdAt = date
message.isRead = false
message.senderId = Int32(currentUserId)
message.senderName = currentUserName
message.senderPhoneNumber = currentUserPhoneNumber
message.status = MessageStatus.unsent.rawValue
message.type = MessageType.text.rawValue
message.owner = MessageOwnerType.mine.rawValue
message.content = content
message.recipientId = chat.recipientId
message.recipientName = chat.title
message.messageId = "\(currentUserToken)_\(date.millisecondsSince1970)"
// here's where i'm setting some relationship (chat to message: 1-to-many)
message.chat = chat
chat.lastMessage = message
message.chat?.lastMessage = message
do {
try context.save()
} catch let err {
print("failed to save private chat:", err)
}
}
The chat ManagedObject has an attribute of lastMessage which is of type Message (also a managed object), i set the newly created message to the Chat's last message. Could the problem be from there.
And here is the delegate code as well:
// MARK: - NSFetchedResultsController Delegates
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch type {
case .insert:
tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
case .delete:
tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
case .move:
break
case .update:
break
}
}
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: .right)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
tableView.reloadRows(at: [indexPath!], with: .fade)
case .move:
tableView.moveRow(at: indexPath!, to: newIndexPath!)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
Note that your handling of moving rows (.move) only moves one row, you should implement it to move many. Not sure if this solves your problem, it depends on how your data is sorted.
Here is how I have implemented my .move
case .move:
if let indexPath = indexPath {
tableView.removeRows(at: IndexSet(integer: indexPath.item), withAnimation: .effectFade)
}
if let newIndexPath = newIndexPath {
tableView.insertRows(at: IndexSet.init(integer: newIndexPath.item), withAnimation: .effectFade)
}
Need a hint, give up after spending several hours struggling with NSFetchedResultsController.
The error message is:
CoreData: error: NSFetchedResultsController: no object at index
2147483647 in section at index 0
...but I don't even know who is firing the error. The last piece of my code is saveContext(), the next breakpoint is inside didChange.
class ViewController : UITableViewController, NSFetchedResultsControllerDelegate
private lazy var channelController: NSFetchedResultsController<ZChannel> = {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
let request: NSFetchRequest<ZChannel> = ZChannel.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "kit", ascending: true), NSSortDescriptor(key: "name", ascending: true)]
let retval: NSFetchedResultsController<ZChannel> = NSFetchedResultsController(fetchRequest: request,
managedObjectContext: appDelegate.persistentContainer.viewContext,
sectionNameKeyPath: "kit",
cacheName: nil)
retval.delegate = self
return retval
}()
public init() {
super.init(style: .grouped)
self.tableView.register(ChannelTableCell.self, forCellReuseIdentifier: "ChannelTableCell")
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.persistentContainer.viewContext.perform {
do {
try self.channelController.performFetch()
} catch {
let e = error as NSError
fatalError("[CoreData] Unresolved fetch error \(e), \(e.userInfo)")
}
}
}
public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type) {
case .insert:
self.tableView?.insertRows(at: [newIndexPath!], with: .bottom)
break;
case .update:
self.tableView?.reloadRows(at: [indexPath!], with: .bottom)
break
default:
break
}
}
public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch (type) {
case .insert:
self.tableView?.insertSections(IndexSet(integer: sectionIndex), with: UITableViewRowAnimation.bottom)
break
case .delete:
self.tableView?.deleteSections(IndexSet(integer: sectionIndex), with: UITableViewRowAnimation.bottom)
break
default:
break
}
}
From the other thread I insert new object:
self.persistentContainer.viewContext.performAndWait
{
// ...
let channel: ZChannel = NSEntityDescription.insertNewObject(forEntityName: "ZChannel", into: self.persistentContainer.viewContext) as! ZChannel
// ...
self.saveContext()
}
Some issues:
2147483647 is NSNotFound. If you are using something like indexOfObject and assuming it is in the array and then using that index that would be cause of the crash.
You are using indexPath for row update when you should use newIndexPath. The reason is that indexPath is the index before any inserts or delete and newIndexPath is the index after the inserts and deletes.
There is no reason to call self.channelController.performFetch() inside persistentContainer.viewContext.perform you are already on the main thread
I'm making a simple application where user can add habits and complete theme using swift and realm for database
Everything is working fine except when I edit the state and delete the object
The application crashes with RLMException reason: 'Index 0 is out of bounds (must be less than 0)'
I noticed that this only happens when the item is the only cell in tableView
I'd appreciate if anyone could help me with this as I've been struggling with it for the entire day
The Habit Object is:
class Habit: Object {
dynamic var id = 0
dynamic var name = ""
dynamic var state = ""
convenience init(name: String) {
self.init()
self.id = self.incrementaID()
self.name = name
self.state = "in Progress"
}
override class func primaryKey() -> String? {
return "id"
}
private func incrementaID() -> Int {
let realm = try! Realm()
let value = realm.objects(Habit).map{$0.id}.maxElement() ?? 0
return value + 1
}}
I'm using RealmSwift, SwiftFetchedResultsController to automatically update a tableView, swift 2 and Xcode 7
Here is the TableViewController relevant code in MyHabitsViewController
override func viewDidLoad() {
super.viewDidLoad()
// Get the default Realm
realm = try! Realm()
let predicate = NSPredicate(value: true)
let fetchRequest = FetchRequest<Habit>(realm: realm, predicate: predicate)
let sortDescriptor = SortDescriptor(property: "name", ascending: true)
let sortDescriptorSection = SortDescriptor(property: "state", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptorSection, sortDescriptor]
self.fetchedResultsController = FetchedResultsController<Habit>(fetchRequest: fetchRequest, sectionNameKeyPath: "state", cacheName: nil)
self.fetchedResultsController!.delegate = self
self.fetchedResultsController!.performFetch()
}
FetchedResultsControllerDelegate methods:
func controllerWillChangeContent<T : Object>(controller: FetchedResultsController<T>) {
tableView.beginUpdates()
}
func controllerDidChangeSection<T : Object>(controller: FetchedResultsController<T>, section: FetchResultsSectionInfo<T>, sectionIndex: UInt, changeType: NSFetchedResultsChangeType) {
let indexSet = NSIndexSet(index: Int(sectionIndex))
switch changeType {
case .Insert:
tableView.insertSections(indexSet, withRowAnimation: .Fade)
case .Delete:
tableView.deleteSections(indexSet, withRowAnimation: .Fade)
case .Update:
tableView.reloadSections(indexSet, withRowAnimation: .Fade)
case .Move:
tableView.deleteSections(indexSet, withRowAnimation: .Fade)
tableView.insertSections(indexSet, withRowAnimation: .Fade)
}
}
func controllerDidChangeObject<T : Object>(controller: FetchedResultsController<T>, anObject: SafeObject<T>, indexPath: NSIndexPath?, changeType: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch changeType {
case .Insert:
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Move:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
}
}
func controllerDidChangeContent<T : Object>(controller: FetchedResultsController<T>) {
tableView.endUpdates()
}
UITableViewDelegate & UITableViewDataSource
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.fetchedResultsController!.numberOfSections()
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return fetchedResultsController!.titleForHeaderInSection(section)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.fetchedResultsController!.numberOfRowsForSectionIndex(section)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("HabitInfoCell", forIndexPath: indexPath) as! HabitInfoCell
let habit = self.fetchedResultsController!.objectAtIndexPath(indexPath)!
cell.configure(habit)
// add delete button
let deleteButton = MGSwipeButton() {
try! self.realm.write {
self.realm.delete(habit)
}
}
cell.leftButtons = [deleteButton]
// add complete button
let completeButton = MGSwipeButton() {
try! self.realm.write {
habit.state = "Completed"
}
}
cell.rightButtons = [completeButton]
return cell
}
This error is shown when you pass an index greater than the total count present in Realm Object.
Check whether you Realm DB contains that entry which you are displaying on Tableview.
Download Realm Browser on Mac: Link
I had the same problem, I observed that the entry was not made to Realm DB.
Thinking that Realm already has the entry, I tried to fetch. Thus resulting in
RLMException reason: 'Index 0 is out of bounds (must be less than 0)'
Log Home Directory on console to get the realm.db file using this code:
let path = NSHomeDirectory().appending("/Documents/")
print(path)
I have a very basic NSFetchedResultsController that shows the data to the user in a UITableView, allows the user to add a new entity and so forth.
However, whenever I add a new entity, my app crashes (or sometimes just warns) with the following message:
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after the update (3) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). with userInfo (null)
Notice that even thought the numberOfRows have been updated from 2 to 3, the insertion/deletion thing still says (0 inserted, 0 deleted). So my best understanding is that the NSFetchedResultsController is not noticing the changes or something.
My code for NSFetchedResultsController is:
func fetch(frcToFetch: NSFetchedResultsController) {
do {
try frcToFetch.performFetch()
} catch {
return
}
}
func fetchRequest() -> NSFetchRequest {
// Initialize Fetch Request
let fetchRequest = NSFetchRequest(entityName: "ItemInfo")
// Add Sort Descriptors
let nameSortDescriptor = NSSortDescriptor(key: "iName", ascending: true)
fetchRequest.sortDescriptors = [nameSortDescriptor]
return fetchRequest
}
func getFRC() -> NSFetchedResultsController {
if let context = self.context{
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: context, sectionNameKeyPath: "iName.stringGroupByFirstInitial", cacheName: nil)
fetchedResultsController.delegate = self
}
return fetchedResultsController
}
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch (type) {
case .Insert:
if let indexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
break;
case .Delete:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
break;
case .Update:
if let indexPath = indexPath {
let cell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
configureCell(cell, atIndexPath: indexPath)
}
break;
case .Move:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
}
break;
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let sections = fetchedResultsController.sections {
return sections.count
}
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = fetchedResultsController.sections {
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
return 0
}
And the code to insert new record is:
let entity: NSEntityDescription.entityForName("ItemInfo", inManagedObjectContext: self.context!)!
let record = NSManagedObject(entity: entity, insertIntoManagedObjectContext: self.context!)
record.setValue(name, forKey: "iName")
record.setValue(self.billingMode.text, forKey: "iBillingMode")
do {
// Save Record
try record.managedObjectContext?.save()
try self.context!.save()
// Dismiss View Controller
dismissViewControllerAnimated(true, completion: nil)
} catch {
let saveError = error as NSError
print("\(saveError), \(saveError.userInfo)")
// Show Alert View
showAlertWithTitle("Unexpected Error", message: "Your data could not be saved. Please try again later.", cancelButtonTitle: "Done")
}
Note that the self.context variable is passed from the actual or master view controller that has the NSFetchedResultsController.
Note that the problem is with the number of sections not the number of rows. You need to implement:
(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
While using the NSFetchedResultsController in my table view, the data is being displayed properly right after creating a new NSManagedObject, but all of the rows/sections are being deleted after updating it. Below, when the update() method is called and the contact is saved, my print statements are outputting that the rows and sections are being deleted from the table view. When the create() method is called, the rows/sections are being inserted (as expected).
Here are the two different sets of output after running:
After information is retrieved from an API request, I am updating the corresponding core data model as shown below if the item already exists (as designated by a unique id):
func update(oldContact: NSManagedObject) -> Bool {
//updates contact
let contact = populateObject(oldContact)
// Delete existing phones
if let phoneDataSet = contact.valueForKeyPath("phones") {
let phonesArray = phoneDataSet.allObjects as! [NSManagedObject]
for object in phonesArray {
context.deleteObject(object)
}
}
// Add phones from response
for phone in phones {
phone.createForContact(contact)
}
// Save contact
do {
try contact.managedObjectContext?.save()
print("saving contact")
return true
} catch {
let nserror = error as NSError
print("error upong saving")
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
return false
}
func populateObject(object: NSManagedObject) -> NSManagedObject {
object.setValue(self.name, forKey: "name")
object.setValue(self.id, forKey: "id")
object.setValue(self.firstLetter, forKey: "firstLetter")
return object
}
If the item does not exist already in core data, it is created like so:
func create() -> Bool {
// Quit if contact already exists.
let data = CoreData().searchObject("Contact", predicate: "id", value: String(self.id))
guard data == nil else { fatalError("Attempted to insert duplicate contact") }
//creates a new contact NSManagedObject
var newContact = NSEntityDescription.insertNewObjectForEntityForName("Contact", inManagedObjectContext: context)
//sets the contact values
newContact = populateObject(newContact)
//creates Phone NSManagedObject, then makes a relationship with contact
for phone in self.phones {
phone.createForContact(newContact)
}
do {
//saves the contact object; also saves the relationship with the phone number
try newContact.managedObjectContext?.save()
print("Creating contact")
return true;
} catch {
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
return false
}
My FetchedResultsController delegate methods are as shown:
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch (type) {
case .Update:
if let indexPath = indexPath {
if let cell = tableView.cellForRowAtIndexPath(indexPath) as? PeopleAndPlaceCell {
configureCell(cell, atIndexPath: indexPath)
tableView.reloadRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
print("UPDATING ROW")
}
}
break;
case .Insert:
if let indexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
print("INSERTING ROW")
}
break;
case .Delete:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
print("DELETING ROW")
}
break;
case .Move:
if let indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
}
break;
}
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Update:
print("UPDATE SECTION")
break
case .Insert:
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
print("INSERTING SECTION")
case .Delete:
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
print("DELETING SECTION")
case .Move:
break
}
}
UPDATE:
The relationship for Contact to Phone is One to Many, and from Phone to Contact is a To-one. The delete rule for the contact relationship in the Phone entity is Cascade. It is also Cascade for the phone relationship in the Contact entity.
The problem is a result of the "Cascade" delete rule for the contact relationship of the Phone entity. In your update() method, you delete all the "old" Phone objects for the given Contact. The cascade delete rule causes the Contact to be deleted as well. Change this delete rule to "Nullify". (You can leave the inverse relationship, phones, in the Contact entity, as "Cascade": when you delete a Contact, it will delete all the associated Phones).