Как получить данные из Firestore, как только появится SwiftUI view?

#swift #firebase #asynchronous #swiftui #observedobject

#swift #firebase #асинхронный #swiftui #observedobject

Вопрос:

У меня есть этот класс CurrentUser , который управляет текущим зарегистрированным пользователем и извлекает данные для этого пользователя из Firebase.

Одним из CurrentUser атрибутов является userEventIDs . У меня также есть коллекция Events документов. У каждого пользователя есть свой собственный массив идентификаторов событий, которые соответствуют событиям в Events коллекции в моей базе данных Firestore.

В структуре MyAccount представления у меня есть onAppear метод, который запрашивает Events коллекцию на основе массива currentUser ‘s eventIds , возвращает эти события, а затем сортирует их, чтобы они были либо до, либо после сегодняшнего дня, в зависимости от даты события.

В настоящее время eventIds загрузка выполняется при первом открытии этого представления, но запрос из событий дважды возвращается пустым, и только после переключения представления на другое и возврата к MyAccount представлению страница будет заполнена этими событиями.

Могу ли я что-нибудь сделать, чтобы события загружались при первом открытии представления?

CurrentUser

 class CurrentUser: ObservableObject {
  let user = Auth.auth().currentUser
  
  @Published var currentUserInformation = User(id: "", name: "", email: "'", accountType: "", profPicURL: "", coverPhotoURL: "", numberFollowers: nil, description: nil, location: nil, websiteLink: nil, orgID: nil, userEventIDs: [String](), userEvents: [Event]())
  
  init() {
    getUserInformation()
  }
  
  func getUserInformation() {
    let UID = user!.uid
    let database = Firestore.firestore()
    database.collection("Organizers").whereField("Organizer ID", isEqualTo: UID).getDocuments() { (querySnapshot, err) in
      
      if err != nil {
        print("Error getting documents: (err!)")
      }
      
      for document in querySnapshot!.documents {
        self.currentUserInformation.id = document.documentID
        self.currentUserInformation.name = document.get("Organization Name") as! String
        self.currentUserInformation.email = document.get("Email") as! String
        self.currentUserInformation.accountType = document.get("Account Type") as! String
        self.currentUserInformation.profPicURL = document.get("Profile Pic URL") as! String
        self.currentUserInformation.coverPhotoURL = document.get("Cover Pic URL") as! String
        self.currentUserInformation.numberFollowers = (document.get("Number of Followers") as! Int)
        self.currentUserInformation.description = (document.get("Organization Description") as! String)
        self.currentUserInformation.websiteLink = (document.get("Organization Website Link") as! String)
        self.currentUserInformation.location = (document.get("Organization Location") as! String)
        self.currentUserInformation.orgID = (document.get("Organizer ID") as! String)
        self.currentUserInformation.userEventIDs = (document.get("Events") as! [String])
        self.currentUserInformation.accountType = "Organizer"
      }
    }
    
    if self.currentUserInformation.id == "" {
      database.collection("Activists").whereField("UID", isEqualTo: UID).getDocuments() { (querySnapshot, err) in
        
        if err != nil {
          print("Error getting documents: (err!)")
        }
        
        for document in querySnapshot!.documents {
          
          self.currentUserInformation.id = document.documentID
          let firstName = document.get("First Name") as! String
          let lastName = document.get("Last Name") as! String
          self.currentUserInformation.name = "(firstName) (lastName)"
          self.currentUserInformation.email = document.get("Email") as! String
          self.currentUserInformation.accountType = "Activist"
          self.currentUserInformation.profPicURL = document.get("Profile Pic") as! String
          self.currentUserInformation.userEventIDs = (document.get("Events") as! [String])
          
        }
      }
    }
  }
  
  func getUserEvents() {
    let database = Firestore.firestore()
    let eventRef = database.collection("Events")
    for eventID in self.currentUserInformation.userEventIDs {
      for event in self.currentUserInformation.userEvents {
        if event.id == eventID {
          break
        }
      }
      eventRef.document(eventID).getDocument() { (document, error) in
        if let document = document {
          let id = document.documentID
          let eventTitle = document.get("Name") as! String
          let organizer = document.get("Organizer") as! String
          let organizerID = document.get("Organizer ID") as! String
          let eventDescription = document.get("Description") as! String
          let date = document.get("Date") as! String
          let time = document.get("Time") as! String
          let location = document.get("Location") as! String
          let numAttending = document.get("Number Attending") as! Int
          let eventPhotoURL = document.get("Event Photo URL") as! String
          self.currentUserInformation.userEvents.append(Event(id: id, eventTitle: eventTitle, eventOrganizer: organizer, eventOrganizerID: organizerID, eventDescription: eventDescription, date: date, time: time, location: location, numAttending: numAttending, eventPhotoURL: eventPhotoURL))
        } else {
          print("Document does not exist")
        }
      }
    }
  }
}
 

Вид

 .onAppear() {
    if currentActivist.currentUserInformation.userEvents.count != currentActivist.currentUserInformation.userEventIDs.count {
    currentActivist.getUserEvents()
      print("Getting user events")
    }
    pastEvents = MyAccountActivistView.getSortedEvent(actEvents: currentActivist.currentUserInformation.userEvents)["Past"]!
    futureEvents = MyAccountActivistView.getSortedEvent(actEvents: currentActivist.currentUserInformation.userEvents)["Upcoming"]!
  }
 

Ответ №1:

Пара быстрых заметок:

  1. Большинство вызовов Firebase являются асинхронными (ознакомьтесь с этой статьей, чтобы понять, почему), поэтому ваш вызов Auth.auth().currentUser , скорее всего, вернется nil . Вместо этого вы должны зарегистрировать AuthenticationStateListener . Посмотрите этот пример кода, чтобы увидеть, как это делается.
  2. Вместо создания экземпляра пустого User экземпляра сделайте currentUserInformation необязательным
  3. Сопоставление данных намного проще с помощью поддержки Firestore для Codable. Я много писал об этом, но суть в том, что вы сможете сопоставлять документы с помощью одной строки кода (вместо того, чтобы вручную сопоставлять каждое отдельное поле). В документации Firestore на самом деле есть хороший фрагмент кода, который вы можете использовать:
 let docRef = db.collection("cities").document("BJ")

docRef.getDocument { (document, error) in
    // Construct a Result type to encapsulate deserialization errors or
    // successful deserialization. Note that if there is no error thrown
    // the value may still be `nil`, indicating a successful deserialization
    // of a value that does not exist.
    //
    // There are thus three cases to handle, which Swift lets us describe
    // nicely with built-in Result types:
    //
    //      Result
    //        /
    //   Error  Optional<City>
    //               /
    //            Nil  City
    let result = Result {
      try document?.data(as: City.self)
    }
    switch result {
    case .success(let city):
        if let city = city {
            // A `City` value was successfully initialized from the DocumentSnapshot.
            print("City: (city)")
        } else {
            // A nil value was successfully initialized from the DocumentSnapshot,
            // or the DocumentSnapshot was nil.
            print("Document does not exist")
        }
    case .failure(let error):
        // A `City` value could not be initialized from the DocumentSnapshot.
        print("Error decoding city: (error)")
    }
}
 
  1. Избегайте принудительного разворачивания (используя! оператор), используйте необязательное разворачивание (с помощью ? оператор) и оператор объединения с нулем (??) вместо