调用时RxSwift主题未触发事件

时间:2019-11-08 10:02:38

标签: swift rx-swift coordinator-pattern publishsubject

我有一个使用MVPCoordinator模式的应用。

当子协调员发送事件时,我希望AppCoordinator递归调用一个方法,该方法基于某个SessionState选择下一个协调器。

应用程序的基本流程如下-

AppCoordinator

  1. start()以初始状态调用coordinateToRoot
  2. 订阅showStartScene(),这将启动子协调器

StartCoordinator

  1. start()创建MVP模块,该模块现在对用户可见
  2. MVP模块调用AuthSvc,该模块对iDP进行异步调用并确认身份验证状态
  3. 完成此任务后,将发布一个事件,该事件由AppCoordinator的{​​{1}}方法中的订阅获取,然后使用适当的视图状态协调器重复该循环。

但是,问题在于该事件的发布没有任何反应。 coordinateToRoot未显示它已接收到事件,并且start()也未再次调用。

我已经创建了最基本的版本,可以在下面进行演示。我还对coordinateToRoot进行了硬编码以返回showStartScene,而不是查找auth状态。

在下面的示例中,我希望一旦加载视图,.signedIn应该立即发出一个事件,该事件导致显示一条打印语句。

SessionState

presenter.signal

AppCoordinator

enum SessionState: String {
    case unknown, signedIn, signedOut
}

StartCoordinator

final class AppCoordinator: BaseCoordinator<Void> {

    private let window: UIWindow

    init(window: UIWindow) {
        self.window = window
    }

    override func start() -> Observable<Void> {
        coordinateToRoot(basedOn: .unknown)
        return .never()
    }

    /// Recursive method that will restart a child coordinator after completion.
    /// Based on:
    /// https://github.com/uptechteam/Coordinator-MVVM-Rx-Example/issues/3
    private func coordinateToRoot(basedOn state: SessionState) {

        switch state {
        case .unknown:
            return showStartScene()
                .subscribe(onNext: { [unowned self] state in
                    self.window.rootViewController = nil
                    self.coordinateToRoot(basedOn: state)
                })
                .disposed(by: disposeBag)

        case .signedIn:
            print("I am signed in")

        case .signedOut:
            print("I am signed out")
        }
    }

    private func showStartScene() -> Observable<SessionState> {
        let coordinator = StartCoordinator(window: window)
        return coordinate(to: coordinator).map { return .signedIn }
    }
}

启动MVP模块

final class StartCoordinator: BaseCoordinator<Void> {

    private(set) var window: UIWindow

    init(window: UIWindow) {
        self.window = window
    }

    override func start() -> Observable<CoordinationResult> {

        let viewController = StartViewController()
        let presenter = StartPresenter(view: viewController)

        viewController.configurePresenter(as: presenter)

        window.rootViewController = viewController
        window.makeKeyAndVisible()

        return presenter.signal
    }
}

有趣的是,如果我在protocol StartViewInterface: class { func configurePresenter(as presenter: StartPresentation) } protocol StartPresentation: class { var viewIsReady: PublishSubject<Void> { get } var signal: PublishSubject<Void> { get } } // MARK:- StartPresenter final class StartPresenter { // Input let viewIsReady = PublishSubject<Void>() // Output let signal = PublishSubject<Void>() weak private var view: StartViewInterface? private lazy var disposeBag = DisposeBag() init(view: StartViewInterface?) { self.view = view viewIsReady.bind(to: signal).disposed(by: disposeBag) } } extension StartPresenter: StartPresentation { } // MARK:- StartViewController final class StartViewController: UIViewController { private var presenter: StartPresentation? override func viewDidLoad() { super.viewDidLoad() if let presenter = presenter { presenter.viewIsReady.onNext(()) } } } extension StartViewController: StartViewInterface { func configurePresenter(as presenter: StartPresentation) { self.presenter = presenter } } 中做类似的事情,该过程确实起作用了,但是它不是我想要达到的目标。

StartCoordinator

供参考,我的 override func start() -> Observable<CoordinationResult> { let viewController = StartViewController() let presenter = StartPresenter(view: viewController) viewController.configurePresenter(as: presenter) window.rootViewController = viewController window.makeKeyAndVisible() let subject = PublishSubject<Void>() DispatchQueue.main.asyncAfter(deadline: .now() + 2) { subject.onNext(()) } return subject } 如下-

BaseCoordinator

编辑 我添加了一些/// Base abstract coordinator generic over the return type of the `start` method. class BaseCoordinator<ResultType>: CoordinatorType { /// Typealias which allows to access a ResultType of the Coordainator by `CoordinatorName.CoordinationResult`. typealias CoordinationResult = ResultType /// Utility `DisposeBag` used by the subclasses. let disposeBag = DisposeBag() /// Unique identifier. internal let identifier = UUID() /// 1. Stores coordinator in a dictionary of child coordinators. /// 2. Calls method `start()` on that coordinator. /// 3. On the `onNext:` of returning observable of method `start()` removes coordinator from the dictionary. /// /// - Parameter coordinator: Coordinator to start. /// - Returns: Result of `start()` method. func coordinate<T: CoordinatorType, U>(to coordinator: T) -> Observable<U> where U == T.CoordinationResult { store(coordinator: coordinator) return coordinator.start() .do(onNext: { [weak self] _ in self?.free(coordinator: coordinator) }) } /// Starts job of the coordinator. /// /// - Returns: Result of coordinator job. func start() -> Observable<ResultType> { fatalError(message: "Start method should be implemented.") } /// Dictionary of the child coordinators. Every child coordinator should be added /// to that dictionary in order to keep it in memory. /// Key is an `identifier` of the child coordinator and value is the coordinator itself. /// Value type is `Any` because Swift doesn't allow to store generic types in the array. private(set) var childCoordinators: [UUID: Any] = [:] /// Stores coordinator to the `childCoordinators` dictionary. /// /// - Parameter coordinator: Child coordinator to store. private func store<T: CoordinatorType>(coordinator: T) { childCoordinators[coordinator.identifier] = coordinator } /// Release coordinator from the `childCoordinators` dictionary. /// /// - Parameter coordinator: Coordinator to release. private func free<T: CoordinatorType>(coordinator: T) { childCoordinators[coordinator.identifier] = nil } } 运算符,我可以看到下一个事件和订阅的订单显示了

debug

创建2019-11-08 10:26:19.289: StartPresenter -> subscribed 2019-11-08 10:26:19.340: StartPresenter -> Event next(()) 2019-11-08 10:26:19.350: coordinateToRoot -> subscribed 后为何coordinateToRoot进行订阅?

1 个答案:

答案 0 :(得分:0)

coordinateToRootAppCoordinator.start(_:) 返回的 Observable 的生命周期无关。这意味着无法保证订阅 coordinateToRootStartPresenter 的顺序。

为了保证顺序,我认为您可以使用 do 运算符并为 onSubscribe 参数传递闭包。此 onSubscribe 闭包将在订阅底层 observable 之前运行。

这是我认为你可以做出的改变:

final class AppCoordinator: BaseCoordinator<Void> {

    override func start() -> Observable<Void> {
        return Observable<Void>.never().do(onSubscribe: { [weak self] _ in
             self?.coordinateToRoot(basedOn: .unknown)
        })
    }
}