Home > Back-end >  SwiftIU/Firebase: How to update lists of users from Firebase in real time and have it reflect in the
SwiftIU/Firebase: How to update lists of users from Firebase in real time and have it reflect in the

Time:04-05

https://www.loom.com/share/de410c2626644dd796ad407fcee7e5c7

^^^ I've attached a loom video demonstrating the bug im facing as well as the code I currently have.

The problem is that the UI doesn't update right away and may confuse users. All the code in terms of updating the backend function correctly (its the updating of the UI thats not working properly), I'm pretty sure it has to do with the way i'm either calling the functions or the function itself.

Any help would be greatly appreciated!


@Published var userRequestInboxUsers = [User]()
@Published var emergencyContactUsers = [User]()

// function to fetch the user requests
func fetchTheUsersRequests() {

  guard let uid = user.id else { return }
  let query = COLLECTION_FOLLOWERS.document(uid).collection("inbox").whereField(
    "currentStatus", isEqualTo: "isPending")

  query.addSnapshotListener(includeMetadataChanges: true) { snapshot, error in
    if let error = error {
      print("There was an error querying the inbox requests: \(error.localizedDescription)")
    } else {

      for request in snapshot!.documents {

        COLLECTION_USERS.document(request.documentID).getDocument { snapshot, error in
          if let error = error {
            print("There was an error fetching the user data: \(error)")
          } else {
            DispatchQueue.main.async {
              guard let userRequestInInbox = try? snapshot?.data(as: User.self) else { return }
              self.userRequestInboxUsers.append(userRequestInInbox)
            }
          }
        }
      }
    }
  }
}

//function that fetches the users contacts (request that have been approved)
func fetchTheUsersContacts() {
  guard let uid = user.id else { return }

  let query = COLLECTION_FOLLOWERS.document(uid).collection("inbox").whereField(
    "currentStatus", isEqualTo: "emergencyContact")

  query.addSnapshotListener(includeMetadataChanges: true) { snapshot, error in
    if let error = error {
      print("There was an error querying the emergency contacts: \(error.localizedDescription)")
    } else {
      for userContact in snapshot!.documents {
        COLLECTION_USERS.document(userContact.documentID).getDocument { snapshot, error in
          if let error = error {
            print("There was an error fetching the user data: \(error)")
          } else {
            DispatchQueue.main.async {
              guard let userEmergencyContactsInInbox = try? snapshot?.data(as: User.self) else {
                return
              }
              self.emergencyContactUsers.append(userEmergencyContactsInInbox)
            }
          }
        }
      }
    }
  }
}

I've tried calling the function every time the view appears but that leads to duplicated results.

I'm currently using snapshot listeners to get real time access but even then this doesn't work.

I've structured my backend to have a contacts sub collection and a requests sub collection but I get the same problem with much more lines of code...

I've thought of switching to async/await but i would prefer my app be compatible to ios 14 rather than just 15 and up.

I could try using strictly Combine rather than call backs but I don't think that would be effective in attacking the problem head on.

CodePudding user response:

The problem is that you're appending the documents to the published property. This will lead to duplicating the entries.

Instead, just assign all of the mapped documents to the emergencyContactUsers property.

Check out Mapping Firestore Data in Swift - The Comprehensive Guide | Peter Friese for more details about this. I've also got a number of other posts about Firestore and SwiftUI on my blog that might be useful.

As for the "duplicate IDs" warning you see - that might actually also contribute to the issue. SwiftUI lists require all list items to have a unique ID, and it seems like your user objects might not have unique IDs. This might either be due to the fact you have multiple copies of the same user in the published properties, or that your User struct is not identifiable.

I noticed that you're using DispatchQueue.async to make sure you're on the main thread. This is not necessary, as Firestore will make sure to call your code on the main thread. See https://twitter.com/peterfriese/status/1489683949014196226 for an explanation.

Also - I am curious about what you said in the beginning of the video about not being able to find documentation about this. We're always looking for ways to make the Firebase docs better - what did you look for / what didn't you find?

  • Related