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
}
1

There are 1 best solutions below

0
aashish tamsya On

This error is shown when you pass an index greater than the total count present in Realm Object.

  1. Check whether you Realm DB contains that entry which you are displaying on Tableview.
  2. 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)'

  1. Log Home Directory on console to get the realm.db file using this code:

    let path = NSHomeDirectory().appending("/Documents/") print(path)