Matching collections in Firestore using SwiftUI

Viewed 22

I'm try to fetch documents from firestore by matching the collections using the code below. I'm fetching the documents from collection("users") in case anyone update their info in the future which the new info will only uploaded to the collection("users") but not the collection("clients").

struct NewFriend: Identifiable, Hashable, Decodable {
    @DocumentID var id: String?
    var name: String
    var uid: String
}

struct NewProspect: Identifiable, Hashable, Decodable {
    @DocumentID var id: String?
    var name: String
    var uid: String
}

class AuthViewModel: ObservableObject {

@Published var userName = ""
@Published var friendName1 = "FriendName1"
@Published var friendName2 = "FriendName2"
@Published var friendUid1 = "friendUid1"
@Published var friendUid2 = "friendUid2"
@Published var matchingUid = ""
let friends = [NewFriend]()
let clients = [NewProspect]()

func uploadInfo() {
        guard let uid = FireBaseManager.shared.auth.currentUser?.uid else { return }
        let data: [String: Any] = [
            "name": self.userName,
            "uid": uid
        ]

        FireBaseManager.shared.firestore.collection("users").document(uid).setData(data) { error in
            if let error = error {
                print("DEBUG: Failed to store user info with error: \(error.localizedDescription)")
                return
            }
        }
    }
}

func addFriend() {
        guard let uid = FireBaseManager.shared.auth.currentUser?.uid else { return }
        let data: [String: Any] = [
            "name": self.friendName1,
            "uid": self.friendUid1
        ]

        FireBaseManager.shared.firestore.collection("users").document(uid).collections("clients").document(self.friendUid1).setData(data) { error in
            if let error = error {
                print("DEBUG: Failed to store user info with error: \(error.localizedDescription)")
                return
            }
        }

func addFriend2() {
        guard let uid = FireBaseManager.shared.auth.currentUser?.uid else { return }
        let data: [String: Any] = [
            "name": self.friendName2,
            "uid": self.friendUid2
        ]

        FireBaseManager.shared.firestore.collection("users").document(uid).collections("clients").document(self.friendUid2).setData(data) { error in
            if let error = error {
                print("DEBUG: Failed to store user info with error: \(error.localizedDescription)")
                return
            }
        }

func fetchFriends() {
        guard let uid = FireBaseManager.shared.auth.currentUser?.uid else { return }
            FireBaseManager.shared.firestore.collection("users").document(uid).collection("clients").getDocuments { snapshot, _ in
                
                guard let documents = snapshot?.documents else { return }
                self.friends = documents.compactMap({ try? $0.data(as: NewFriend.self) })
                
                for i in 0 ..< self.friends.count {
                    self.matchingUid = self.friend[i].uid
                    print("print1: \(self.matchingUid)")

                    FireBaseManager.shared.firestore.collection("users")
                        .whereField("uid", isEqualTo: self.friendUid)
                        .getDocuments { snapshot, _ in
                            guard let documents = snapshot?.documents else { return }
                            self.clients = documents.compactMap({ try? $0.data(as: NewProspect.self) })
                            print("print2: \(self.matchingUid)")
                        }
                }
            }
    }
}

struct ListView: View {

@ObservedObject var viewModel = AuthViewModel()

     var body: some View {

          List {
                       ForEach(viewModel.clients) { client in
                           Text(client.name)
                                .padding()
                       }
               }
               .onAppear {
                   viewModel.fetchFriends()
               }
     }
}

I'm able to print both of the friends' uid in print1 but when it comes to print2 only one of the uid is printed and the list only shows one of the two friends. Any idea why is this happening? Or is there any better way to make this function work?

1 Answers

When you fetch users before print2, you are fetching with a where clause.

self.db.collection("users")
    // This WHERE condition causes that only one friend will be found
    .whereField("uid", isEqualTo: self.friendUid1)
    .getDocuments { snapshot, _ in
        guard let documents = snapshot?.documents else { return }
        self.clients = documents.compactMap({ try? $0.data(as: NewProspect.self) })
        print("print2: \(self.matchingUid)")
    }

The one in your example (self.friendUid) is not part of your AuthViewModel, I assume you meant .whereField("uid", isEqualTo: self.friendUid1)

Anyway, that where clause is limiting your results to one of the friends (in this case the friend with friendUid1 only.

Maybe you can use .whereField("uid", in: [self.friendUid1, self.friendUid2]) instead:

self.db.collection("users")
    // In this case the WHERE condition cares about both friends
    .whereField("uid", in: [self.friendUid1, self.friendUid2])
    .getDocuments { snapshot, _ in
        guard let documents = snapshot?.documents else { return }
        self.clients = documents.compactMap({ try? $0.data(as: NewProspect.self) })
        print("print2: \(self.matchingUid)")
    }
Related