# #swift #firebase #google-cloud-firestore #combine
Вопрос:
В настоящее время я занимаюсь этой проблемой. У меня есть база данных Firestore. Моя цель-заполнить friends
массив User
сущностями после извлечения. Я вызываю fetchFriends
, который выбирает текущего зарегистрированного пользователя, в котором есть friends
массив (каждый элемент-идентификатор друга). friends
затем массив зацикливается, извлекается каждый идентификатор друга и создается новая сущность User
. Я хочу сопоставить этот friends
массив с friends
опубликованной переменной. То, что я там сделал, не работает, и я не могу придумать какое-то решение.
База данных Firestore
User
- name: String
- friends: [String]
Модель пользователя
struct User: Identifiable, Codable {
@DocumentID var id: String?
var name: String?
var email: String
var photoURL: URL?
var friends: [String]?
}
Модель представления пользователя
@Published var friends = [User?]()
func fetchFriends(uid: String) {
let userRef = db.collection("users").document(uid)
userRef.addSnapshotListener { documentSnapshot, error in
do {
guard let user = try documentSnapshot?.data(as: User.self) else {
return
}
self.friends = user.friends!.compactMap({ friendUid in
self.fetchUserAndReturn(uid: friendUid) { friend in
return friend
}
})
}
catch {
print(error)
}
}
}
func fetchUserAndReturn(uid: String, callback:@escaping (User)->User) {
let friendRef = db.collection("users").document(uid)
friendRef.getDocument { document, error in
callback(try! document?.data(as: User.self) as! User)
}
}
Комментарии:
1. Где именно в этой цепочке событий она обрывается? Поместите
User
объект фактически инициализирован, что массив фактически зациклен и т. Д. Где-то по пути он будет напечатан неправильно и сообщит нам, где это находится.2. @MJ-12 Отличное предложение. Я бы также добавил точку останова и прошелся по вашему коду строка за строкой, изучая переменные по пути. Как только вы найдете тот, который не соответствует вашим ожиданиям, обновите этот вопрос, указав, какая это была строка и каким должно было быть ожидаемое значение.
3. Разве основная проблема здесь не в том, что
self.friends
ожидается заполнение повторяющимися вызовамиfetchUserAndReturn
, которые также являются асинхронными (или, в частности,getDocument
функция внутри них) и не вернут aUser
вcompactMap
ожидаемое состояние?4. в строке
self.fetchUserAndReturn
я теперь получаю это сообщение об ошибке: «Не удается преобразовать значение типа ‘()’ в тип результата закрытия ‘Пользователь??'»5. @jnpdx это может быть оно. У вас есть какие-нибудь идеи, как заставить это работать? Я пытался добавлять в массив self.friends каждый раз, когда получал результат, но он странно работал в поле зрения (из — за добавления в массив мне приходилось очищать self.friends каждый раз, когда в базу данных вносились новые изменения, и из-за этого каждая перезагрузка представления сначала очищала все содержимое, а затем заполняла его подвидами-это выглядело не очень хорошо).
Ответ №1:
Один из вариантов-использовать группы диспетчеризации для группирования чтения пользователей, но на самом деле код в вопросе не так уж далек.
На самом деле в compactMap нет необходимости, так как идентификаторы пользователей уникальны, как и идентификаторы документов в одной коллекции, поэтому не должно быть проблем с дублированием идентификаторов пользователей в качестве друзей.
Используя объект пользователя в вопросе, вот как заполнить массив друзей
func fetchFriends(uid: String) {
let userRef = db.collection("users").document(uid)
userRef.addSnapshotListener { documentSnapshot, error in
guard let user = try! documentSnapshot?.data(as: User.self) else { return }
user.friends!.forEach( { friendUid in
self.fetchUserAndReturn(uid: friendUid, completion: { returnedFriend in
self.friendsArray.append(returnedFriend)
})
})
}
}
func fetchUserAndReturn(uid: String, completion: @escaping ( MyUser ) -> Void ) {
let userDocument = self.db.collection("users").document(uid)
userDocument.getDocument(completion: { document, error in
guard let user = try! document?.data(as: User.self) else { return }
completion(user)
})
}
Обратите внимание, что я удалил всю проверку ошибок для краткости, поэтому обязательно включите проверку на наличие ошибок Firebase, а также нулевых объектов.