Как справиться с удалением прослушивателей Firestore в производственных приложениях Swift?

# #swift #firebase #google-cloud-firestore

Вопрос:

Мое приложение настроено с несколькими классами ___API, которые содержат код firebase для чтения и записи в мою базу данных firestore. Недавно я обнаружил полезность прослушивателей(.addSnapshotListener) для обновлений в реальном времени, где ранее мой проект состоял из одноразовых выборок (.getDocuments). До сих пор я нахожу их чрезвычайно полезными, однако управлять ими сложнее, потому что я должен удалять слушателя при переходе между представлениями.

В настоящее время я передаю объекты «ListenerRegistration» через мои завершения, мне нравится этот подход, однако я не могу ссылаться на слушателя, когда завершение вложено внутри слушателя. Мое текущее решение состоит в том, чтобы создать переменную для QuerySnapshot и использовать .asyncAfter, но, конечно, есть более элегантный способ получить доступ к слушателям в отдельных классах для легкого удаления без .asyncAfter:

 
     static func fetchHosts(venID: String, completion: @escaping([UserModel], ListenerRegistration) -> Void){
        var hostObj = [UserModel]()
        var snapshot : QuerySnapshot? = nil
        
        let listener = PRIV_VENUES_COLLECTION.document(venID).collection("Hosts").addSnapshotListener { _snapshot, e in
            snapshot = _snapshot

     ///USUALLY THE CODE WITHIN THE ASYNC IS HERE, 
    ///BUT completion(hostObj, listener) THROWS ERROR: (declaration before initialization)

        }
        
        DispatchQueue.main.asyncAfter(deadline: .now()   0.5) {
            if let snap = snapshot{
                snap.documents.forEach { doc in
                    let dic = doc.data()
                    
                    let dateTimestamp = dic[DatabaseUserField.dateCreated] as? Timestamp
                    guard let date = dateTimestamp?.dateValue() else {print("NO Date")
                        return}
                    
                    
                    
                    let userID = doc.documentID
                    
                    ProfileAPI.fetchUserInfonDate(userID: userID, dateCreated: date) { aHost in
                        hostObj.append(aHost)
                        
                        if hostObj.count == snap.documents.count{
                            
                            let sort =  hostObj.sorted(by: { $0.dateString! > $1.dateString! })
                            completion(sort, listener)
                        }
                    }
                }
            }else{
                print("snapshot is nil")
            }
        }
    }

 

Вот как я на самом деле использую «ListenerRegistration» в своем сетевом классе. В ViewController viewDidAppear я вызываю «выборку» и «отклонение» в viewDidDisappear

 class HostNetworking{
    
    var hostVC: HostViewController!
    var venueData : VenueModel!
    var hostListener : ListenerRegistration?
  

    init(hostVC: HostViewController, venueData: VenueModel){
        self.hostVC = hostVC
        self.venueData = venueData
        
    }

func fetchHosts(){
        guard let venID = venueData.venID else {print("NO VENID")
            return}
        
        HostAPI.fetchHosts(venID: venID) { hostData, listener in
            self.hostVC.hostObject = []
            self.hostVC.hostObject = hostData
            self.hostListener = listener
        }
    }
    
 func dismissHostListener(){
        guard let listener = self.hostListener else {print("NO HOST LISTNR")
            return
        }
        
        print("Host Listener removed")
        listener.remove()
    }
 

Ответ №1:

Решение, которое я придумал, состояло в том, чтобы создать статические переменные в верхней части моего класса API, чтобы я мог просто задавать их слушателям при вызове выборки, так же как и функции, переменные легко доступны. Я могу эффективно удалять слушателей из любого места. Я все еще открыт для других решений, но это работает гладко.

 class HostAPI{
    
    static var guestListener : ListenerRegistration!
   
    
    //MARK: - Fetch a venues guests
    static func fetchGuests(venID: String, completion: @escaping([UserModel]) -> Void){
        var guestObj = [UserModel]()
        
        
       guestListener = PRIV_VENUES_COLLECTION.document(venID).collection("Guests").addSnapshotListener { snapshot, e in
            
            
            if snapshot != nil{
                snapshot!.documents.forEach({ doc in
                  let userID =  doc.documentID
                    let dic = doc.data()
                    
                    let timeStamp = dic[DatabaseUserField.dateCreated] as? Timestamp
                    guard let date = timeStamp?.dateValue() else {print("NO Date")
                        return}
                   
                    ProfileAPI.fetchUserInfonDate(userID: userID, dateCreated: date) { user in
                        guestObj.append(user)
                        
                        if guestObj.count == snapshot!.documents.count{
                            
                            let sort =  guestObj.sorted(by: { $0.dateString! > $1.dateString! })
                           
                                completion(sort)
                           
                        }
                    }
                })
            }else{
                print("nil - guests")
                }
            }
        }
    }

 

Это код в сетевом классе:

 class HostNetworking{
    
    var hostVC: HostViewController!
    var venueData : VenueModel!
    var guestListener : ListenerRegistration!

    init(hostVC: HostViewController, venueData: VenueModel){
        self.hostVC = hostVC
        self.venueData = venueData
        
    }
    
    //MARK: - Guests
    
    func fetchGuests(){
        guard let venID = venueData.venID else {print("NO VENID")
            return}
        
        HostAPI.fetchGuests(venID: venID) {  guestData in
            self.hostVC.guestsObject = []
            self.hostVC.guestsObject = guestData
            self.guestListener = HostAPI.guestListener 
            
           
        }
    }
    
    func dismissGuestListener(){
        guard let listener = self.guestListener else {print("NO GUEST LISTNR")
            return
        }
        print("Guest Listener removed")
        listener.remove()
    }
}