https://www.loom.com/share/de410c2626644dd796ad407fcee7e5c7
^^^ Я прикрепил видео о ткацком станке, демонстрирующее ошибку, с которой я столкнулся, а также код, который у меня есть в настоящее время.
Проблема в том, что пользовательский интерфейс не обновляется сразу и может запутать пользователей. Весь код с точки зрения правильного обновления бэкэнд-функции (это обновление пользовательского интерфейса, который не работает должным образом), я почти уверен, что это связано с тем, как я вызываю функции или саму функцию.
Любая помощь будет принята с благодарностью!
@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)
}
}
}
}
}
}
}
Я пытался вызывать функцию каждый раз, когда появляется представление, но это приводит к дублированию результатов.
В настоящее время я использую прослушиватели снимков для получения доступа в реальном времени, но даже тогда это не работает.
Я структурировал свой бэкэнд, чтобы иметь подколлекцию контактов и подколлекцию запросов, но я получаю ту же проблему с гораздо большим количеством строк кода...
Я думал о переключении на async/await, но я бы предпочел, чтобы мое приложение было совместимо с ios 14+, а не только с 15 и выше.
Я мог бы попробовать использовать строго Combine, а не обратные вызовы, но я не думаю, что это было бы эффективно для решения проблемы в лоб.
Все ObservableObject должны быть завернуты в @ObservedObject в SwiftUI View, чтобы увидеть изменения. Переменные массива ObservableObject не отслеживаются родителем автоматически.
Не могли бы вы обновить свой вопрос и добавить код для своей структуры User? (Вы можете использовать swift-format.com для форматирования кода).
@PeterFriese Мой пользовательский объект соответствует идентифицируемому и кодируемому. Я понимаю, что вы говорите, и понятно, почему добавление пользователей приводит к дублированию результатов. Как мне назначить контакты массиву (нубский вопрос...)? В настоящее время я извлекаю документы (сохраняет идентификатор пользователя в качестве идентификатора документа) из firebase, а затем выполняет итерацию по ним и сопоставляет их с моей пользовательской моделью, а затем добавляет их в массив.





Проблема в том, что вы добавляете документы к опубликованному свойству. Это приведет к дублированию записей.
Вместо этого просто назначьте все сопоставленных документов свойству emergencyContactUsers.
Проверьте Отображение данных Firestore в Swift — полное руководство | Питер Фризе для более подробной информации об этом. У меня также есть ряд других сообщений о Firestore и SwiftUI в моем блоге, которые могут быть полезными.
Что касается предупреждения о «дубликатах идентификаторов», которое вы видите, это также может способствовать возникновению проблемы. Списки SwiftUI требуют, чтобы все элементы списка имели уникальный идентификатор, и кажется, что ваши пользовательские объекты могут не иметь уникальных идентификаторов. Это может быть связано либо с тем, что у вас есть несколько копий одного и того же пользователя в опубликованных свойствах, либо с тем, что ваша структура User не поддается идентификации.
Я заметил, что вы используете DispatchQueue.async, чтобы убедиться, что вы находитесь в основной теме. В этом нет необходимости, так как Firestore обязательно вызовет ваш код в основном потоке. См. пояснение в https://twitter.com/peterfriese/status/1489683949014196226.
Кроме того, мне любопытно, что вы сказали в начале видео о том, что не можете найти документацию по этому поводу. Мы всегда ищем способы улучшить документацию Firebase — что вы искали/чего не нашли?
Если вы используете прослушиватель моментальных снимков, вы можете использовать либо словарь (с ключом по идентификатору объекта), либо массив, в который вы сопоставляете новые элементы (заменяя элемент совпадающим идентификатором) вместо добавления, что приведет к вам результат, который вы упомянули, где вы получаете повторяющиеся элементы. Async/await был портирован обратно, но он не решит логическую ошибку, связанную с постоянным добавлением к массиву — та же проблема с Combine.