Che*_*rK2 5 model-view-controller design-patterns ios swift swiftui
我正在尝试实现像MVC这样的设计模式,以实现代码不同部分之间的低耦合。我个人认为网上很少有关于 IOS 或 swift UI 开发和 MVC 模式的资料没有帮助。
我想了解的是控制器类应该如何控制或呈现 Swift UI 中的 UI?
例如,遵循 MVC 模式 - 视图不应该知道模型的外观,因此将对象从数据库发送回视图以直观地呈现它不是一个好主意。
假设我们有以下视图和控制器,当从数据库发送回数据时,我应该如何进行控制器和视图之间的交互,以便将其直观地呈现在视图中?
看法:
import SwiftUI
import Foundation
struct SwiftUIView: View {
var assignmentController = AssignmentController()
@State var assignmentName : String = ""
@State var notes : String = ""
var body: some View {
NavigationView {
VStack {
Form {
TextField("Assignment Name", text: $assignmentName)
TextField("Notes", text: $notes)
}
Button(action: {
self.assignmentController.retrieveFirstAssignment()
}) {
Text("Get The First Assignment !")
}
}
.navigationBarTitle("First Assignment")
}
}
}
Run Code Online (Sandbox Code Playgroud)
控制器
var assignmentModel = AssignmentModel()
func retrieveFirstAssignment()
{
var firstAssignment : Assignment
firstAssignment=assignmentModel.retrieveFirstAssignment()
}
Run Code Online (Sandbox Code Playgroud)
目前,它对找到的对象没有任何作用。
模型
模型中的对象由两个字符串字段组成:“ taskingName ”和“ notes ”。
*我们假设分配模型有一个工作函数,可以从数据库中检索一项任务,以便将其呈现在视图中。
struct SwiftUIView: View {
@State m = AssignmentModel()
var body: some View {
// use m
}
func loadAssignmentFromDB() {
m.retrieveFirstAssignment()
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我所说的“带有内置MVVM的增强型MVC”。
它同时满足了您对 MVC 和可能的 MVVM 的追求,并且花费更少的精力。
现在我将论证原因:
当您的模型是值类型时,这是有意义的。如果没有您的具体说明,没有什么可以改变它,例如;@State注解。由于改变模型的唯一方法是通过这些指定的端点,因此您的控件仅在改变这些端点的函数中生效。
确实,SwiftUI 与 MVVM 比与 MVC 更接近。然而,Apple 文档中的几乎所有示例代码都非常简单,以至于完全忽略了 ViewModel(和/或 MVC 中的控制器)。一旦您开始创建更大的项目,就需要某种东西来桥接您的视图和模型。然而,在我看来,SwiftUI 文档还没有以令人满意的方式完全解决这个问题。
SwiftUI 增强了 MVC。拥有 ViewModel 的目的是什么?
a.) 具有模型视图绑定,这出现在我上面的代码片段中。
b.) 管理与对象关联的状态。如果@State没有给您留下由您来管理状态的印象,那么我不知道什么会。有趣的是,有多少 MVVM 开发人员对此视而不见。正如您不需要用于控制的视图控制器一样,您也不需要用于虚拟机的视图模型。设计模式是一种@State心灵。不涉及具体的命名和严格的结构。
c.) 说我对 MVVM 持开放态度,但没有任何基础。您认为哪个代码片段更有机会扩展到更大的项目?我的紧凑型还是另一回复中建议的?提示:想想你将拥有多少额外的文件、视图模型、可观察对象、粘合代码、将 vm 作为参数传递、接受 vm 作为参数的 init 函数。这些只是供您在另一个对象中编写一些代码。它没有提到减少或简化手头的任务。该死的,它甚至会告诉您如何重构控制代码,因此您很可能会再次重复在 MVC 中做错的事情。我是否提到过 ViewModel 是一个具有隐式状态管理的共享引用类型对象?那么当您打算用引用类型模型覆盖它时,拥有值类型模型有什么意义呢?
有趣的是,MVVM 开发人员说 SwiftUI 的基本形式无法扩展到更大的项目。保持事情简单是扩展规模的唯一方法。
这就是我观察到的 2020 年开发进展路线图。 Day1:初学者 Day2:谷歌一些,放弃 MVC Day3:谷歌更多,SwiftUI 不可扩展 Day4:好的,我需要 MVVM+RxSwift+Coordinator+Router+DependencyInjection 来避免SDK的缺点。
由于这似乎是一个常见的初学者问题,我的建议是在跑步之前先学会走路。
我个人见过 RxSwift 开发人员将控制器代码移至视图,以便控制器显得“干净”,并且需要 3 个第三方库(一个是自定义 fork)来发送 http GET。
如果你不能让简单的事情变得简单,设计模式就没有意义。
对我来说这是一个非常好的问题。确实,SwiftUI 与 MVVM 比与 MVC 更接近。然而,Apple 文档中的几乎所有示例代码都非常简单,以至于完全忽略了 ViewModel(和/或 MVC 中的控制器)。一旦您开始创建更大的项目,就需要某种东西来桥接您的视图和模型。然而,在我看来,SwiftUI 文档还没有以令人满意的方式完全解决这个问题。我希望其他开发人员能够纠正我或对此进行扩展(我仍在学习),但这是我迄今为止发现的:
@Published创建一个大型 ObservableObject 并添加其所有属性可能很诱人。然而,这意味着观察该对象的视图会更新(有时是可见的),即使视图甚至不依赖的属性发生更改。以下是我将如何将其应用到您的示例中:
import SwiftUI
//
// Helper class for observing value types
//
class ObservableValue<Value: Hashable>: ObservableObject {
@Published var value: Value
init(initialValue: Value) {
value = initialValue
}
}
//
// Model
//
struct Assignment {
let name : String
let notes: String
}
//
// ViewModel?
//
// Usually a view model transforms data so it is usable by the view. Strings are already
// usable in our components. The only change here is to wrap the strings in an
// ObservableValue so views can listen for changes to the individual properties.
//
// Note: In Swift you often see transformations of the data implemented as extensions to
// the model rather than in a separate ViewModel.
class AssignmentModelView {
var name : ObservableValue<String>
var notes: ObservableValue<String>
init(assignment: Assignment) {
name = ObservableValue<String>(initialValue: assignment.name)
notes = ObservableValue<String>(initialValue: assignment.notes)
}
var assignment: Assignment {
Assignment(name: name.value, notes: notes.value)
}
}
//
// Controller
//
// Publish the first assignment so Views depending on it can update whenever we change
// the first assignment (**not** update its properties)
class AssignmentController: ObservableObject {
@Published var firstAssignment: AssignmentModelView?
func retrieveFirstAssignment() {
let assignment = Assignment(name: "My First Assignment", notes: "Everyone has to start somewhere...")
firstAssignment = AssignmentModelView(assignment: assignment)
}
}
struct ContentView: View {
// In a real app you should use dependency injection here
// (i.e. provide the assignmentController as a parameter)
@ObservedObject var assignmentController = AssignmentController()
var body: some View {
NavigationView {
VStack {
// I prefer to use `map` instead of conditional views, since it
// eliminates the need for forced unwrapping
self.assignmentController.firstAssignment.map { assignmentModelView in
Form {
ObservingTextField(title: "Assignment Name", value: assignmentModelView.name)
ObservingTextField(title: "Notes", value: assignmentModelView.notes)
}
}
Button(action: { self.retrieveFirstAssignment() }) {
Text("Get The First Assignment !")
}
}
.navigationBarTitle("First Assignment")
}
}
func retrieveFirstAssignment() {
assignmentController.retrieveFirstAssignment()
}
}
//
// Wrapper for TextField that correctly updates whenever the value
// changes
//
struct ObservingTextField: View {
let title: String
@ObservedObject var value: ObservableValue<String>
var body: some View {
TextField(title, text: $value.value)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Run Code Online (Sandbox Code Playgroud)
这对于您的应用程序来说可能有点过分了。有一个更简单的版本,但它的缺点是文本字段会更新,即使它们的内容没有改变。在这个特定的例子中,我认为这并不重要。对于较大的项目,它可能变得很重要,不仅仅是出于性能原因,但更新有时非常明显。供参考:这是更简单的版本。
import SwiftUI
// Model
struct Assignment {
let name : String
let notes: String
}
// ViewModel
class AssignmentViewModel: ObservableObject {
@Published var name : String
@Published var notes: String
init(assignment: Assignment) {
name = assignment.name
notes = assignment.notes
}
}
// Controller
class AssignmentController: ObservableObject {
@Published var firstAssignment: AssignmentViewModel?
func retrieveFirstAssignment() {
let assignment = Assignment(name: "My First Assignment", notes: "Everyone has to start somewhere...")
firstAssignment = AssignmentViewModel(assignment: assignment)
}
}
struct ContentView: View {
// In a real app you should use dependency injection here
// (i.e. provide the assignmentController as a parameter)
@ObservedObject var assignmentController = AssignmentController()
var body: some View {
NavigationView {
VStack {
self.assignmentController.firstAssignment.map { assignmentModelView in
FirstAssignmentView(firstAssignment: assignmentModelView)
}
Button(action: { self.retrieveFirstAssignment() }) {
Text("Get The First Assignment !")
}
}
.navigationBarTitle("First Assignment")
}
}
func retrieveFirstAssignment() {
assignmentController.retrieveFirstAssignment()
}
}
struct FirstAssignmentView: View {
@ObservedObject var firstAssignment: AssignmentViewModel
var body: some View {
Form {
TextField("Assignment Name", text: $firstAssignment.name)
TextField("Notes", text: $firstAssignment.notes)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4324 次 |
| 最近记录: |