swift - RxSwift 主题在调用时不触发事件

标签 swift rx-swift coordinator-pattern publishsubject

我有一个应用程序使用 MVPCoordinator图案。

当子协调器发送事件时,我希望我的 AppCoordinator递归调用一个方法,根据一些 SessionState 选择下一个协调器.

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

  • start()调用 coordinateToRoot具有初始状态
  • 订阅 showStartScene()启动子协调器
  • StartCoordinator
  • start()创建 MVP模块现在对用户可见
  • MVP模块调用 AuthSvc它对 iDP 进行异步调用并确认身份验证状态
  • 完成此任务后,发布由 AppCoordinator 中的订阅获取的事件。的 coordinateToRoot方法和循环使用适当的 View 状态的协调器重复。

  • 然而,问题是在该事件发布时,什么也没有发生。 start()没有显示它收到了事件和 coordinateToRoot不再被调用。

    我在下面创建了最基本的版本来演示这一点。我也硬编码了 showStartScene返回 .signedIn而不是查找身份验证状态。

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

    session 状态

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

    应用协调员

    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 }
        }
    }
    

    启动协调器

    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
        }
    }
    

    启动 MVP 模块

    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运算符(operator),我可以看到下一个事件和订阅的订单显示关闭
    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订阅后StartPresenter被 build ?

    最佳答案

    coordinateToRootAppCoordinator.start(_:) 返回的 Observable 的生命周期无关.这意味着无法保证 coordinateToRoot 的顺序。和 StartPresenter已订阅。
    为了保证顺序,我觉得你可以用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)
            })
        }
    }
    
    

    关于swift - RxSwift 主题在调用时不触发事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58764341/

    相关文章:

    ios - 如何手动触发CLLocationManager更新?

    swift - 返回 Observable 不起作用

    swift - RxSwift 异步请求设计

    swift - Swift 中的条件导入

    ios - 是否可以通过 watch 连接将复杂的数组从 iPhone 发送到 Apple Watch?

    ios - 如果 self 是 SKSpriteNode 导致崩溃,则不提供与崩溃相关的详细信息

    swift - RxSwift 取消 RxMoya 网络请求

    ios - PresentingViewController 如何收到其 PresentedViewController 自行关闭的通知?

    ios - 使用协调器模式时奇怪的保留周期