jon*_*ham 7 google-cloud-firestore swiftui combine swiftui-list
我正在开发一个 SwiftUI 项目,该项目使用合并从 Firebase Firestore 中提取数据。每个用户都可以在应用程序中创建“优惠”。为了在他们的帐户页面上列出他们的报价,我使用 onAppear 将 currentUserUid 传递到我的视图模型,以便我可以使用 currentUserUid 过滤数据库结果。OfferHistoryView 如下。当视图第一次出现时,这非常有效。我的问题是当我从 OfferDetailView 返回时,我收到以下消息。
ForEach<Array, String, NavigationLink<OfferRowView, ModifiedContent<OfferDetailView, _EnvironmentKeyWritingModifier<Optional>>>>:ID 在集合中出现多次,这将给出未定义的结果!
虽然这不会使应用程序崩溃,但并不理想。我尝试过,每次加载视图时以及每次调用组合时都从集合中删除所有项目,但这并不能解决问题。我还添加了打印语句来尝试捕获重复项,但我从未看到重复项。您可以在下面看到我的打印语句和其余相应文件。任何帮助,将不胜感激。
OfferViewHistory - 消息的来源。
struct OfferHistoryView: View {
let db = Firestore.firestore()
@EnvironmentObject var authSession: AuthSession
@EnvironmentObject var offerHistoryViewModel: OfferHistoryViewModel
var body: some View {
return VStack {
List {
ForEach(self.offerHistoryViewModel.offerRowViewModels, id: \.id) { offerRowViewModel in
NavigationLink(destination: OfferDetailView(offerDetailViewModel: OfferDetailViewModel(offer: offerRowViewModel.offer, listing: offerRowViewModel.listing ?? testListing1))
.environmentObject(authSession)
) {
OfferRowView(offerRowViewModel: offerRowViewModel)
}
} // ForEach
} // List
.navigationBarTitle("Offer History")
} // VStack
.onAppear(perform: {
for offerRowViewModel in self.offerHistoryViewModel.offerRowViewModels {
print("Before startCombine: \(offerRowViewModel.id)")
}
self.offerHistoryViewModel.startCombine(currentUserUid: self.authSession.currentUserUid)
for offerRowViewModel in self.offerHistoryViewModel.offerRowViewModels {
print("After startCombine: \(offerRowViewModel.id)")
}
})
} // View
}
Run Code Online (Sandbox Code Playgroud)
OfferHistoryViewModel - 调用组合的地方。
class OfferHistoryViewModel: ObservableObject {
var offerRepository: OfferRepository
// Published Properties
@Published var offerRowViewModels = [OfferRowViewModel]()
// Combine Cancellable
private var cancellables = Set<AnyCancellable>()
// Intitalizer
init(offerRepository: OfferRepository) {
self.offerRepository = offerRepository
}
// Starting Combine - Filter results for offers created by the current user only.
func startCombine(currentUserUid: String) {
for offerRowViewModel in self.offerRowViewModels {
print("Before startCombine func: \(offerRowViewModel.id)")
}
offerRepository
.$offers
.receive(on: RunLoop.main)
.map { offers in
offers
.filter { offer in
(currentUserUid != "" ? offer.userId == currentUserUid : false)
}
.map { offer in
OfferRowViewModel(offer: offer, listingRepository: ListingRepository())
}
}
.assign(to: \.offerRowViewModels, on: self)
.store(in: &cancellables)
for offerRowViewModel in self.offerRowViewModels {
print("After startCombine func: \(offerRowViewModel.id)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
报价行视图
struct OfferRowView: View {
@ObservedObject var offerRowViewModel: OfferRowViewModel
var body: some View {
// Convenience variable for accessing the offer & listing.
let offer = offerRowViewModel.offer
let listing = offerRowViewModel.listing
return VStack {
Text(offer.id ?? "ID")
Text(listing?.id ?? "ID")
} // VStack
} // View
}
Run Code Online (Sandbox Code Playgroud)
报价行视图模型
class OfferRowViewModel: ObservableObject, Identifiable {
// Properties
var id: String = ""
var listingRepository: ListingRepository
// Published Properties
@Published var offer: Offer
@Published var listing: Listing?
// Combine Cancellable
private var cancellables = Set<AnyCancellable>()
// Initializer
init(offer: Offer, listingRepository: ListingRepository) {
self.offer = offer
self.listingRepository = listingRepository
self.startCombine()
}
// Starting Combine
func startCombine() {
// Get Offer
$offer
.receive(on: RunLoop.main)
.compactMap { offer in
offer.id
}
.assign(to: \.id, on: self)
.store(in: &cancellables)
// Get Connected Listing
listingRepository
.$listings
.receive(on: RunLoop.main)
.map { listings in
listings
.first(where: { $0.id == self.offer.listingId})
}
.assign(to: \.listing, on: self)
.store(in: &cancellables)
}
}
Run Code Online (Sandbox Code Playgroud)
问题是 OfferRowViewModel 声明一个id属性,最初将其设置为"",然后使用合并发布者将其更新为“真实”id 作为基础属性。
当您遵循 时Identifiable,使用计算属性通常会非常方便。这将始终给出正确的 id 并且永远不会不同步:
var id: String { offer.id }
Run Code Online (Sandbox Code Playgroud)
示例中还有另一个问题值得解决。OfferRowViewModel 代表了一个监视列表并对其进行过滤的发布者。但正如所写,这意味着每个 OfferRowViewModel 都会迭代每个列表,或者换句话说,每次报价或列表更改时都会执行 O(n * m) 操作。这在实践中可能没问题,但如果有大量优惠或大量列表,或者它们经常更改,则可能会产生性能问题。联合起来确实很强大,但它的缺点之一是它会让我们更难看到这样的效率问题。
通过用简单的模型类型替换 OfferRowViewModel 可以使此代码变得更简单:
struct OfferAndListing: Identifiable {
var offer: Offer
var listing: Listing
var id: String { offer.id }
}
Run Code Online (Sandbox Code Playgroud)
那么您提供报价的发布商可能看起来更像这样:
Publishers.CombineLatest(offerRepository.$offers, listingRepository.$listings)
.receive(on: RunLoop.main)
.map { (offers, listings) in
offers
.filter { $0.userId == currentUserUid }
.map { OfferAndListing(
offer: $0,
listing: listings.first(where: { })
)}
}
.sink { [weak self] in self?.offersAndListings = $0 }
.store(in: &cancellables)
Run Code Online (Sandbox Code Playgroud)
这还没有提高效率,但它提供了一个地方来查看工作正在哪里完成和优化,使用更容易推理的单个发布者链,并简化了模型类型,因此它不需要了解任何信息合并、数据存储库等。
| 归档时间: |
|
| 查看次数: |
6674 次 |
| 最近记录: |