计划 0.2.1

Plan 0.2.1

Kyohei Ito 维护。



Plan 0.2.1

  • Kyohei Ito

Plan

Build Carthage compatible codecov Version Platform License

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文件。