Firebase 和 swiftUI,监听实时更新奇怪行为怪异 [英] Firebase and swiftUI, listening for real time update strange behave weird

查看:23
本文介绍了Firebase 和 swiftUI,监听实时更新奇怪行为怪异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的项目中,我使用 firebase Cloud Firestore 来实现类似Facebook"的系统.请求和确认好友.

On my project I'm using firebase Cloud Firestore in order to implement a similar system like "Facebook" to request and confirm friends.

在我的应用中,有些用户是管理员,有些只是普通用户.

On my app some user are Administrator and some just normal user.

只有普通用户才能搜索和添加管理员,以便加入管理员安排的某些特定活动.

Only normal user can search and add Administrator in order to join some specific event scheduled from the administrator.

以下是我在 Cloud Firestore 中的数据情况:

here below how is my data in Cloud Firestore :

每个用户都有待处理和确认好友的收藏.

every user have collection with pending and confirm friends.

我正在使用视图列出每个用户的所有待处理和确认好友

I' m using a view to list all the pending and confirm friends for each user

使用 firebase 的 .addSnapshotListener {} 每次确认和挂起的朋友发生更改时,我都会检查以下函数,并在 2 个 @Published 数组中发布更改,pendingFriendsADMIN = [UserMODEl] 和 ConfirmedFriendADMIN =[用户模型]

using the .addSnapshotListener {} of firebase I'm checking with the following function every time there is a change on the confirm and pending friend and I publish the change in 2 @Published arrays, pendingFriendsADMIN = [UserMOdel] and confirmedFriendADMIN = [UserMOdel]

func userUpdateFriendUser(userInfo: UserModel){

           db.collection("userUser").document(userInfo.email).collection("pendingFriends")
             
            
            .addSnapshotListener(includeMetadataChanges: false) { documentSnapshot, error in
                self.pendingFriendsUSER = []
                guard let documents = documentSnapshot?.documents else {
                    print("Error fetching documents: \(String(describing: error?.localizedDescription))")
                    return
                }
                
                 var i = 0
                for doc in documents {
                    
                    debugPrint("inizio il ciclo pending user\(i)")
                    let idUser = doc["userID"] as? String ?? "no ID"
                    
                    self.downloadImageForAdmin(userID: idUser) { (urlImage) in
                        
                        let userPending = UserModel(name: "", surname: "" ,username: "", email: "", userID: "", adminLevel: "", immagine: urlImage!, position: "centro", position2: "sx", vote: 0)
                        userPending.name = doc["name"] as? String ?? "NA name"
                        userPending.surname = doc["surname"] as? String ?? "NA surname"
                        userPending.adminLevel = doc["adminLevel"] as? String ?? "NA admin"
                        userPending.email = doc["email"] as? String ?? "NA email"
                        userPending.username = doc["username"] as? String ?? "NA username"
                        userPending.userID = doc["userID"] as? String ?? "NA id"
                        userPending.position = doc["position"] as? String ?? "na position"
                        userPending.position2 = doc["position2"] as? String ?? "na position"
                        userPending.vote = doc["vote"] as? Int ?? 0
                       
                         self.pendingFriendsUSER.append(userPending)
                        i = i+1
                                               debugPrint("finito ciclo pending")
                    }
                    
                }
           }
        
        db.collection("userUser").document(userInfo.email).collection("confirmedFriend")
                     .addSnapshotListener (includeMetadataChanges: false){ documentSnapshot, error in
                               self.confirmedFriendUSER = []
                                guard let documents = documentSnapshot?.documents else {
                                   print("Error fetching documents: \(String(describing: error?.localizedDescription))")
                                    return
                                }
                      
                       
                       for doc in documents {
                         
                           debugPrint("inizio il ciclo confirm user \(i)")
                           let idUser = doc["userID"] as? String ?? "no ID"
                           self.downloadImageForAdmin(userID: idUser) { (urlImage) in
                               let userConfirm = UserModel(name: "", surname: "" ,username: "", email: "", userID: "", adminLevel: "", immagine: urlImage!, position: "centro", position2: "sx", vote: 0)
                               userConfirm.name = doc["name"] as? String ?? "NA name"
                               userConfirm.surname = doc["surname"] as? String ?? "NA surname"
                               userConfirm.adminLevel = doc["adminLevel"] as? String ?? "NA admin"
                               userConfirm.email = doc["email"] as? String ?? "NA email"
                               userConfirm.username = doc["username"] as? String ?? "NA username"
                               userConfirm.userID = doc["userID"] as? String ?? "NA id"
                               userConfirm.position = doc["position"] as? String ?? "na position"
                               userConfirm.position2 = doc["position2"] as? String ?? "na position"
                               userConfirm.vote = doc["vote"] as? Int ?? 0
                               self.confirmedFriendUSER.append(userConfirm)
                               
                           }
                       }
               }
    }

类似的方法也用于在 userFriendList 上列出更改.

the similar method is used also for listed the change on the userFriendList.

用户,可以通过电子邮件搜索管理员,并向他发送好友请求(见下文)

a user, can search an administrator via the email, and send to him a friend request(see below)

用户使用以下函数发送好友请求:简单地说,我在用户的待处理朋友上写下管理员电子邮件,在管理员待处理朋友中写下用户电子邮件

the user sent the friend request with the following function: simply, I write on the pending friend the of the user the admin email and in the admin pending friend the user email

 func sendFriendRequest(userInfo: UserModel, userToRequest: UserModel, closure: @escaping warning){
        // check if reuqest already sent
        self.db.collection("userAdmin").document(userToRequest.email).collection("confirmedFriend").whereField("email", isEqualTo: userInfo.email).getDocuments() { (queryResult, err) in
            if let err = err {
                debugPrint("unable to get data , friend alrady request\(err)")
            } else {
                if queryResult!.documents.count > 0 {
                    debugPrint("siete gia amici") // mettere warning
                    let warning = true
                    closure(warning)
                    return
                } else {
                    // if request never sent, metto user nella lista dell admin pending
                    self.db.collection("userAdmin").document(userToRequest.email).collection("pendingFriends").document(userInfo.email).setData([
                    
                        "username": userInfo.username,
                        "email" : userInfo.email,
                        "userID" : userInfo.userID,
                        "adminLevel": userInfo.adminLevel,
                        "name":userInfo.name,
                        "surname":userInfo.surname,
                        "position": userInfo.position,
                        "position2": userInfo.position2,
                        "vote": userInfo.vote
                    
                        
                    
                    ], merge: false) { (err) in
                        self.db.collection("userUser").document(userInfo.email).collection("pendingFriends").document(userToRequest.email).setData([
                        
                        "username": userToRequest.username,
                        "email" : userToRequest.email,
                        "userID" : userToRequest.userID,
                        "adminLevel": userToRequest.adminLevel,
                        "name":userToRequest.name,
                        "surname":userToRequest.surname,
                        "position": userToRequest.position,
                        "position2": userToRequest.position2,
                        "vote": userToRequest.vote
                        ], merge: false)
                    }
                    // metto sulla mia pending request
                    
                }
            }
        }
        
        
        
        
    }

问题来了……有时,并非总是在我向朋友管理员发送请求后,.addSnapshotListener 会复制更改,因为您可以从第三张图片中看到有 2 次是相同的待处理朋友.

Here the problem... some time , not always once I sent the request to a friend admin the .addSnapshotListener duplicate the change , as you can se from the third picture there is 2 time the same pending friend.

如果我退出视图并返回,则未决朋友是正确的.

If I exit from the view and I go back the pending friend are correct.

这里是我的 AdminFriendRequest 的代码:查看

here the code of my AdminFriendRequest : View

import SwiftUI
import URLImage
struct AdminFriendRequest: View {
    @Binding var dismissView : Bool
    @ObservedObject var dm : DataManager
    @Binding var meInfo: UserModel?
    
    
    var body: some View {
        
        VStack{
            fakebar
            Spacer()
            List{
                
                
                HStack {
                    Image(systemName: "person.2")
                    Text("Pending friends request:")
                }.font(.headline)
                    .foregroundColor(.blue)
                
                
                ForEach(dm.pendingFriendsADMIN) { friend in
                    HStack{
                        if friend.immagine == nil{
                            Image(systemName: "person")
                                .resizable()
                                .frame(width: 30, height: 30, alignment: .center)
                                .clipShape(Circle())
                        } else {
                            URLImage(friend.immagine!) { proxy in
                                proxy.image
                                    .resizable()
                                    .frame(width: 30, height: 30, alignment: .center)
                                    .clipShape(Circle())
                            }
                        }
                        Text(friend.username)
                        Spacer()

                        Image(systemName: "checkmark.circle")

                    }
                    .onTapGesture {
                        if self.meInfo != nil {
                            self.dm.tapToConfirmFriend(me: self.meInfo!, friendToConfirm: friend) { (isFriendConfirm) in
                                debugPrint("is friend confirm \(isFriendConfirm)")
                            }
                        }
                    }
                }
                if dm.pendingFriendsADMIN.isEmpty {
                    Text("No friend request yet").font(.caption)
                }
                HStack {
                    Image(systemName: "person.3")
                    Text("Friends:")
                }.font(.headline)
                    .foregroundColor(.blue)
                
                ForEach(dm.confirmedFriendADMIN) { friend in
                    HStack{
                        if friend.immagine == nil{
                            Image(systemName: "person")
                                .resizable()
                                .frame(width: 30, height: 30, alignment: .center)
                                .clipShape(Circle())
                        } else {
                            URLImage(friend.immagine!) { proxy in
                                proxy.image
                                    .resizable()
                                    .frame(width: 30, height: 30, alignment: .center)
                                    .clipShape(Circle())
                            }
                        }
                        Text(friend.username)
                        Spacer()
                        
                        Image(systemName: "checkmark.circle").foregroundColor(.green)
                        
                        Button(action: {
                            self.dm.removeFriend(me: self.meInfo!, friendConfirm: friend)
                        }, label: {
                            Text("remove friend")
                        })
                        
                    }.padding(.all)
                    
                    
                }
            }.padding(.trailing)
        }
        .onAppear {
            self.dm.newListUpdateForAdmin(userInfo: self.meInfo!)

        }
    }
    var fakebar: some View {
        ZStack {
            HStack {
                Spacer()
                
                Image(systemName: "chevron.compact.down")
                    .font(.system(size: 60))
                    .aspectRatio(contentMode: .fit)
                    .foregroundColor(.white)
                
                Spacer()
            }
            
            HStack {
                Spacer()
                Button(action: {
                    self.dismissView.toggle()
                }) {
                    Text("Close")
                        .fontWeight(.bold)
                        .foregroundColor(.white)
                        .padding(.horizontal)
                }
            }
        }
        .frame(height: 44)
        .background(Color.green.padding(.top, -44))
    }
}

我使用 onAppear 通过 .addSnapshotListener 触发列表更新

I use the onAppear to trigger the list update with the .addSnapshotListener

.onAppear {
            self.dm.newListUpdateForAdmin(userInfo: self.meInfo!)

        }

我不知道为什么...我使用 .addSnapshotListener 的方式是否正确?或者任何其他想法如何处理好友请求.很高兴改变我处理好友请求的方式.

I can't find out why... is it correct the way how i'm using the .addSnapshotListener ? Or any other idea how to handle the friend request. happy to change my way how to deal with the friend request.

谢谢

推荐答案

一些可能有帮助的建议:

Some suggestions that maybe helps:

1.实现监听器状态控制:这些有助于控制用户何时添加、修改或删除记录,有助于不重新加载所有数据并允许不重复事件,例如在下面的代码中我获取所有用户(事件文档更改.add) 如果添加了新用户,则不会重新加载所有用户数组.

1. Implement listener state control: These help to control when a user add, modify or remove a record, helps not to reload all data and allow not to duplicate events, for example in the code below I get all users (event documentChange.add) if a new user is added you don't reload all users array.

API:

 function getUsersPending(userInfo: UserModel, onSuccess: @escaping([UserModel]) -> Void, onError: @escaping(_ errorMessage: String) -> Void, newPendingUser: @escaping(UserModel) -> Void ) {

     db.collection("userUser").document(userInfo.email).collection("pendingFriends").addSnapshotListener(includeMetadataChanges: false) { documentSnapshot, error in
            self.pendingFriendsUSER = []
            
      guard let snapshot = documentSnapshot else { return }
      var userPendingArray = [UserModel]()
      snapshot.documentChanges.forEach { (documentChange) in
        switch documentChange.type {
          case: .added :
             let dict = documentChange.document.data()
             //Get User from firebase doc pendingUser = ....
             newPendingUser(pendingUser) //escape New User
             userPendingArray.appen(pendingUser)
             print("Pending User Added")
          case .modified :
             //implements action (new escaping)
             print("Pending User Modified") 
          case .removed :
             print("User pending removed")
        }
      }
      onSuccess(userPendingArray)
        
    }

用户等待 ViewModel 示例

Users pending ViewModel sample

class UserPendingViewModel() : ObservableObject {
  
  @Published var usersPending: [UserModel] = []
  @Published var isLoading = false
  var errorString : String = ""

  func loadUsersPending() {
     self.usersPending = []
     self.isLoading = true
     
    dm. getUsersPending(userInfo: userModel, onSuccess: { (users) in
        if (self.usersPending.isEmpty) { self.usersPending = users }
        self.isLoading = false
    }, onError: { (errorMessage) in
        print("Error Message \(errorMessage)")
    }, newPendingUser: { (user) in
        if (!self.usersPending.isEmpty) { self.usersPending.append(user) }
    }) 
    
  }
}

查看

struct UserPendingView: View {

   @ObservedObject var model = UserPendingViewModel()
   
   var body: some View {

         ScrollView {
            
                if !model.usersPending.isEmpty {
                    ForEach(model.usersPending, id: \.messageId) { user in
                        //Show your data
                        }
                    }
                }.onAppear{ self.model.loadUsersPending() }
           
         }

2.激活/停用监听器.如果您的应用未显示待处理视图,则用户无需保持活动侦听器.激活监听器 onAppear 并停用 onDisappear.

2. Activate / Deactivate listeners. If your app is not showing the pending view users no need to keep alive listeners. Activate the listener onAppear and Deactivate onDisappear.

在之前的示例中,Api 上的新转义、var 侦听器声明和结果

On previous sample New escaping, var listener declaration and result on Api

function getUsersPending(userInfo: UserModel, onSuccess: @escaping([UserModel]) -> Void, onError: @escaping(_ errorMessage: String) -> Void, newPendingUser: @escaping(UserModel) -> Void, listener: @escaping(_ listenerHandle: ListenerRegistration) -> Void ) { ) {

     let listenerRegistration = db.collection("userUser").document(userInfo.email).collection("pendingFriends").addSnapshotListener(includeMetadataChanges: false) { documentSnapshot, error in
            self.pendingFriendsUSER = []
            
      guard let snapshot = documentSnapshot else { return }
      var userPendingArray = [UserModel]()
      snapshot.documentChanges.forEach { (documentChange) in
        switch documentChange.type {
          case: .added :
             let dict = documentChange.document.data()
             //Get User from firebase doc pendingUser = ....
             newPendingUser(pendingUser) //escape New User
             userPendingArray.appen(pendingUser)
             print("Pending User Added")
          case .modified :
             //implements action (new escaping)
             print("Pending User Modified") 
          case .removed :
             print("User pending removed")
        }
      }
      onSuccess(userPendingArray)
    } 
  listener(listenerRegistration)  //escaping listener
}

用户待定 ViewModel 示例、声明侦听器和添加函数侦听器结果(注意:导入 Firebase)

Users pending ViewModel sample, declaration listener and add function listener result (Note: import Firebase)

class UserPendingViewModel() : ObservableObject {
  
  @Published var usersPending: [UserModel] = []
  @Published var isLoading = false
  var errorString : String = ""
  var listener : ListenerRegistration!

  func loadUsersPending() {
     self.usersPending = []
     self.isLoading = true
     
    dm. getUsersPending(userInfo: userModel, onSuccess: { (users) in
        if (self.usersPending.isEmpty) { self.usersPending = users }
        self.isLoading = false
    }, onError: { (errorMessage) in
        print("Error Message \(errorMessage)")
    }, newPendingUser: { (user) in
        if (!self.usersPending.isEmpty) { self.usersPending.append(user) }
    }) { (listener) in
        self.listener = listener
    } 
    
  }
}

View 实现 onDisappear 断开监听器

View implement onDisappear to disconnect listener

struct UserPendingView: View {

   @ObservedObject var model = UserPendingViewModel()
   
   var body: some View {

         ScrollView {
            
                if !model.usersPending.isEmpty {
                    ForEach(model.usersPending, id: \.messageId) { user in
                        //Show your data
                        }
                    }
                }.onAppear{ self.model.loadUsersPending() }
                 .onDisappear {
                     if self.model.listener != nil {
                        self.model.listener.remove()
                     }
                }
           }

这篇关于Firebase 和 swiftUI,监听实时更新奇怪行为怪异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆