#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:
Пара быстрых заметок:
- Большинство вызовов Firebase являются асинхронными (ознакомьтесь с этой статьей, чтобы понять, почему), поэтому ваш вызов
Auth.auth().currentUser
, скорее всего, вернетсяnil
. Вместо этого вы должны зарегистрироватьAuthenticationStateListener
. Посмотрите этот пример кода, чтобы увидеть, как это делается. - Вместо создания экземпляра пустого
User
экземпляра сделайтеcurrentUserInformation
необязательным - Сопоставление данных намного проще с помощью поддержки 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)")
}
}
- Избегайте принудительного разворачивания (используя! оператор), используйте необязательное разворачивание (с помощью ? оператор) и оператор объединения с нулем (??) вместо