Ad

Firebase Items Returning Multiple Times In Collection View On Database Change

When the function to create or delete a list is called inside of my app the remaining lists are duplicated and displayed multiple times within the collectionView until the app is reloaded. I only called the fetchLists function twice, in the viewDidLoad and in the pull to refresh function. On pull to refresh the lists return to normal.

Fetch list:

   fileprivate func fetchLists() {
    self.collectionView?.refreshControl?.endRefreshing()

    guard let currentUid = Auth.auth().currentUser?.uid else { return }

    let ref = Database.database().reference().child("list-feed").child(currentUid)
    ref.observe(.value) { (listFeedSnapshot) in

        guard var allObjects = listFeedSnapshot.children.allObjects as? [DataSnapshot] else { return }

        allObjects.forEach({ (allObjectsSnapshot) in

            let listId = allObjectsSnapshot.key

            let listRef = Database.database().reference().child("lists").child(listId)
            listRef.observeSingleEvent(of: .value, with: { (snapshot) in

                guard let dict = snapshot.value as? [String: Any] else { return }
                guard let uid = dict["uid"] as? String else { return }

                Database.fetchUserWithUID(uid: uid, completion: { (user) in
                    guard let dictionary = snapshot.value as? [String: Any] else { return }

                    var list = List(user: user, dictionary: dictionary)

                    let listId = snapshot.key
                    list.id = snapshot.key
                    self.list = list

                    self.lists.append(list)

                    self.lists.sort(by: { (list1, list2) -> Bool in
                        return list1.creationDate.compare(list2.creationDate) == .orderedDescending
                    })

                    self.collectionView?.reloadData()
                    ref.keepSynced(true)
                    listRef.keepSynced(true)
                })
            })
        })
    }
}

Create list:

 let values = ["uid": uid, "title": listNameText, "creationDate": Date().timeIntervalSince1970] as [String : Any]
 ref.updateChildValues(values, withCompletionBlock: { (error, ref) in
 if let error = error {
                        self.navigationItem.rightBarButtonItem?.isEnabled = true
 print("failed to save user info into db:", error.localizedDescription)
                        return
                    }

let memberValues = [uid : 1]

ref.child("list-members").updateChildValues(memberValues)

self.handleUpdateFeeds(with: ref.key!)
self.handleListFeeds(with: ref.key!)

print("successfully created list in db")

Update feeds:

    func handleUpdateFeeds(with listId: String) {

    guard let uid = Auth.auth().currentUser?.uid else { return }

    let values = [listId: 1]

    Database.database().reference().child("list-feed").child(uid).updateChildValues(values)
}

func handleListFeeds(with listId: String) {

    guard let uid = Auth.auth().currentUser?.uid else { return }

    let values = [listId: 1]

    Database.database().reference().child("user-lists").child(uid).updateChildValues(values)
}

Firebase database:

  {
  "list-feed" : {
  "otxFDz0FNbVPpLN27DYBQVP4e403" : {
  "-LjeAoHJTrYK7xjwcpJ9" : 1,
  "-LjeApq-Mb_d_lAz-ylL" : 1
  }
  },
  "lists" : {
  "-LjeAoHJTrYK7xjwcpJ9" : {
  "creationDate" : 1.5630020966384912E9,
  "title" : "Test 1",
  "uid" : "otxFDz0FNbVPpLN27DYBQVP4e403"
},
"-LjeApq-Mb_d_lAz-ylL" : {
  "creationDate" : 1.563002101329072E9,
  "list-members" : {
    "otxFDz0FNbVPpLN27DYBQVP4e403" : 1
  },
  "title" : "Test 2",
  "uid" : "otxFDz0FNbVPpLN27DYBQVP4e403"
}
}
}

db

Ad

Answer

Since you're calling ref.observe(, you're attaching a permanent observer to the data. This means that if you call fetchLists a second, you're attaching a second observer and you'll get the same data twice.

If you only want the data to be read once per call to fetchLists, you should use observeSingleEventOfType:

fileprivate func fetchLists() {
    self.collectionView?.refreshControl?.endRefreshing()

    guard let currentUid = Auth.auth().currentUser?.uid else { return }

    let ref = Database.database().reference().child("list-feed").child(currentUid)
    ref.observeSingleEvent(of: .value) { (listFeedSnapshot) in

Also see the documentation on reading data once.

Ad
source: stackoverflow.com
Ad