为什么 EnvironmentObject 在 SwiftUI 项目中不起作用?



我正在使用MVVM进行SwiftUI项目。我有以下文件,适用于具有商品信息的商城。

列表存储库.swift - 连接到 Firebase Firestore 上市.swift - 上市模型文件 市场视图模型 - 市场视图模型 市场视图 - 市场列表视图

最初,我正在使我的存储库文件成为有效的环境对象。在研究过程中,我意识到将ViewModel设置为环境对象更有意义。但是,我在制作环境对象时遇到问题。当我尝试访问 marketplaceViewModel 时,Xcode 在我的 MarketplaceView.swift 文件中给了我以下错误,我不明白为什么?

SwiftUI:0:致命错误:没有可观察对象类型 找到市场视图模型。A View.environmentObject(_:) for MarketplaceViewModel可能缺少作为此视图的祖先。

以下是简化形式的文件。

应用文件

@main
struct Global_Seafood_ExchangeApp: App {

@StateObject private var authSession = AuthSession() @StateObject private var marketplaceViewModel = MarketplaceViewModel(listingRepository: ListingRepository())

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(marketplaceViewModel)

.environmentObject(authSession) } } }

列表存储库.swift

class ListingRepository: ObservableObject {
let db = Firestore.firestore()

@Published var listings = [Listing]()

init() {
startSnapshotListener()
}

func startSnapshotListener() {
db.collection(FirestoreCollection.listings).addSnapshotListener { (querySnapshot, error) in
if let error = error {
print("Error getting documents: (error)")
} else {
guard let documents = querySnapshot?.documents else {
print("No Listings.")
return
}

self.listings = documents.compactMap { listing in
do {
return try listing.data(as: Listing.self)
} catch {
print(error)
}
return nil
}
}
}
}
}

上市.swift

struct Listing: Codable, Identifiable {
@DocumentID var id: String?
var title: String?
}

市场模型视图.swift

class MarketplaceViewModel: ObservableObject {
var listingRepository: ListingRepository
@Published var listingRowViewModels = [ListingRowViewModel]()

private var cancellables = Set<AnyCancellable>()

init(listingRepository: ListingRepository) {
self.listingRepository = listingRepository
self.startCombine()
}

func startCombine() {
listingRepository
.$listings
.receive(on: RunLoop.main)
.map { listings in
listings.map { listing in
ListingRowViewModel(listing: listing)
}
}
.assign(to: .listingRowViewModels, on: self)
.store(in: &cancellables)
}
}

市场视图.swift

struct MarketplaceView: View {
@EnvironmentObject var marketplaceViewModel: MarketplaceViewModel

var body: some View {
// ERROR IS HERE
Text(self.marketplaceViewModel.listingRowViewModels[1].listing.title)
}
}

列表行视图模型.swift

class ListingRowViewModel: ObservableObject {
var id: String = ""
@Published var listing: Listing

private var cancellables = Set<AnyCancellable>()

init(listing: Listing) {
self.listing = listing

$listing
.receive(on: RunLoop.main)
.compactMap { listing in
listing.id
}
.assign(to: .id, on: self)
.store(in: &cancellables)
}
}

内容视图.swift

struct ContentView: View {
@EnvironmentObject var authSession: AuthSession
@EnvironmentObject var marketplaceViewModel: MarketplaceViewModel
var body: some View {
Group{
if (authSession.currentUser != nil) {
TabView {
MarketplaceView()
.tabItem {
Image(systemName: "shippingbox")
Text("Marketplace")
}.tag(0) // MarketplaceView
AccountView(user: testUser1)
.tabItem {
Image(systemName: "person")
Text("Account")
}.tag(2) // AccountView
} // TabView
.accentColor(.white)
} else if (authSession.currentUser == nil) {
AuthView()
}
}// Group
.onAppear(perform: authenticationListener)

}

// MARK: ++++++++++++++++++++++++++++++++++++++ Methods ++++++++++++++++++++++++++++++++++++++

func authenticationListener() {
// Setup Authentication Listener
authSession.listen()
}
}

任何帮助将不胜感激。

在您的应用程序中,您有:

ContentView().environmentObject(marketplaceViewModel)

所以在"内容视图"中,你应该有第一行:

@EnvironmentObject var marketplaceViewModel: MarketplaceViewModel

注意在"ContentView"中,你有"@EnvironmentObject var authSession: AuthSession" 但这不会从您的应用程序传入。

编辑:使用此受限设置测试通过"市场视图模型"。

class MarketplaceViewModel: ObservableObject {
...
let showMiki = "here is Miki Mouse"
...
}

struct MarketplaceView: View {
@EnvironmentObject var marketplaceViewModel: MarketplaceViewModel

var body: some View {
// ERROR NOT HERE
Text(marketplaceViewModel.showMiki)
//  Text(self.marketplaceViewModel.listingRowViewModels[1].listing.title)
}
}

任何正在寻找将 MVVM 与 Firebase Firestore 一起使用并使视图模型成为环境对象的方法的人 我在下面添加了我的代码。此项目具有列表视图和详细信息视图。每个视图都有相应的视图模型。该项目还使用存储库并使用 Combine。

应用.swift

import SwiftUI
import Firebase
@main
struct MVVMTestApp: App {

@StateObject private var marketplaceViewModel = MarketplaceViewModel(listingRepository: ListingRepository())

// Firebase
init() {
FirebaseApp.configure()
}

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(marketplaceViewModel)
}
}
}

内容视图.swift

import SwiftUI
struct ContentView: View {
var body: some View {
Group {
MarketplaceView()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

市场视图.swift

import SwiftUI
struct MarketplaceView: View {
@EnvironmentObject var marketplaceViewModel: MarketplaceViewModel
var body: some View {
NavigationView {
List {
ForEach(self.marketplaceViewModel.listingRowViewModels, id: .id) { listingRowViewModel in
NavigationLink(destination: ListingDetailView(listingDetailViewModel: ListingDetailViewModel(listing: listingRowViewModel.listing))) {
ListingRowView(listingRowViewModel: listingRowViewModel)
}
} // ForEach
} // List
.navigationTitle("Marketplace")
} // NavigationView
}
}
struct MarketplaceView_Previews: PreviewProvider {
static var previews: some View {
MarketplaceView()
}
}

列表行视图.swift

import SwiftUI
struct ListingRowView: View {
@ObservedObject var listingRowViewModel: ListingRowViewModel
var body: some View {
VStack(alignment: .leading, spacing: 5) {
Text(listingRowViewModel.listing.name)
.font(.headline)
Text(String(listingRowViewModel.listing.description))
.font(.footnote)
}
}
}
struct ListingRowView_Previews: PreviewProvider {
static let listingRowViewModel = ListingRowViewModel(listing: testListing1)
static var previews: some View {
ListingRowView(listingRowViewModel: listingRowViewModel)
}
}

列表详细信息视图.swift

import SwiftUI
struct ListingDetailView: View {
var listingDetailViewModel: ListingDetailViewModel

var body: some View {
VStack(spacing: 5) {
Text(listingDetailViewModel.listing.name)
.font(.headline)
Text(String(listingDetailViewModel.listing.description))
.font(.footnote)
}
}
}
struct ListingDetailView_Previews: PreviewProvider {
static let listingDetailViewModel = ListingDetailViewModel(listing: testListing1)

static var previews: some View {
ListingDetailView(listingDetailViewModel: listingDetailViewModel)
}
}

市场视图模型.swift

import Foundation
import SwiftUI
import Combine
class MarketplaceViewModel: ObservableObject {
var listingRepository: ListingRepository
@Published var listingRowViewModels = [ListingRowViewModel]()

private var cancellables = Set<AnyCancellable>()

init(listingRepository: ListingRepository) {
self.listingRepository = listingRepository
self.startCombine()
}

func startCombine() {
listingRepository
.$listings
.receive(on: RunLoop.main)
.map { listings in
listings.map { listing in
ListingRowViewModel(listing: listing)
}
}
.assign(to: .listingRowViewModels, on: self)
.store(in: &cancellables)
}
}

列表行视图模型.swift

import Foundation
import SwiftUI
import Combine
class ListingRowViewModel: ObservableObject {
var id: String = ""
@Published var listing: Listing

private var cancellables = Set<AnyCancellable>()

init(listing: Listing) {
self.listing = listing

$listing
.receive(on: RunLoop.main)
.compactMap { listing in
listing.id
}
.assign(to: .id, on: self)
.store(in: &cancellables)
}
}

列表详细信息视图模型.swift

import Foundation
import SwiftUI
import Combine
class ListingDetailViewModel: ObservableObject, Identifiable {
var listing: Listing

init(listing: Listing) {
self.listing = listing
}
}

上市.swift

import Foundation
import SwiftUI
import FirebaseFirestore
import FirebaseFirestoreSwift
struct Listing: Codable, Identifiable {
@DocumentID var id: String?
var name: String
var description: String
}

列表存储库.swift

import Foundation
import Firebase
import FirebaseFirestore
import FirebaseFirestoreSwift
class ListingRepository: ObservableObject {
// MARK: ++++++++++++++++++++++++++++++++++++++ Properties ++++++++++++++++++++++++++++++++++++++
// Access to Firestore Database
let db = Firestore.firestore()

@Published var listings = [Listing]()

init() {
startSnapshotListener()
}

// MARK: ++++++++++++++++++++++++++++++++++++++ Methods ++++++++++++++++++++++++++++++++++++++
func startSnapshotListener() {
db.collection("listings").addSnapshotListener { (querySnapshot, error) in
if let error = error {
print("Error getting documents: (error)")
} else {
guard let documents = querySnapshot?.documents else {
print("No Listings.")
return
}

self.listings = documents.compactMap { listing in
do {
return try listing.data(as: Listing.self)
} catch {
print(error)
}
return nil
}
}
}
}
}

最新更新