Medium上看到一篇文章感觉不错,就用我专业16级的英语翻译下,原文链接贴了估计你们也看不了,就凑合看我翻译的吧
2019年苹果发布了SwiftUI
。它非常强大,今年的WWDC
,苹果公司也宣布了对SwiftUI
进行了一系列的提升改进,然而并没有多少关于如何用SwiftUI
实现面向Protocol编程的话题,
当然网上有很多的文章,我在这里用5分钟总结下,这篇文章同样适合初学者
我们最终的目标是能够用SwiftUI
获取数据(假数据)并填充到List View
上,我们的demo是一个显示多个足球运动员名字的列表,通常我们会把所有的东西都放到一个 Struct view
对象里,现在我们试着分三层来讨论:
View Layer
Business Layer
Entity Layer
我们先从Entity Layer 开始,为了能把数据通过viewmodel
显示到view
上,我们需要一个遵循了Identifiable
协议(现在是SwiftUI
的一部分了)的 dummy model
,这样view
就能知道每一行的不同了
// The dummy model
struct Player: Identifiable {
let id: Int
let name: String
let number: Int
}
现在我们来看看BL
层,通常我们一般都会把所有的东西都写到一个viewmodel
里,那假如我们只想让调用者知道viewmodel
里的暴露出来的DataSource
和action
该怎么办,这个时候我们就可以用面向协议的编程来实现
// The datasource
protocol ItemViewModelDatasource {
var data: [Player] { get set }
}
// The action capabilities
protocol ItemViewModelAction {
func fetchItem()
func addPlayer(_ player: Player)
}
上面这这两个协议是我们后面用到的viewmode的datasource和action的接口,现在既然我们是在用SwiftUI
编程,所以我们还需要一个遵循了ObservableObject
协议的Class和一个有@Publish
属性的数据源
// Base model protocol
protocol ListViewModel: ObservableObject {
var action: ItemViewModelAction { get }
var datasource: ItemViewModelDatasource { get set }
}
上面是ListViewModel
协议,我们把它封装并且限制它只能访问action
和DataSource
,
现在让我们来实现完整的viewmodel class
class PlayerListViewModel: ListViewModel, ItemViewModelAction, ItemViewModelDatasource {
// MARK: - Datasource
@Published var data: [Player] = []
private lazy var _datasource: ItemViewModelDatasource = {
return self
}()
var datasource: ItemViewModelDatasource {
get {
return _datasource
}
set {
_datasource = newValue
}
}
// MARK: - Action
var action: ItemViewModelAction {
return self
}
func fetchItem() {
data = [
Player(id: 1, name: "G. Donnaruma", number: 1),
Player(id: 2, name: "Andrea Conti", number: 12),
Player(id: 3, name: "G. Bonaventura", number:5),
Player(id: 1, name: "Zlatan Ibrahimovic", number: 21)
]
}
func addPlayer(_ player: Player) {
data.append(player)
}
}
正如你所看到的,viewmodel
需要遵循ObservedObject
协议还有DataSource
和action
,对了别忘了在属性data
前面加上@Published
修饰符,这一步是强制的,因为这个修饰符的功能就是告诉我们的view
,这个属性来自于一个可观察(observable)
对象然后通知监听者
struct ContentView: View where Model: ListViewModel {
@ObservedObject var viewModel: Model
@State var items: [String] = [
"G. Donnarumma",
"Andrea Conti",
"Jack Bonaventura",
"Zlatan Ibrahimovic"
]
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
Text("Item - \(item)")
}
}
.navigationBarTitle("AC Milan Player")
}
}
}
最后一步,我们可以像上面那样创建一个简单的view
,当我们声明Struct
的时候要在名字后面跟上Model
类型和它支持的类型ListViewModel
,这样我们就能够保证调用者传过来的model
是ListViewModel
类型的,上面的代码我们还是用的@State items
变量来作为数据源,现在我们要把它改为用我们的viewmodel
里DataSource
struct ContentView: View where Model: ListViewModel {
@ObservedObject var viewModel: Model
var body: some View {
NavigationView {
List {
ForEach(viewModel.datasource.data.indices, id: \.self) { index in
Text("Item - \(self.viewModel.datasource.data[index].name)")
}
}
.onAppear {
self.viewModel.action.fetchItem()
}
.navigationBarTitle("AC Milan Player")
}
}
}
现在我们把list
和viewmodel
里的数据做了绑定,而不是用local state
,我们还做了在list
的.onAppear
方法里去获取数据,一旦获取到数据就会得到和下图一样的效果
swiftUI.png
好了就到此为止了,通过该文你应该大概了解了在SwiftUI
里如何分离view
和business logic
喜欢的朋友可以动动小手,关注下公众号
公众号
还有小程序
小程序