Plan
Plan.framework
帮助您保持 iOS 应用程序设计整洁。把它想象成一个干净的架构。关于干净架构的更多内容请参阅这里。
概述
这个设计的目的在于清除处理流程和依赖关系。
理想的处理流程如下。
如果在 Controller
中难以接受处理,您可以准备 Adapter
而不是 Controller
,并且 Adapter
可以与 Controller
交互。但是,Adapter
应该只做必要的操作。
详细内容
Plan
提供了五种主要类型,但在许多情况下,允许响应式编程的框架是有帮助的。
交互器
Controller
的输入在此进行处理。数据 I/O 和 API 调用也在此进行。处理完成后,执行 Dispatcher
的操作。它包含在 Domain Layer
中。
enum LoginUseCaseAction {
case loading
case login(Result<User, Error>)
}
protocol LoginUseCase {
func login(userName: String, password: String)
}
class LoginInteractor: Interactor<LoginUseCaseAction>, LoginUseCase {
let disposeBag = DisposeBag()
func login(userName: String, password: String) {
dispatcher.dispatch(.loading)
userRepository.login(userName: userName, password: password)
.subscribe(onNext: { [weak self] user in
self?.dispatcher.dispatch(.login(.success(user)))
}, onError: { [weak self] error in
self?.dispatcher.dispatch(.login(.failure(error)))
})
.disposed(by: disposeBag)
}
}
分发器
通过交互器
传递输出到转换器
。
存储器
存储视图
的状态。状态由转换器
修改。它包含在表示层
中。
class LoginStore: Store {
let viewModel = BehaviorRelay(value: LoginViewModel())
}
转换器
将来自交互器
的数据转换为用于配置视图
的数据,并更新存储器
的状态。它包含在表示层
中。
struct LoginTranslator: Translator {
func translate(action: LoginUseCaseAction, store: LoginStore) {
switch action {
case .loading:
store.viewModel.modefy { viewModel in
viewModel.loginProgress = .loading
}
case .login(.success(let user)):
store.viewModel.modefy { viewModel in
viewModel.user = user
viewModel.loginProgress = .loadSucceeded
}
case .login(.failure(let error)):
print("login failed \(error)")
store.viewModel.modefy { viewModel in
viewModel.loginProgress = .loadFailed
}
}
}
}
呈现器
将存储的状态转换为视图
使用的最佳形式。它包含在表示层
中。
class LoginPresenter: Presenter<LoginTranslator>, LoginPresenterProtocol {
var viewModel: Observable<LoginViewModel> {
store.viewModel.asObservable()
}
}
初始化交互器
时,将呈现器
实例作为分发器
传递。
let presenter = LoginPresenter(store: LoginStore(), translator: LoginTranslator())
let interactor = LoginInteractor(dispatcher: presenter.asDispatcher())
通常,可以分发到呈现器
的动作
仅限于在转换器
中定义的,但如果存在多个用例
,通过覆盖asDispatcher()
方法,可以将其分发到多个交互器
。
class LoginPresenter: Presenter<LoginTranslator>, LoginPresenterProtocol {
var viewModel: Observable<LoginViewModel> {
store.viewModel.asObservable()
}
func asDispatcher() -> AnyDispatcher<UserInfoUseCaseAction> {
AnyDispatcher(self)
.map { (action: UserInfoUseCaseAction) in
// Convert UserInfoUseCaseAction to LoginTranslator.Action
}
}
}
let presenter = LoginPresenter(store: LoginStore(), translator: LoginTranslator())
// Can dispatch to one Presenter from multiple Interactors.
let loginInteractor = LoginInteractor(dispatcher: presenter.asDispatcher())
let userInfoInteractor = UserInfoInteractor(dispatcher: presenter.asDispatcher())
更多详细信息
见示例。
需求
- Swift 5.0
- iOS 10.0 或更高版本
- macOS 10.12 或更高版本
- tvOS 10.0 或更高版本
- watchOS 3.0 或更高版本
安装
CocoaPods
将以下内容添加到您的 Podfile
pod "Plan"
Carthage
将以下内容添加到您的 Cartfile
github "KyoheiG3/Plan"
致谢
我一直使用VueFlux在架构中,但我创建了一个Plan
,试图使其更加简单,以便模块测试。
许可证
遵循MIT许可证。详情请见LICENSE文件。