首页 > 解决方案 > SwiftUI MVVM 方法与视图中的变量

问题描述

我正在基于 MVVM 设计模式在 SwiftUI 中构建一个应用程序。我正在做的是这样的:

struct AddInvestment: View {
    
    @ObservedObject private var model = AddInvestmentVM()
    @State private var action: AssetAction?
    @State private var description: String = ""
    @State private var amount: String = ""
    @State private var costPerShare: String = ""
    @State private var searchText: String = ""
    @State var asset: Asset?
    
    var body: some View {
        NavigationView {
            VStack {
                Form {
                    Section("Asset") {
                        NavigationLink(model.chosenAsset?.completeName ?? "Scegli asset") {
                            AssetsSearchView()
                        }
                    }
                    Section {
                        Picker(action?.name ?? "", selection: $action) {
                            ForEach(model.assetsActions) { action in
                                Text(action.name).tag(action as? AssetAction)
                            }
                        }
                        .pickerStyle(.segmented)
                        .listRowBackground(Color.clear)
                    }
                    Section {
                        TextField("", text: $amount, prompt: Text("Unità"))
                            .keyboardType(UIKit.UIKeyboardType.decimalPad)
                        TextField("", text: $costPerShare, prompt: Text("Prezzo per unità"))
                            .keyboardType(UIKit.UIKeyboardType.decimalPad)
                    }
                }
            }
            .navigationTitle("Aggiungi Investimento")
        }
        .environmentObject(model)
        .onAppear {
            model.fetchBaseData()
        }
    }
}

然后我有我的 ViewModel,这个:

class AddInvestmentVM: ObservableObject {
    
    private let airtableApiKey = "..."
    private let airtableBaseID = "..."
    
    private let assetsTableName = "..."
    private let assetsActionsTableName = "..."
    
    private let airtable = Airtable.init("...")
    
    private var tasks = [AnyCancellable]()
    
    @Published var chosenAsset: Asset?
    @Published var assets = [Asset]()
    @Published var assetsActions = [AssetAction]()
    
    init() { }
    
    func fetchBaseData() {
        
        print("Fetching data...")
        
        let assetActionsRequest = AirtableRequest.init(baseID: airtableBaseID, tableName: assetsActionsTableName, view: nil)
        let assetsActionsPublisher: AnyPublisher<[AssetAction], AirtableError> = airtable.fetchRecords(request: assetActionsRequest)
        
        assetsActionsPublisher
            .eraseToAnyPublisher()
            .sink { completion in
                print("** **")
                print(completion)
            } receiveValue: { assetsActions in
                print("** **")
                print(assetsActions)
                self.assetsActions = assetsActions
            }
            .store(in: &tasks)
    }
}

现在,如您所见,我在视图上有一些属性绑定到一些文本字段。让我们考虑这些:

@State private var description: String = ""
@State private var amount: String = ""
@State private var costPerShare: String = ""
@State private var searchText: String = ""

记住 MVVM 模式,这些属性是否应该在 ViewModel 中声明并从那里绑定?或者这是一个正确的方法?

标签: iosswiftdesign-patternsmvvmswiftui

解决方案


我倾向于将视图视为一个设备或一组设备(iPhone、iPad、Mac 等)的应用程序人机界面的定义。我倾向于将模型视为应用程序逻辑所在的地方,它不属于任何视图(业务逻辑、数据操作、网络操作等)。我认为 ViewModel 是 View 需要使 View 工作的任何逻辑退出的地方,例如格式化数据以在 View 上显示。我认为您正在寻找视图所需的数据元素的单一事实来源。在我看来,当且仅当它特定于 View 时,它才应该存在于 ViewModel 中。任何关于应用程序的逻辑或数据都应该存在于模型中,而不是视图模型中。通过这样做,我使我的模型更可重用,更独立于视图。总是有边缘情况,我想如果我构建一个完全独立的第二个视图,是否需要重新创建这些数据或逻辑,比如一个通过 HTML/JavaScript/等与浏览器一起工作的视图。如果我必须使用新的视图层重新创建该数据或逻辑,那么我认为数据或逻辑属于模型。如果数据或逻辑特定于该 View 层,那么我会将其放入该 View 的 ViewModel 中。

使用上面的想法,您的 AddInvestment 类看起来就像我将放在模型中的东西,而不是 ViewModel。当然,这完全是一个品味问题,我认为这里没有任何一个正确的答案,但我认为我会在自己的应用程序中对其进行切片和切块。


推荐阅读