Using 2 FRCs with one TableView and configuring the FRCDelegate
Asked Answered
K

0

2

I found this post question link, and I have done my best to convert it from Obj-C to Swift. When I added a new Entity and try to save it to CoreData, the app crashes and highlights this line:

modifiedIndexPath = NSIndexPath(row: (indexPath?.row)!, section: 0)

The error in green behind it is as follows: Thread 1; EXC_BREAKPOINT (code=1, subcode=0x100008b118). I have no idea what this means, and the output panel only shows (llbd) in green as well.

EDIT - I figured out that the app is crashing because indexPath?.row is returning nil. I just don't know why it's returning nil.

My VC has 2 sections being set like this:

    func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}

And the title for header being set like this:

// set the titles of the tables
func tableView( _ tableView : UITableView,  titleForHeaderInSection section: Int)->String? {

    switch(section) {
    case 0:
        if(positiveFetchedResultsController.fetchedObjects!.count == 0){
            return nil
        }
        else{
             return "Owes you:"
        }
    case 1:
        if(negativeFetchedResultsController.fetchedObjects!.count == 0){
            return nil
        }
        else{
            return "You owe:"
        }

    default :return nil
    }
}

Here is how I am creating both of my RFCs

override func viewWillAppear(_ animated: Bool) {
    // load the data

    let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
    fetchRequest.predicate = NSPredicate(format:"[email protected] >= 0")
    let sort = NSSortDescriptor(key: #keyPath(Person.name), ascending: true)
    fetchRequest.sortDescriptors = [sort]
    positiveFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: coreDataStack.managedContext, sectionNameKeyPath: nil, cacheName: nil)

    do{
     try positiveFetchedResultsController.performFetch()
     }catch let error as NSError{
        print("Fetching error: \(error), \(error.userInfo)")
    }

    let negativFetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
    negativFetchRequest.predicate = NSPredicate(format:"[email protected] < 0")
    let negativeSort = NSSortDescriptor(key: #keyPath(Person.name), ascending: true)
    negativFetchRequest.sortDescriptors = [negativeSort]
    negativeFetchedResultsController = NSFetchedResultsController(fetchRequest: negativFetchRequest, managedObjectContext: coreDataStack.managedContext, sectionNameKeyPath: nil, cacheName: nil)

    do{
        try negativeFetchedResultsController.performFetch()
    }catch let error as NSError{
        print("Fetching error: \(error), \(error.userInfo)")
    }

    positiveFetchedResultsController.delegate = self
    negativeFetchedResultsController.delegate = self

    print("\(positiveFetchedResultsController.fetchedObjects!.count) positive fetch count")
    //print("\(positiveFetchedResultsController.fetchedObjects![0].statement!.count) positive statements count")
    print("\(negativeFetchedResultsController.fetchedObjects!.count) negative fetch count")
    //print("\(negativeFetchedResultsController.fetchedObjects![0].statement!.count) negative statements count")

}

Here is the FRC delegate code that I converted from the post above:

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?) {

    var modifiedIndexPath = IndexPath()
    var modifiedNewIndexPath = IndexPath()


    if(controller == positiveFetchedResultsController){
        modifiedIndexPath = IndexPath(row: (indexPath?.row)!, section: 0)
        modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 0)
    }

    if(controller == negativeFetchedResultsController){
        modifiedIndexPath = IndexPath(row: (indexPath?.row)!, section: 1)
        modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 1)
    }

    switch type {
    case .insert:
        //tableView.insertRows(at: [newIndexPath!], with: .automatic)
        tableView.insertRows(at: [modifiedNewIndexPath as IndexPath], with: .automatic)
    case .delete:
        //tableView.deleteRows(at: [indexPath!], with: .automatic)
        tableView.deleteRows(at: [modifiedIndexPath as IndexPath], with: .automatic)
    case .update:
        tableView.reloadRows(at: [modifiedIndexPath as IndexPath], with: .automatic)
    case .move:
        //tableView.deleteRows(at: [indexPath!], with: .automatic)
        //tableView.insertRows(at: [newIndexPath!], with: .automatic)

        tableView.deleteRows(at: [modifiedIndexPath as IndexPath], with: .automatic)
        tableView.insertRows(at: [modifiedNewIndexPath as IndexPath], with: .automatic)

    }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
}

}

EDIT It seems that the IndexPath.row is return nil for both indexPath and newIndexPath. I tried to prevent this from happening with this code, but I don't know if I'm heading in the right direction with this thought process:

        print("\(indexPath?.row) is the index row number")
    print("\(newIndexPath?.row) is the NEW index row number")

    if(controller == self.positiveFetchedResultsController){


        if(indexPath?.row == nil)
        {
           modifiedNewIndexPath = IndexPath(row: 0, section: 0)
            modifiedNewIndexPath = IndexPath(row: 0, section: 0)
        }
        else{
            modifiedIndexPath = IndexPath(row: indexPath!.row, section: 0)
            modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 0)
        }
    }

    if(controller == negativeFetchedResultsController){
        if(indexPath?.row == nil)
        {
            modifiedIndexPath = IndexPath(row: 0, section: 1)
            modifiedNewIndexPath = IndexPath(row: 0, section: 1)
        }
        else{
            modifiedIndexPath = IndexPath(row: indexPath!.row, section: 1)
            modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 1)
        }

    }

And here is how I am trying to save my data:

/* STEP 2 - Check to see if that persons names was already used */
    let personFetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
    personFetchRequest.predicate = NSPredicate(format: "name == %@", name.text!)

    do{
        let results = try coreDataStack.managedContext.fetch(personFetchRequest)
        print("\(results.count)  this is the results count for the predicate")
        guard results.count == 0 else{
            // print("The Same Name is Used")
            warningText = "You already used that name."
            warningMessage()
            return
        }

        /* STEP 3 - Use the name that was typed in */
        print("You can use that name")
        //let personEntity = NSEntityDescription.entity(forEntityName: "Person", in: coreDataStack.managedContext)
        let person = Person(entity: Person.entity(), insertInto: coreDataStack.managedContext)
        person.name = name.text

        /* STEP 4 - Save the data */
        coreDataStack.saveContext()
        self.dismiss(animated: true, completion: {});

    }catch{
        print("Error")
    }

It seems to crash right after the this print statement

print("You can use that name")

I'm not able to save to CoreData, and I'm not sure why.

EDIT This seems to have fixed my issue, I'm still testing but it seems to be working.

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

    var modifiedIndexPath = IndexPath()
    var modifiedNewIndexPath = IndexPath()

    print("\(indexPath?.row) is the index row number")
    print("\(newIndexPath?.row) is the NEW index row number")

    if(controller == self.positiveFetchedResultsController){


        if(indexPath?.row == nil)
        {
            modifiedNewIndexPath = IndexPath(row: 0, section: 0)
            modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 0)
        }
        else if(newIndexPath?.row == nil){
            modifiedIndexPath = IndexPath(row: indexPath!.row, section: 0)
            modifiedNewIndexPath = IndexPath(row: 0, section: 0)
        }
        else{
            modifiedIndexPath = IndexPath(row: indexPath!.row, section: 0)
            modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 0)
        }
    }

    if(controller == negativeFetchedResultsController){
        if(indexPath?.row == nil)
        {
            modifiedIndexPath = IndexPath(row: 0, section: 1)
            modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 1)
        }
        else if(newIndexPath?.row == nil)
        {
            modifiedIndexPath = IndexPath(row: indexPath!.row, section: 1)
            modifiedNewIndexPath = IndexPath(row: 0, section: 1)
        }
        else{
            modifiedIndexPath = IndexPath(row: indexPath!.row, section: 1)
            modifiedNewIndexPath = IndexPath(row: (newIndexPath?.row)!, section: 1)
        }

    }

    print("\(indexPath?.row) is the index row number")
    print("\(newIndexPath?.row) is the NEW index row number")

    switch type {
    case .insert:
        print("\(newIndexPath?.row) is the NEW index row number")
       // tableView.insertRows(at: [newIndexPath!], with: .automatic)

        tableView.insertRows(at: [modifiedNewIndexPath as IndexPath], with: .automatic)
    case .delete:
        //tableView.deleteRows(at: [indexPath!], with: .automatic)
        tableView.deleteRows(at: [modifiedIndexPath as IndexPath], with: .automatic)
    case .update:
        tableView.reloadRows(at: [modifiedIndexPath as IndexPath], with: .automatic)
    case .move:
        //tableView.deleteRows(at: [indexPath!], with: .automatic)
        //tableView.insertRows(at: [newIndexPath!], with: .automatic)

        tableView.deleteRows(at: [modifiedIndexPath as IndexPath], with: .automatic)
        tableView.insertRows(at: [modifiedNewIndexPath as IndexPath], with: .automatic)
    }
}

Seems like allot of if/else statements for this type of thing. Hoping someone might have a cleaner version of this.

Kissee answered 24/12, 2016 at 4:21 Comment(4)
let person = Person(entity: Person.entity(), insertInto: coreDataStack.managedContext) person.name = name.text here is problemKimball
Why is this a problem?Kissee
did you get data in Person.entity(),Kimball
Yes the data saves.Kissee

© 2022 - 2024 — McMap. All rights reserved.