ekazaev / route-composer

Protocol oriented, Cocoa UI abstractions based library that helps to handle view controllers composition, navigation and deep linking tasks in the iOS application. Can be used as the universal replacement for the Coordinator pattern.
MIT License
896 stars 63 forks source link

Best way to handle if/else logic when a route depends on two different prior View Controllers #93

Closed bennnjamin closed 1 year ago

bennnjamin commented 1 year ago

I have a DestinationStep that requires coming from two different places. What's the best way to handle this? Here is some example code for how I thought I could accomplish it:


   let studyTabBarStep = StepAssembly(
        finder: NilFinder<StudyTabBarController, Any?>(),
        factory: StudyTabBarFactory())
        .using(UINavigationController.pushAsRoot())
        .from(GeneralStep.custom(using: ClassFinder<StudyListController, Any?>()).expectingContainer())
        .assemble()
    func showStudy(id: Int) {
        if let _ = ClassFinder<StudyListController, Any?>().getViewController() {
            router.commitNavigation(to: Destination(to: studyTabBarStep, with: StudyContext(id: id), animated: true, completion: nil)
        } else {
            let step = StepAssembly(
                finder: NilFinder<StudyTabBarController, Any?>(),
                factory: StudyTabBarFactory())
                .using(UINavigationController.pushAsRoot())
                .from(NavigationControllerStep<UINavigationController, Any?>())
                .using(GeneralAction.replaceRoot())
                .from(GeneralStep.root())
                .assemble()

            router.commitNavigation(to: Destination(to: step, with: StudyContext(id: id), animated: true, completion: nil)
        }
    }

I have a Study list controller, which is the root view controller. When you select a study from the list, it will replace the root view controller with the StudyTabBar. But if you come from a deeplink or notification, the StudyListController might not be on the screen (perhaps the user is on a StudyTabBar screen for a different ID). I need to replace the root view controller with this new StudyTabBar.

I use a Finder to check if the StudyListController exists first, and if it does I can use the Step to replace the view controller with the study. If the view controller doesn't exist, then I create a different step which builds the navigation controller and replaces root with the study tab bar.

Is this a good way to do it or is there a better way?

ekazaev commented 1 year ago

@bennnjamin Hi. Sorry for the late reply. Yes, it is absolutely fine. Finder answers you a question what you should do depending on the current step of the app. You can also use SwitchAssembly if needed. There should be a few examples in the Example app to be able to reuse the pieces of the configuration if needed. But logic will be more or less the same. Of course you can bring some kinda state variable to your app and istead of checking if some view controller is present or not - use that variable and choose the correct configuration to use. But usually just knowing that some view controller is present or not is enough.

ekazaev commented 1 year ago

Something like:

    let screen = StepAssembly(
            finder: ClassFinder<ProductViewController, Any?>(),
            factory: ClassFactory())
            .using(UINavigationController.push())
            .from(SwitchAssembly<UINavigationController, Any?>()
                    .addCase(expecting: ClassFinder<UINavigationController, Any?>(options: .visible)) // If found - just push into it
                    .assemble(default: { // else
                        return ChainAssembly()
                                .from(SingleContainerStep(finder: NilFinder(), factory: NavigationControllerFactory()))
                                .using(GeneralAction.presentModally())
                                .from(GeneralStep.current())
                                .assemble()
                    })
            ).assemble()

from https://ekazaev.github.io/route-composer/examples.html

ekazaev commented 1 year ago

But again, there is nothing wrong with the approach you are using.

bennnjamin commented 1 year ago

Thanks for your help, I was looking for a way to do this with SwitchAssembly but it's good to know my approach is fine as well.