rxswfit+mvvm简单实践

一个简单的活动列表展示页面

1
2
3
4
5
6
7
8
9
10
11
12
13
extension  where Base: LoadingHUD {


var isAnimating: Binder<Bool> {
return Binder(self.base) { hud, active in
if active {
hud.startAnimating()
} else {
hud.stopAnimating()
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
func bindData() {
// 加载状态指示器
let activityIndicator = ActivityIndicator()
// 将网络请求取得活动列表数据转化为可观察的序列,之后驱动 tableView 使用
// 当观察者对 BehaviorSubject 进行订阅时,它会将源 Observable 中最新的元素发送出来(如果不存在最新的元素,就发出默认元素)。然后将随后产生的元素发送出来。
let activityResponseDriver = BehaviorSubject(value: ())
.flatMapLatest {
Networking
.rx.requestObject(ActivityResponse.self, url: URL.notifyList)
.trackActivity(activityIndicator) // 追踪加载状态
}
.asDriver(onErrorJustReturn: ActivityResponse())
// 驱动HUD
activityIndicator.asDriver().drive(hud.rx.isAnimating).disposed(by: disposeBag)

// tableView 数据源
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Activity>>(
configureCell: { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: ActivityListCell.classIdentifier, for: indexPath) as! ActivityListCell
cell.activity = element
return cell
},
titleForHeaderInSection: { dataSource, sectionIndex in
return dataSource[sectionIndex].model
}
)
// 活动列表数据通过 map 转化为一个section数据 再驱动tableView
activityResponseDriver
.map { [SectionModel(model: kActivityNotificationStringLocalized, items: $0.data)] }
.drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
// 活动列表数据 驱动统计view的展示
activityResponseDriver
.map { $0.informationNum }
.drive(onNext: { [unowned self] (informationNum) in
self.informationNumView.isHidden = informationNum == 0
self.informationNumView.setTitle("(informationNum)", for: .normal)
})
.disposed(by: disposeBag)

tableView.rx.modelSelected(Activity.self)
.subscribe(onNext: { [unowned self] (activity) in
if #available(iOS 9.0, *) {
let vc = SFSafariViewController(url: url)
vc.title = kActivityNotificationStringLocalized
self.navigationController?.pushViewController(vc, animated: true)
} else {
let vc = ActivityWebViewController()
vc.title = kActivityNotificationStringLocalized
vc.url = url
self.navigationController?.pushViewController(vc, animated: true)
}
})
.disposed(by: disposeBag)

}

一个订单信息列表页面(DataPageable是一个提供分页功能的序列输出功能类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
func bindData() {

let pageVariable = Variable(1)

tableView.mj_header = MJRefreshNormalHeader(refreshingBlock: {
pageVariable.value = 1
})
tableView.mj_footer = MJRefreshBackNormalFooter(refreshingBlock: {
pageVariable.value += 1
})

let viewModel = OrderInfoListViewModel(pageVariable: pageVariable)

// hud
viewModel.hudDriver
.drive(hud.rx.isAnimating)
.disposed(by: disposeBag)

// tableView dataSource
viewModel.orderInfoListDriver
.drive(tableView.rx.items(cellIdentifier: OrderInfoCell.classIdentifier, cellType: OrderInfoCell.self)) { (row, orderInfo, cell) in
cell.orderInfo = orderInfo
}
.disposed(by: disposeBag)

// endRefresh
viewModel.endHeaderRefreshingDriver
.drive(tableView.rx.endHeaderRefreshing)
.disposed(by: disposeBag)
viewModel.endFooterRefreshingDriver
.drive(tableView.rx.endFooterRefreshing)
.disposed(by: disposeBag)

// some UI
viewModel.orderInfoListDriver
.skip(1) // skip 跳过第一个元素,emptyView.isHidden
.map { $0.count > 0 }
.drive(emptyDataView.rx.isHidden)
.disposed(by: disposeBag)

// tableView Delegate
tableView.rx
.modelSelected(OrderInfo.self)
.subscribe(onNext: { [weak self] orderInfo in
let vc = OrderDetailController()
vc.tradeNo = orderInfo.tradeNo
self?.navigationController?.pushViewController(vc, animated: true)
})
.disposed(by: disposeBag)
}

}

class OrderInfoListViewModel {

// outputs
let hudDriver: Driver<Bool>
let orderInfoListDriver: Driver<[OrderInfo]>
let endHeaderRefreshingDriver: Driver<Void>
let endFooterRefreshingDriver: Driver<Int>

init(pageVariable: Variable<Int>) {
// 元组,包含网络请求状态序列、数据列表序列,刷新信号
let driversTuple = DataPageable.getDriversTuple(pageVariable: pageVariable, url: URL.consumeRecordQuery, param: ["mobile": UserManager.shared.currentUser!.mobile], type: OrderInfo.self, path: "orderInfo")
let taskStatusDriver = driversTuple.taskStatusDriver
orderInfoListDriver = driversTuple.dataListDriver
endHeaderRefreshingDriver = driversTuple.endHeaderRefreshingDriver
endFooterRefreshingDriver = driversTuple.endFooterRefreshingDriver

hudDriver = Driver
.combineLatest(taskStatusDriver, orderInfoListDriver) { $0.isExecuting && $1.isEmpty }
.distinctUntilChanged()
}

}

历程

我最开始是通过 RxSwift电子书 官方电子书 + Demo来学习的,也看过 中文文档 。看到不是特别能理解的内容时(比如各种创建序列的方式、Subject、Operator),跟着敲一下代码,运行一下,感觉会比较容易记忆和理解,也会惊叹代码还可以这样简洁,感觉学习 RxSwift 是个非常有趣的过程。然后把现有的工程一点点MVVM化,遇到问题就回头去看文档和Demo,加深印象和理解,基本就能处理一些简单的业务逻辑了。