SwiftUI не обновляет переменную после асинхронного вызова firebase

# #ios #swift #firebase #google-cloud-firestore #swiftui

Вопрос:

Я пытаюсь реализовать модель дружбы с firebase и swiftui. В одном из моих представлений, при появлении вызова, я вызываю метод проверки статуса дружбы, как показано ниже:

Редактировать: я сделал isFriend опубликованной переменной, которая должна обновляться следующим образом

 

class UserViewModel : ObservableObject{
    
     .... 
    //check friendship variabe
    @Published var isFriend = 0
 func checkFriendRequest(otherUserUID: String) -> Int{
        
        //0 if not found in friend list or friend request
        //1 means theyre friends
        //2 means that user sent self/me a friend request
        print("otherUserUID is (otherUserUID)")
        var pendingFriendRequests: [String: Bool] = [:]
        self.ref.collection("Users").document(self.uid).getDocument(){
            (document, err) in
            
            if let err = err {
                print("Error getting documents (err)")
            } else {
                pendingFriendRequests = document!.data()!["friends"] as! [String : Bool]
                
                print("pendingFriendRequests is (pendingFriendRequests)")

                for key in pendingFriendRequests.keys {
                    if key == otherUserUID{
                        print("key is (key) and value is (pendingFriendRequests[key])")

                        if pendingFriendRequests[key] == true {
                            self.isFriend = 1
                        }else if pendingFriendRequests[key] == false {
                            self.isFriend = 2
                            
                        }
                    }
                }
            }
        }
        return self.isFriend
    }
}
 

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

 
struct OtherUser: View {
    @StateObject var userData = UserViewModel()

    var otherUserUID: String
    var otherUserName: String
    var otherUserUsername: String
    var otherUserPic: String
    
    @Environment(.presentationMode) var present

    
    init(_ otherUserUID: String, _ otherUserName: String, _ otherUserUsername: String, _ otherUserPic: String){
        self.otherUserUID = otherUserUID
        self.otherUserName = otherUserName
        self.otherUserUsername = otherUserUsername
        self.otherUserPic = otherUserPic
        

    }
    var body: some View {
        ZStack(alignment: .top){
            Color("Black")
                .ignoresSafeArea()
            VStack {
                if (userData.checkFriendRequest(otherUserUID: otherUserUID)) == 1 {
                   //button for if returned 1
              }else if (userData.checkFriendRequest(otherUserUID: otherUserUID)) == 2 {
                   //button for if returned 2
              }else {
             
              }

            }
        }
    }
}
 

Однако при появлении представления оно автоматически возвращает 0 без обновления значения после асинхронного вызова firebase. Я искал другие решения и пробовал группы отправки, но, похоже, они не работают. Я понимаю, что это как-то связано с асинхронной природой firebase, но хотел бы получить некоторую помощь, чтобы понять, как это улучшить, чтобы переменная isFriend обновлялась, а затем возвращалась до появления представления. Спасибо за вашу помощь

Комментарии:

1. «…обновляется, а затем возвращается до появления представления» То, о чем вы спрашиваете, невозможно. Что делать, если сеть работает медленно или не подключена? Вид должен зависать/блокироваться? Лучшее, что вы можете сделать, это обновить объект @State или @ObservedObject, который (автоматически) запускает представление для обновления с новыми результатами. Вы не показываете свой код просмотра…

2. Спасибо за ответ, я отредактировал свой код, чтобы включить опубликованный/наблюдаемый объект, но он все равно не работал и возвращает только 0. Я показал свой код представления, который я хотел бы изменить в зависимости от того, возвращает ли он 0, 1 или 2. Спасибо

3. Вызовы Firebase, такие как getDocument (), являются асинхронными. Вы не можете вернуть значение в конце функции. Вместо if (userData.checkFriendRequest) того, чтобы вы должны были использовать if userData.isFriend .

4. Где бы я тогда вызвал функцию checkFriendRequest() для обновления переменной isFriend?

5. @Харикришнасентхилкумар традиционно, что-то вроде onAppear

Ответ №1:

Представления SwiftUI автоматически запускают обновления в соответствии с состоянием их представлений. Поскольку вызовы Firebase являются асинхронными, вы не можете ожидать, что синхронный вызов функции выборки вернется с правильным результатом.

Вместо этого вам необходимо обновить опубликованное значение (в основном потоке), которое затем передаст сообщение об обновлении всем подписчикам этого значения. SwiftUI автоматически обрабатывает механизм подписки с помощью объекта состояния или объекта наблюдения и запускает новый запрос для основной части представления.

Чтобы ваш код работал с SwiftUI, настройте checkFriendRequest следующим образом:

Измените это:

             if pendingFriendRequests[key] == true {
                self.isFriend = 1
            } else if pendingFriendRequests[key] == false {
                self.isFriend = 2
            }
 

Кому: (Потому что он запускает события пользовательского интерфейса, и весь пользовательский интерфейс должен выполняться в главном потоке)

           DispatchQueue.main.async {
                if pendingFriendRequests[key] == true {
                    self.isFriend = 1
                } else if pendingFriendRequests[key] == false {
                    self.isFriend = 2
                }
          }
 

По вашему мнению, измените VStack на:

       VStack {
          if userData.isFriend == 1 {
               //button for if returned 1
          } else if userData.isFriend == 2 {
               //button for if returned 2
          } else {
         
          }
      }.onAppear() {
          userData.checkFriendRequest(otherUserUID: otherUserUID)
      }
 

Комментарии:

1. Проверьте свои if предложения на наличие несоответствующих скобок.

2. удалены ненужные скобки.