Search code examples
iosswiftuicollectionviewrealmxcode8

'NSInternalInconsistencyException' Inserting Item with Collection View Swift 3


So, I am using Realm as a data store, which I'm pretty sure I need to first add content to before inserting an item at index path in a collection view. But I keep getting this all too familiar error:

'NSInternalInconsistencyException', reason: 'attempt to insert item 1 into section -1, but there are only 1 items in section 1 after the update'

Here is my model:

final class Listing: Object {
dynamic var id = ""
dynamic var name = ""
dynamic var item = ""

}

Here is my view controller that conforms to UICollectionView data sources and delegates:

 override func viewDidLoad() {
    super.viewDidLoad()

    // MARK: - Get Listings!

    queryListings()

    // MARK: - Delegates

    self.collectionView.delegate = self
    self.collectionView.dataSource = self
}

// MARK: - Query Listings

func queryListings() {
    let realm = try! Realm()

    let everyListing = realm.objects(Listing.self)
    let listingDates = everyArticle.sorted(byKeyPath: "created", ascending: false)

    for listing in listingDates {
        listing.append(listing)
        self.collectionView.performBatchUpdates({ 
            self.collectionView.insertItems(at: [IndexPath(item: self.listing.count, section: 1)])
        }, completion: nil)
    }
}

Delegates:

 // MARK: UICollectionViewDataSource

 func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return listing.count
}

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ListingCollectionViewCell

    cell.awakeFromNib()

    return cell
}

I've tried every permutation of self.listing.count 0, 1, -1 , +1 as well as section 0, 1, -1, +1 and the exception raised is the same plus or minus the section and items that exist. Calling reloadData() doesn't help either.

Anyone solve this with a collection view?


Solution

  • The lines of code for listing in listingDates { listing.append(listing) } seem a bit unsafe. Either you're referring to separate objects named listing (such as a class property), or that's in reference to the same listing object. If listing is a Realm Results object, it shouldn't be possible to call append on it.

    In any case, you're probably doing a bit more work than you need to. Realm objects, whether they are Object or Results are live, in that they'll automatically update if the underlying data changes them. As such, it's not necessary to perform multiple queries to update a collection view.

    Best practice is to perform the query once, and save the Results object as a property of your view controller. From that point, you can use Realm's Change Notification feature to assign a Swift closure that'll be executed each time the Realm query changes. This can then be used to animate the updates on the collection view:

    class ViewController: UITableViewController {
      var notificationToken: NotificationToken? = nil
    
      override func viewDidLoad() {
        super.viewDidLoad()
        let realm = try! Realm()
        let results = realm.objects(Person.self).filter("age > 5")
    
        // Observe Results Notifications
        notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
          guard let tableView = self?.tableView else { return }
          switch changes {
          case .initial:
            // Results are now populated and can be accessed without blocking the UI
            tableView.reloadData()
            break
          case .update(_, let deletions, let insertions, let modifications):
            // Query results have changed, so apply them to the UITableView
            tableView.beginUpdates()
            tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
                               with: .automatic)
            tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
                               with: .automatic)
            tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
                               with: .automatic)
            tableView.endUpdates()
            break
          case .error(let error):
            // An error occurred while opening the Realm file on the background worker thread
            fatalError("\(error)")
            break
          }
        }
      }
    
      deinit {
        notificationToken?.stop()
      }
    }