【问题标题】:Swiftui + Firestore + CombineLatest keep publishing dataSwiftui + Firestore + CombineLatest 持续发布数据
【发布时间】:2021-03-22 21:51:31
【问题描述】:

我正在尝试合并来自两个不同集合的查询的两个已发布对象,以使用 combineLatest 创建一个已发布数组。

import Combine
import Firebase

class FirestoreViewModel: ObservableObject {
    let userId: String
    
    @Published var aList: [DocumentA]? = nil
    @Published var bList: [DocumentB]? = nil
    
    init(userId: String, listenForChanges: Bool = false)    {
        
        // Get the user
        if (listenForChanges)   {
            self.loadA()
            self.loadB()
        }
    }
    
    var cList: AnyPublisher<[DocumentC]?, Never> {
        return Publishers.CombineLatest(
            $aList.map { (aList) -> [DocumentC]?  in
                guard let aList = aList else { return nil }
                return aList.map {a in
                    DocumentC(from: a)
                }
            },
            $bList.map { (bList) -> [DocumentC]?  in
                guard let bList = bList else { return nil }
                return bList.map { n in
                    DocumentC(from: b)
                }
            })
            .map { (aList, bList) -> [DocumentC]?  in
                
                if (aList == nil && bList == nil) { return nil }
                
                var cList: [DocumentC] = []
                
                if let aList = aList {
                    cList.append(contentsOf: aList)
                }
                
                if let bList = bList {
                    cList.append(contentsOf: bList)
                }
                return cList
            }
            .eraseToAnyPublisher()
    }
    
    private func loadA() {
        
        // Start listening
        Firestore.firestore().collection("colA").addSnapshotListener { (snapshot, error) in
            if let error = error {
                print("DEBUG: Unable to get user data: \(error.localizedDescription)")
                return
            }
            
            // Invalid data return
            guard let snapshot = snapshot else {
                print("DEBUG: null data returned")
                self.aList = nil
                return
            }
            
            // Update the info
            var aList: [DocumentA] = []
            snapshot.documents.forEach { document in
                let aData = document.data()
                guard !aData.isEmpty else {
                    return
                }
                aList.append(DocumentA(from: aData)
                
            }
            self.aList = aList
        }
    }
    
    private func loadB() {
        
        // Start listening
        Firestore.firestore().collection("colB").addSnapshotListener { (snapshot, error) in
            if let error = error {
                print("DEBUG: Unable to get user data: \(error.localizedDescription)")
                return
            }
            
            // Invalid data return
            guard let snapshot = snapshot else {
                print("DEBUG: null data returned")
                self.bList = nil
                return
            }
            
            // Update the info
            var bList: [DocumentB] = []
            snapshot.documents.forEach { document in
                let bData = document.data()
                guard !bData.isEmpty else {
                    return
                }
                bList.append(DocumentB(from: bData))
                
            }
            self.userUnits = userUnits
        }
    }
}

当我调试时,我可以看到 aListbList 发布了一次,但是,cList 一直在发布,最终吃掉了所有的内存......

cList 是通过视图中的 onReceive 语句使用的,该语句输出每个项目。

有人能告诉我为什么combineLatest 继续发布cList,尽管没有新的aListbList 发布吗?

非常感谢。

【问题讨论】:

  • 你能显示消耗cList的视图吗?我的猜测是,每当cList 发出一个值时,视图就会更新,这会导致onReceive 再次获得cList。它是一个计算属性,因此无论何时访问它都会生成一个新的发布者

标签: swift google-cloud-firestore combine combinelatest


【解决方案1】:

很有可能,您会使用刚刚从cList 收到的值更新视图,这会导致重新计算body,从而导致另一个onReceive(vm.cList) {...},从而导致new 由计算属性 cList 返回的发布者,它再次发出值并重复循环。

这是我的意思的简化示例:

class ViewModel: ObservableObject {
   @Published var aList: [Int] = [1,2]

   var cList: AnyPublisher<Int, Never> {
      $aList.map { $0 + $0 }.eraseToAnyPublisher()
   }
}

struct ContentView: View {
   @StateObject var vm = ViewModel()
   @State var list: [Int] = []

   var body: some View {
      VStack {
         ForEach(list, id: \.self) { v in
            Text("\(v)")
         }
      }
      .onReceive(vm.cList) { self.list = $0 }
   }
}

为避免这种情况,cList 不应是计算属性。它可能只是一个惰性分配的常量:

lazy var cList: AnyPublisher<Int, Never> = 
   $alist.map { $0 + $0 }
         .eraseToAnyPublisher()

【讨论】:

    猜你喜欢
    • 2019-11-16
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多