eyeezzi eyeezzi - 24 days ago 22
Swift Question

FirebaseTableViewDataSource crashes on user signout and signin

My app has a UITableViewController which uses a FirebaseTableViewDataSource (from FirebaseUI). The table shows the user's bookmarked posts correctly, but when I log that user off, and log another user in, the app crashes with the following message:


* 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 (1) must be equal to the number of rows contained in that section before the update (2), 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 suspect the problem is related to how FirebaseUI updates the content of the tableView. I've being debugging this for the past 4 days; searched the SO questions; read the relevant docs but none mentions this unique issue. Any help will be much appreciated.

The Details (Sorry it's actually long)

This is my database structure:

|
+-posts
| |
| +-$postid
| |
| +-/* properties of a post: title, text, etc */
|
+-users
|
+-$userid
|
+-bookmarks
|
+-post1: true
|
+-post2: true


I am using FirebaseUI to show a user his/her bookmarks by passing the
users/$userid/bookmarks
ref to FirebaseTableViewDataSource as a query. Then for each key, I observe a single value event on
posts/$postid
in order to retrieve the post details...code below:

self.authStateListenerHandle = FIRAuth.auth()?.addStateDidChangeListener { auth, user in

guard let user = user else {
return
}

self.query = FIRDatabase.database().reference().child("users").child(user.uid).child("bookmarks")

self.tableViewDataSource = FirebaseTableViewDataSource(query: self.query!, view: self.tableView, populateCell: { (tableView, indexPath, snapshot) -> UITableViewCell in

if snapshot.exists() {

let postRef = FIRDatabase.database().reference().child("posts").child(snapshot.key)

let cell = tableView.dequeueReusableCell(withIdentifier: BookmarksVC.reuseIdentifier, for: indexPath) as! MiniTableCell

postRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let post = Event(snapshot: snapshot)!
let postVM = EventViewModel(post: post)
cell.populateCellWithEvent(postVM)
cell.delegate = self
}
})

return cell
}
else {
return UITableViewCell()
}
})

self.tableView.dataSource = self.tableViewDataSource
}


I put the above code in viewDidAppear and then remove the authStateListenerHandle in viewWillDisappear like so

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let handle = self.authStateListenerHandle {
FIRAuth.auth()?.removeStateDidChangeListener(handle)
}
}


Almost everything works fine except, when I am viewing the bookmarks for a user, then log out and log back in, the app crashes with the message


* 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 (1) must be equal to the number of rows contained in that section before the update (2), 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).'
*


Answer

In the viewWillDisappear set the self.tableViewDataSource = nil. So, that you don't update the dataSource improperly.

Comments