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
}
}
}
当我调试时,我可以看到 aList
和 bList
被发布了一次,但是 cList
一直被发布,最终吃掉了所有内存...
cList
通过视图中的 onReceive 语句使用,输出每个项目。
谁能告诉我为什么 combineLatest
继续发布 cList
,尽管没有新的 aList
或 bList
发布?
非常感谢。
您很可能会遇到这样一种情况,即您使用刚刚从 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()
我正在尝试使用 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
}
}
}
当我调试时,我可以看到 aList
和 bList
被发布了一次,但是 cList
一直被发布,最终吃掉了所有内存...
cList
通过视图中的 onReceive 语句使用,输出每个项目。
谁能告诉我为什么 combineLatest
继续发布 cList
,尽管没有新的 aList
或 bList
发布?
非常感谢。
您很可能会遇到这样一种情况,即您使用刚刚从 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()