Swiftui + Firestore + CombineLatest 持续发布数据

Swiftui + Firestore + CombineLatest keep publishing data

我正在尝试使用 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 收到的值更新视图,这会导致重新计算 body,从而导致另一个 onReceive(vm.cList) {...},这导致 new 发布者由计算的 属性 cList 返回,它再次发出值并重复循环.

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

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

   var cList: AnyPublisher<Int, Never> {
      $aList.map { [=10=] + [=10=] }.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 = [=10=] }
   }
}

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

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