异步firebase调用后SwiftUI不更新变量

Posted

技术标签:

【中文标题】异步firebase调用后SwiftUI不更新变量【英文标题】:SwiftUI not updating variable after async firebase call 【发布时间】:2021-07-23 18:22:42 【问题描述】:

我正在尝试使用 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 得到更新,然后在视图出现之前返回。感谢您的帮助

【问题讨论】:

"...在视图出现之前得到更新然后返回"你问的是不可能的。如果网络速度慢或未连接怎么办?视图应该挂起/阻塞?您可以做的最好的事情是更新一个 \@State 或 \@ObservedObject (自动)触发视图以使用新结果更新自身。您没有显示您的视图代码... 感谢您的回复,我已经编辑了我的代码以包含已发布/观察到的对象,但它仍然无法正常工作并且只返回 0。我已经展示了我想要的视图代码根据返回 0、1 或 2 查看更改。谢谢 像 getDocument() 这样的 Firebase 调用是异步的。您不能在函数末尾返回值。你应该使用if userData.isFriend,而不是if (userData.checkFriendRequest) 那么我应该在哪里调用 checkFriendRequest() 函数来更新 isFriend 变量? @HariKrishnaSenthilkumar 传统上类似于onAppear 【参考方案1】:

SwiftUI 视图会根据其视图的状态自动触发更新。由于 Firebase 调用是异步的,因此您不能期望对 fetch 函数的同步调用会返回正确的结果。

相反,您需要更新一个已发布的值(在主线程上),然后该值将向该值的所有订阅者广播一条更新消息。 SwiftUI 自动处理 StateObject 或 ObservedObject 的订阅机制,并触发对视图主体的新请求。

要让您的代码与 SwiftUI 一起使用,请按如下方式调整 checkFriendRequest:

改变这个:

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

To:(因为它会触发 UI 事件,并且所有 UI 都必须在主线程上运行)

          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)
      

【讨论】:

检查您的 if 子句中是否存在不匹配的括号。 删除了不必要的括号。

以上是关于异步firebase调用后SwiftUI不更新变量的主要内容,如果未能解决你的问题,请参考以下文章

删除 SwiftUI 后 Firebase Firestore 不更新 UI

在 Firebase 完成块中设置 @Published var 不更新 SwiftUI 视图

如何使用 SwiftUI 更新 Firebase 中的集合?

在 SwiftUI 中仅更新来自 Firebase 的新数据

观察调用不更新字典值。异步的问题

SwiftUI - 在 Firebase 上更新密码