RxSwiftCommunity / RxFlow

RxFlow is a navigation framework for iOS applications based on a Reactive Flow Coordinator pattern
MIT License
1.88k stars 118 forks source link

The best practice to handle nested flows #56

Closed snowtema closed 6 years ago

snowtema commented 6 years ago

My app has tab bar navigation with two tabs (Profile and Feed).

AppFlow class implement code for tabBar:

class AppFlow: Flow {
    var root: Presentable {
        return self.rootWindow
    }

    private let rootWindow: UIWindow!
    private let tabBarController: UITabBarController!

    init(with window: UIWindow) {
        self.rootWindow = window
        self.tabBarController = UITabBarController()
    }

    func navigate(to step: Step) -> NextFlowItems {
        guard let step = step as? MyStep else {
            return NextFlowItems.none
        }

        switch step {
        case .introComplete:
            return navigationToMain()
        default:
            return NextFlowItems.none
        }
    }

    private func navigationToMain() -> NextFlowItems {
        let feedFlow = FeedFlow()
        let profileFlow = ProfileFlow()
        Flows.whenReady(flow1: feedFlow, flow2: profileFlow, block: { [unowned self] (root1: UINavigationController, root2: UINavigationController) in
            let tabBarItem1 = UITabBarItem(title: "Profile", image: nil, selectedImage: nil)
            let tabBarItem2 = UITabBarItem(title: "Feed", image: nil, selectedImage: nil)
            root1.tabBarItem = tabBarItem1
            root2.tabBarItem = tabBarItem2
            self.tabBarController.setViewControllers([root1, root2], animated: false)
            self.rootWindow.rootViewController = self.tabBarController
        })

        return NextFlowItems.multiple(flowItems: [
            NextFlowItem(nextPresentable: profileFlow, nextStepper: OneStepper(withSingleStep: MyStep.profileShow)),
            NextFlowItem(nextPresentable: feedFlow, nextStepper: OneStepper(withSingleStep: MyStep.feedShowScreen))
        ])
    }
}

FeedFlow class implement navigation for feed:

class FeedFlow: Flow {
    var root: Presentable {
        return self.rootViewController
    }

    private let rootViewController = UINavigationViewController()

    func navigate(to step: Step) -> NextFlowItems {
        guard let step = step as? MyStep else {
            return NextFlowItems.none
        }

        switch step {
        case .feedPickNews(let news):
            return navigationToOpenNews(with: news)
        case .feedShowScreen:
            return navigationToShow()
        case .feedSaveNews:
            return NextFlowItems.end(withStepForParentFlow: MyStep.openProfileTab)
        default:
            return NextFlowItems.none
        }
    }

    private func navigationToShow() -> NextFlowItems {
        let feedViewController = StoryboardScene.Feed.feedViewController.instantiate()
        let feedViewModel = FeedViewModel()
        feedViewController.viewModel = feedViewModel
        self.rootViewController.pushViewController(feedViewController, animated: true)
        return NextFlowItems.one(flowItem: NextFlowItem(nextPresentable: feedViewController, nextStepper: feedViewModel))
    }

    private func navigationToOpenNews(with news: News) -> NextFlowItems {
        let viewController = StoryboardScene.Feed.feedInfoViewController.instantiate()
        let viewModel = FeedInfoViewModel(news: news)
        viewController.viewModel = viewModel
        self.rootViewController.pushViewController(viewController, animated: true)
        return NextFlowItems.one(flowItem: NextFlowItem(nextPresentable: viewController, nextStepper: viewModel))
    }
}

And I want to realize this flow:

Open app (Profile tab) –> Choose Feed tab –> Pick any news cell –> On News screen click button Save news –> ... and I want to dismiss navigation from Feed Info screen (.feedSaveNews) to root and locate Profile screen back.

First, I tried to finish (.end(...)) Feed flow, it works but further, I can't open any news in Feed. Then I tried to create one more flow for FeedInfoViewController, that I could be finished FeedInfoFlow, but I stuck with navigations problems.

Can you advise something for my case?

twittemb commented 6 years ago

Hi @snowtema

Let me think about it and i'll get back to you ASAP (today or tomorrow).

Thanks.

snowtema commented 6 years ago

@twittemb thank you

snowtema commented 6 years ago

Hi @twittemb! Do you have any ideas?

Thanks.

twittemb commented 6 years ago

Hi @snowtema

Sorry for the delay, I've been busy lately. Can you try something with the last 'develop' version ?

I think your approach with the .end(withStepForParentFlow: MyStep.openProfileTab) was the good one. But internally, RxFlow stops listening to the current flow (your FeedFlow) once it emits a .end().

I have added a new value for NextFlowItems -> .triggerParentFlow(withStep:) that allows to "communicate" with the parent Flow while continuing listening for the current Flow steppers.

The code is on develop, I quickly tested it on a simple app and It seemed to do what you want.

Please let me know if it fits your needs 👍

twittemb commented 6 years ago

Hi @snowtema

Have you tried the solution I suggested ?

snowtema commented 6 years ago

Salute @twittemb,

Sorry for late answer. Yeah, it works fine, exactly what I want! Thanks a lot for your responses and quick improvements. 👍

twittemb commented 6 years ago

Great. Thanks for using RxFlow 👍