SwiftIU/Firebase: как обновлять списки пользователей из Firebase в режиме реального времени и отображать это в пользовательском интерфейсе?

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, а не обратные вызовы, но я не думаю, что это было бы эффективно для решения проблемы в лоб.

Если вы используете прослушиватель моментальных снимков, вы можете использовать либо словарь (с ключом по идентификатору объекта), либо массив, в который вы сопоставляете новые элементы (заменяя элемент совпадающим идентификатором) вместо добавления, что приведет к вам результат, который вы упомянули, где вы получаете повторяющиеся элементы. Async/await был портирован обратно, но он не решит логическую ошибку, связанную с постоянным добавлением к массиву — та же проблема с Combine.

jnpdx 04.04.2022 00:34

Все ObservableObject должны быть завернуты в @ObservedObject в SwiftUI View, чтобы увидеть изменения. Переменные массива ObservableObject не отслеживаются родителем автоматически.

lorem ipsum 04.04.2022 00:40

Не могли бы вы обновить свой вопрос и добавить код для своей структуры User? (Вы можете использовать swift-format.com для форматирования кода).

Peter Friese 04.04.2022 09:03

@PeterFriese Мой пользовательский объект соответствует идентифицируемому и кодируемому. Я понимаю, что вы говорите, и понятно, почему добавление пользователей приводит к дублированию результатов. Как мне назначить контакты массиву (нубский вопрос...)? В настоящее время я извлекаю документы (сохраняет идентификатор пользователя в качестве идентификатора документа) из firebase, а затем выполняет итерацию по ним и сопоставляет их с моей пользовательской моделью, а затем добавляет их в массив.

Tarun Reddy 06.04.2022 01:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
31
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что вы добавляете документы к опубликованному свойству. Это приведет к дублированию записей.

Вместо этого просто назначьте все сопоставленных документов свойству emergencyContactUsers.

Проверьте Отображение данных Firestore в Swift — полное руководство | Питер Фризе для более подробной информации об этом. У меня также есть ряд других сообщений о Firestore и SwiftUI в моем блоге, которые могут быть полезными.

Что касается предупреждения о «дубликатах идентификаторов», которое вы видите, это также может способствовать возникновению проблемы. Списки SwiftUI требуют, чтобы все элементы списка имели уникальный идентификатор, и кажется, что ваши пользовательские объекты могут не иметь уникальных идентификаторов. Это может быть связано либо с тем, что у вас есть несколько копий одного и того же пользователя в опубликованных свойствах, либо с тем, что ваша структура User не поддается идентификации.

Я заметил, что вы используете DispatchQueue.async, чтобы убедиться, что вы находитесь в основной теме. В этом нет необходимости, так как Firestore обязательно вызовет ваш код в основном потоке. См. пояснение в https://twitter.com/peterfriese/status/1489683949014196226.

Кроме того, мне любопытно, что вы сказали в начале видео о том, что не можете найти документацию по этому поводу. Мы всегда ищем способы улучшить документацию Firebase — что вы искали/чего не нашли?

Другие вопросы по теме