【问题标题】:Adding An EnvironmentObject to a ViewModel SwiftUI and MVVM将 EnvironmentObject 添加到 ViewModel SwiftUI 和 MVVM
【发布时间】:2021-10-30 10:27:12
【问题描述】:

我正在使用 SwiftUI,并且正在使用 MVVM,我的 VM 充当我的 EnvironmentObjects。

我首先创建一个 AuthSession 环境对象,其中存储了 currentUserId 的字符串。

然后,我为尝试包含 AuthSession 环境对象的 Offers 创建另一个环境对象,这样我就可以使用 Combine 过滤从数据库中提取的结果。当我将 @EnvironmentObject 属性添加到 Offer 视图模型时,我收到一条错误消息,指出未通过 AuthSession。这是有道理的,因为它不是视图。

我的问题是,最好在视图中对结果进行排序,还是有办法将 EnvironmentObject 添加到另一个 EnvironmentObject?我知道有一个答案here,但这个模型答案没有使用 VM 作为 EO。

应用文件

@main
struct The_ExchangeApp: App {
    
    // @EnvironmentObjects
    @StateObject private var authListener = AuthSession()
    @StateObject private var offerHistoryViewModel = OfferHistoryViewModel(offerRepository: OfferRepository())
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(authListener)
                .environmentObject(offerHistoryViewModel)
        }
    }
}

AuthSession.swift

class AuthSession: ObservableObject {
    @Published var currentUser: User?
    @Published var loggedIn = false
    @Published var currentUserUid = ""
    
    // Intitalizer
    init() {
        
        self.getCurrentUserUid()
    }
}

OfferHistoryViewModel.swift - 在 startCombine() 中的 .filter 之后调用错误。

class OfferHistoryViewModel: ObservableObject {
    // MARK: ++++++++++++++++++++++++++++++++++++++ Properties ++++++++++++++++++++++++++++++++++++++
    
    // Access to AuthSession for filtering offer made by the current user.
    @EnvironmentObject var authSession: AuthSession

    // Properties
    var offerRepository: OfferRepository
    
    // Published Properties
    @Published var offerRowViewModels = [OfferRowViewModel]()
    
    // Combine Cancellable
    private var cancellables = Set<AnyCancellable>()
    
    // MARK: ++++++++++++++++++++++++++++++++++++++ Methods ++++++++++++++++++++++++++++++++++++++
    
    // Intitalizer
    init(offerRepository: OfferRepository) {
        self.offerRepository = offerRepository
        self.startCombine()
    }
    
    // Starting Combine - Filter results for offers created by the current user only.
    func startCombine() {
        offerRepository
            .$offers
            .receive(on: RunLoop.main)
            .map { offers in
                offers
                    .filter { offer in
                        (self.authSession.currentUserUid != "" ? offer.userId == self.authSession.currentUserUid : false) // ERROR IS CALLED HERE
                    }
                    .map { offer in
                        OfferRowViewModel(offer: offer)
                    }
            }
            .assign(to: \.offerRowViewModels, on: self)
            .store(in: &cancellables)
    }
}

错误

Thread 1: Fatal error: No ObservableObject of type AuthSession found. A View.environmentObject(_:) for AuthSession may be missing as an ancestor of this view.

【问题讨论】:

  • 不可能@EnvironmentObject 并且大多数 SwiftUI 包装器仅适用于 SwiftUI 视图,这是 @Published 中的唯一包装器
  • 我想通了。可以在那里使用 combine 对视图进行排序吗?这似乎与执行繁重工作的 VM 背道而驰。
  • 你应该把工作放在视野之外。

标签: swiftui


【解决方案1】:

我通过将 AuthSession 中的 currentUserUid 从我的视图传递到视图模型来解决这个问题。视图模型更改为以下内容。

class OfferHistoryViewModel: ObservableObject {
    // MARK: ++++++++++++++++++++++++++++++++++++++ Properties ++++++++++++++++++++++++++++++++++++++
    
    var offerRepository: OfferRepository
    
    // Published Properties
    @Published var offerRowViewModels = [OfferRowViewModel]()
    
    // Combine Cancellable
    private var cancellables = Set<AnyCancellable>()
    
    // MARK: ++++++++++++++++++++++++++++++++++++++ Methods ++++++++++++++++++++++++++++++++++++++
    
    // Intitalizer
    init(offerRepository: OfferRepository) {
        self.offerRepository = offerRepository
    }
    
    // Starting Combine - Filter results for offers created by the current user only.
    func startCombine(currentUserUid: String) {
        offerRepository
            .$offers
            .receive(on: RunLoop.main)
            .map { offers in
                offers
                    .filter { offer in
                        (currentUserUid != "" ? offer.userId == currentUserUid : false)
                    }
                    .map { offer in
                        OfferRowViewModel(offer: offer)
                    }
            }
            .assign(to: \.offerRowViewModels, on: self)
            .store(in: &cancellables)
    }
}

然后在视图中我在 onAppear 中传递 currentUserUid。

struct OfferHistoryView: View {
    // MARK: ++++++++++++++++++++++++++++++++++++++ Properties ++++++++++++++++++++++++++++++++++++++
        
    @EnvironmentObject var authSession: AuthSession
    @EnvironmentObject var offerHistoryViewModel: OfferHistoryViewModel
    
    // MARK: ++++++++++++++++++++++++++++++++++++++ View ++++++++++++++++++++++++++++++++++++++
    
    var body: some View {
         // BuildView
        } // View
        .onAppear(perform: {
            self.offerHistoryViewModel.startCombine(currentUserUid: self.authSession.currentUserUid)
        })
    }
}

这对我很有效,我希望它对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多