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
Related
I am getting server response as like below
[[“image_url": https://someurl1, "title": Title1], ["image_url": https://someurl2, "title": Title2], ["image_url": https://someurl3, "title": Title3], ["image_url": https://someurl4, "title": Title4]]
I am storing this data to core data by loop.
So, I am trying to fetch this data from core data using NSFetchedResultsController and I am trying to display in Tableview
func saveDataToDataBase(json: [[String: Any]]) {
for eachData in json {
Categories.saveCategories(jsonResponse: eachData, completionHandler: { [weak self] success in
if (success) {
}
})
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.fetchData()
}
}
func fetchData() {
fetchedResultsController = NSFetchedResultsController(fetchRequest: allCategoriesData(), managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
do {
try fetcheResultsController()!.performFetch()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.localizedDescription)")
}
}
func allCategoriesData() -> NSFetchRequest<NSFetchRequestResult> {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: HomeKeyConstant.Entity_Categories)
let sortDescriptor = NSSortDescriptor(key: HomeKeyConstant.categories_Id, ascending: true)
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
// UITableviewDelegate & Data Source methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
guard let sectionCount = fetchedResultsController.sections?.count else {
return 0
}
return sectionCount
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.fetchedResultsController == nil {
} else {
if let sectionData = fetchedResultsController.sections?[section] {
return sectionData.numberOfObjects
}
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = homeTableView.dequeueReusableCell(withIdentifier: HomeKeyConstant.key_Cell_Identifier, for: indexPath) as! HomeTableViewCell
self.configureCell(cell, at: indexPath)
return cell
}
func configureCell(_ cell: HomeTableViewCell?, at indexPath: IndexPath?) {
let category = fetchedResultsController.object(at: indexPath!) as! Categories
cell?.tableTitleLabel.text = category.value(forKey: HomeKeyConstant.categories_Title) as? String
cell?.tableDescriptionLabel.text = category.value(forKey: HomeKeyConstant.ctegories_Description) as? String
}
// MARK: - FetchedResultsController Delegate
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
homeTableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
homeTableView.endUpdates()
}
func controller(controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .insert:
homeTableView.insertSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .automatic)
case .delete:
homeTableView.deleteSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .automatic)
default: break
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
homeTableView.insertRows(at: [(newIndexPath! as IndexPath)], with: .automatic)
case .delete:
homeTableView.deleteRows(at: [(indexPath! as IndexPath)], with: .automatic)
default: break
}
}
Actually, I am storing 4 indexes of data. While fetching its showing as 50 indexes and crashig
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (50) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
I have a doubt, In my tableview I dont have edit/delete/insert/update options. Just I have to fetch data from database and I have to show in tableview.
So, these NSFetchedResultsControllerDelegate methods are required to implement or not required?
How to fix this crash?
I think the problem here is you you save data to your database. When you saved, you don't wait for them to finish and update your TableviewController. So if you have a little data to save. This code will work otherwise, it's will be crashed.
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)
}
I've been playing with Core Data for the past 18 hours or so. I'm fetching data with NSFetchedResultsController and shows data with UITableView. Adding a new record and deleting the selected record aren't my problems.
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - Instance variables
private let persistentContainer = NSPersistentContainer(name: "Profiles") // core data model file (.xcdatamodeld)
var managedObjectContext: NSManagedObjectContext?
// MARK: - IBOutlets
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// loading persistentContainer //
persistentContainer.loadPersistentStores { (persistentStoreDescription, error) in
if let error = error {
print("Unable to Load Persistent Store")
} else {
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
}
// notifications //
NotificationCenter.default.addObserver(self, selector: #selector(profileDidUpdate), name: NSNotification.Name(rawValue: "HomeViewControllerPictureDidSelect"), object: nil)
}
// MARK: - fetchedResultsController(controller with the entity)
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Person> = {
// Create Fetch Request with Entity
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]
// Create Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
return fetchedResultsController
}()
// MARK: - fetchedResultsController
// MARK: - Notifications
#objc func profileDidUpdate(notification: NSNotification) {
let profile = notification.object as! Profile
let context = persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Person", in: context)
let newPerson = NSManagedObject(entity: entity!, insertInto: context)
newPerson.setValue(profile.uuid, forKey: "uuid") // uuid is used to make each record unique
newPerson.setValue(profile.firstName, forKey: "firstName")
newPerson.setValue(profile.lastName, forKey: "lastName")
newPerson.setValue(profile.age, forKey: "age")
newPerson.setValue(profile.pictData, forKey: "pictData")
do {
try context.save()
print("saved...")
} catch {
print("failed saving")
}
}
// MARK: - Notifications
}
extension HomeViewController: 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 .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
default:
print("...")
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
}
}
Shown above, I create a new record from another view controller, which sends an object of a model (Profile) to the current view controller (HomeViewController). I don't have to reload the table thanks to NSFetchedResultsController.
The entity has several attributes (age, firstName, lastName, pictData, uuid). And I want to change the selected record in the list with two attributes: firstName and lastName. The uuid attribute is used to identify a specific record.
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBAction func editTapped(_ sender: UIButton) {
guard let indexPath = tableView.indexPathForSelectedRow else {
return
}
let selectedRow = indexPath.row
if selectedRow >= 0 {
editRecord(index: selectedRow)
}
}
func editRecord(index: Int) {
let indexPath = IndexPath(row: index, section: 0)
let person = fetchedResultsController.object(at: indexPath)
let uuid = person.uuid!
let context = self.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "uuid == %#", uuid)
do {
let result = try context.fetch(fetchRequest)
if (result.count > 0) {
let managedObject = result[0] as! NSManagedObject
managedObject.setValue("Donald", forKey: "firstName")
managedObject.setValue("Washington", forKey: "lastName")
try context.save()
print("Changes saved...")
}
} catch {
print("Failed")
}
}
}
Now, if I click on the edit button, the app won't update the list immediately. If I restart the app, I see changes. So how can I update the table with NSFetchedResultsController when I make changes to the selected record? Thanks.
Since you're using the NSFetchedResultsControllerDelegate, you need to handle (for your particular use case), the following cases for the NSFetchedResultsChangeType in your didChange method:
insert
delete
update
Your function should look something like this:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch (type) {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
tableView.reloadRows(at: [indexPath], with: .automatic)
break;
default:
print("...")
}
}
I have UICollectionView that linked to CoreData entity via NSFetchedResultsController.
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == .insert {
blockOperations.append(BlockOperation(block: {
self.MessagesCollectionView?.insertItems(at: [newIndexPath!])
}))
}
if type == .update {
blockOperations.append(BlockOperation(block: {
self.MessagesCollectionView?.reloadItems(at: [newIndexPath!])
}))
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let count = fetchedResultsControler.sections?[0].numberOfObjects
return count!
}
UICollection updates properly when i am inserting unique data into entity. But when i insert data that conflicts with unique constraint - new record in entity does not inserts (its ok), but UICollection didChange method gets '.insert' type of operation and inserts empty row!!! How to figure out this constraint issue in didChange method?
I can check existent of record in DB layer class before inserting, but in this case constraints are useless...
Here is the NSFetchResultController code:
lazy var fetchedResultsControler in UICollectionView: NSFetchedResultsController<ChatMessages> = {
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ChatMessages")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "message_created", ascending : true)]
fetchRequest.includesPendingChanges = false
let frc = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
frc.delegate = self
return frc as! NSFetchedResultsController<ChatMessages>
}()
Here is the core data insert code:
let newValue = NSEntityDescription.insertNewObject(forEntityName: "ChatMessages", into: context) as NSManagedObject
newValue.setValue(text, forKey: "text")
newValue.setValue(message_id, forKey:"message_id")
let parentContext = delegate.persistentContainer.viewContext
parentContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
context.parent = parentContext
context.performAndWait {
do
{
dump(value)
try context.save()
print("Obj prepared for saving:")
print(value)
parentContext.performAndWait {
do {
try parentContext.save()
} catch {
print("Failture to save context: \(error)")
parentContext.rollback()
}
}
}
catch
{
print("Object exists...")
context.rollback()
}
}
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.