iOS 中的 MVVM 架构早就是个老生常谈的问题,相比于传统的 MVC 架构方式, MVVM 比较核心的地方在于双向绑定的过程,即 View 和 ViewModel 之间的绑定,而创建绑定关系最优方案是经过响应式的方式构建,iOS 原生方面能够经过 KVO + KVC 的方式去搭建响应式,缺点是API相对复杂,操做不方便,纯 Swift 对象须要标记为 dynamic
,须要手动管理 KVO 的生命周期。react
RxSwift 属于 ReactiveX 系列,目前存在多个语言版本,基本覆盖所有主流编程语言,其专一于异步编程与控制可观察数据(或者事件)流的API,背后是微软的团队在开发维护,因此稳定性较高。RxSwift 是一种响应式的编程思想,故很是适合与 MVVM 架构配合使用。编程
MVVM 架构最核心的部分无疑是 ViewModel ,其主要负责模块的逻辑处理、状态的维护等。在 iOS 开发中,状态一词说起的相对较少,不像 React 中组件对状态的依赖那么强烈。其实每个具备交互功能的控件都会依赖于状态,状态决定控件的表示方式,故状态在 iOS 开发中一样重要。传统的 MVC 方式,状态的管理主要是 Controller 负责,在 MVVM 中由 ViewModel 管理。网络
ReactorKit 是一个轻量级的响应式框架,其依赖于 RxSwift 并结合了 Flux 。Flux 是 faceBook 提出的一种架构思想,其核心概念是数据的单向流动, 一样适用于 iOS,在 iOS 中主要表现为 Action 和 State 两个部分:架构
View 发出的 Action,经由 Reactor 处理后,由 State 抛出后绑定到 View 上,即每个状态的改变都要派发一个 Action 。也就是说 View 以怎样的方式显示是被动的,如想改变自身的渲染方式须要本身派发 Action。框架
ReactorKit 将 Controller 和 View 都归类为 View
,使用方式是须要实现 View
协议:异步
class ReactorViewController: UIViewController, View {
...
}
复制代码
能够理解为 Reactor 就是 ViewModel 。Reactor 一样是协议,其限定了 ViewModel 的行为(代码片断来自网络):编程语言
class ReactorViewModel: Reactor {
/// - Action: View 派发的事件
enum Action {
case refreshFollowingStatus(Int)
case follow(Int)
}
/// - Mutation:Action 和 State 之间的过渡
enum Mutation {
case setFollowing(Bool)
}
/// - State:状态管理器
struct State {
var isFollowing: Bool = false
}
/// - 状态管理器初始化
let initialState: State = State()
}
复制代码
func mutate(action: Action) -> Observable<Mutation> {
switch action {
/// - View 派发的 Action 会在这里被响应
case let .refreshFollowingStatus(userID):
return UserAPI.isFollowing(userID) // create an API stream
.map { (isFollowing: Bool) -> Mutation in
/// - 派发一个 Mutation
return Mutation.setFollowing(isFollowing)
}
/// - 同理
case let .follow(userID):
return UserAPI.follow()
.map { _ -> Mutation in
return Mutation.setFollowing(true)
}
}
func reduce(state: State, mutation: Mutation) -> State {
/// - 对 State 作一份拷贝,由于 State 是 let 声明的结构体
var state = state
switch mutation {
/// - 将 Mutation 所关联的数据映射到 State 上
case let .setFollowing(isFollowing):
/// - 改变 State
state.isFollowing = isFollowing
/// - 返回一个新的 State
return state
}
复制代码
以上是 ViewModel 的大体工做流程:Action -> Mutatuin -> State,还有一些可选的API,如 transform(),可翻阅官方文档查看。经过以上代码可知,ReactorKit 符合 Flux 编程思想,简单来讲 State 改变需经过 Action。ide
众所周知,MVVM 中 Controller 需持有 ViewModel,一样 ReactorKit 中的 View 协议规定需显示的指定 Reactor 的类型,并提供了 bind()
方法,能够在这个方法中创建 View 和 ViewModel 之间的绑定关系。异步编程
class ReactorViewController: UIViewController, View {
func bind(reactor: ReactorViewModel) {
/// - View 发出的 Action 绑定到了 ViewModel(Reactor) 的 action 上
refreshButton.rx.tap.map { Reactor.Action.refresh }
.bindTo(reactor.action)
.addDisposableTo(self.disposeBag)
/// - ViewModel(Reactor) 的 State 绑定到了 View 上,并当即根据 State 渲染
reactor.state.map { $0.isFollowing }
.bindTo(followButton.rx.isSelected)
.addDisposableTo(self.disposeBag)
}
}
复制代码
有了 ReactorKit 的协助,ViewModel 的行为更清晰明了。ui
Coordinator 导航层。传统的开发模式下,页面间的跳转是经过 navigationController
的 push()
方法,这种方法当然便捷,可是实现跳转存在页面间耦合。Coordinator 的诞生就是为了解决这一问题,固然 路由 或者引入 中间管理层 也能够实现解耦,但 Coordintaor 更轻量。
引入 Coordinator 后跳转逻辑对页面不可见,由 Coordinator 管理,其提供了 navigationController
的接口并持有 Controller,跳转逻辑隐藏在了 Coordinator 中。Coordinator 独立与 MVVM 以外,是一个附加层,不依赖于 MVVM 中的任何一部分。能够理解为 Coordinator 是每一个组件对外暴露的接口,当然页面间的交互,只能经过 Coordinator,一样依赖于 RxSwift。
/// - 页面 Pop 后触发的事件
enum PopResult {
case reload
case cancel
}
final class ExampleCoordinator: BaseCoordinator<PopResult> {
override func start() -> Observable<PopResult> {
let controller = ReactorViewController()
let reactor = ReactorViewModel()
let cancel = controller.popedAction
.asObserver()
.map { PopResult.cancel }
let delete = reactor.state
.map { $0.isDelete }
.filter { (bool) -> Bool in
return bool
}
.map { _ in PopResult.reload }
return Observable
.merge(cancel, delete)
.take(1).do(onNext: { [weak self] (result) in
if let `self` = self {
switch result {
case .reload:
self.navigationController.popViewController(animated: true)
break
default:
break
}
}
})
}
}
复制代码
self.coordinate(to: ExampleCoordinator())
.subscribe(onNext: { result in
switch result {
case reload:
...
case cancel:
...
}
})
.disposed(by: self.disposeBag)
复制代码
Coordinator 源码一样很简单
public protocol CoordinatorType: NSObjectProtocol {
var identifier: UUID { get }
var childCoordinators: [UUID: Any] { get set }
var navigationController: RTRootNavigationController! { get set }
}
public extension CoordinatorType {
func store<T>(coordinator: BaseCoordinator<T>) {
coordinator.navigationController = navigationController
childCoordinators[coordinator.identifier] = coordinator
}
func free<T>(coordinator: BaseCoordinator<T>) {
childCoordinators[coordinator.identifier] = nil
}
@discardableResult
public func coordinate<T>(to coordinator: BaseCoordinator<T>) -> Observable<T> {
store(coordinator: coordinator)
return coordinator.start()
.do(onNext: { [weak self] _ in
if let `self` = self {
self.free(coordinator: coordinator)
}
})
}
}
public class BaseCoordinator<ResultType>: NSObject, UINavigationControllerDelegate, CoordinatorType {
/// Typealias which will allows to access a ResultType of the Coordainator by `CoordinatorName.CoordinationResult`.
typealias CoordinationResult = ResultType
public var navigationController: RTRootNavigationController!
func start() -> Observable<ResultType> {
fatalError("Coordinator Start method should be implemented by subclass.")
}
func noResultStart() {
fatalError("Coordinator noResultStart method should be implemented by subclass.")
}
/// UINavigationControllerDelegate
public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
// ensure the view controller is popping
if let transitionCoordinator = navigationController.transitionCoordinator,
let fromViewController = transitionCoordinator.viewController(forKey: .from),
!navigationController.viewControllers.contains(fromViewController) {
fromViewController.popedAction.onNext(())
fromViewController.popedAction.onCompleted()
}
}
let disposeBag = DisposeBag()
public let identifier = UUID()
public var childCoordinators = [UUID: Any]()
}
复制代码