forXifLess / LinkNavigator

🌊 Easy & Powerful navigation library in SwiftUI
Other
382 stars 27 forks source link

Question about TabLinkNavigator? #34

Closed ngocpd-1250 closed 5 months ago

ngocpd-1250 commented 5 months ago

I would like to know how to display alerts or perform navigation tasks similar to SingleLinkNavigator with TabLinkNavigator in the MainApp file. Could you please guide me on how to achieve this? Thank you.

example code:

private let tabLinkNavigator = TabLinkNavigator(
        routeBuilderItemList: HomeRouteGroup().routers,
        dependency: NavigatorDependency()) // use for homeRoute

private let singleLinkNavigator = SingleLinkNavigator(
        routeBuilderItemList: AuthRouteGroup().routers,
        dependency: NavigatorDependency()) // use for authRoute

var body: some Scene {
    WindowGroup {
        if isLoggedIn {
            homeRoute()
                .onOpenURL { url in
                    let action = ActionButton(title: "OK", style: .cancel)
                    let alert = Alert(title: "Deeplink URL:", message: url.absoluteString, buttons: [action], flagType: .error)
                    // i want this here
                    tabLinkNavigator.alert(target: .root, model: alert) (or tabLinkNavigator.rootNavigator.alert(target: .root, model: alert)..) 
                }
        } else {
            authRoute()
                .onOpenURL { url in
                    let action = ActionButton(title: "OK", style: .cancel)
                    let alert = Alert(title: "Deeplink URL:", message: url.absoluteString, buttons: [action], flagType: .error)
                    singleLinkNavigator.alert(target: .root, model: alert) 
                }
        }
    }
}
interactord commented 5 months ago

@ngocpd-1250 Do you want to use the same function as DeepLink in TabLinkNavigator? I will organize the example code and share it with you on May 7th.

Please wait a little longer.

Thank you.

97chos commented 5 months ago

Hello, @ngocpd-1250 ! Thank you for incorporating our LinkNavigator library into your project.

Previously, the TabLinkNavigator did not have an alert implementation, and it was only accessible by utilizing the RootNavigatorType inherited by the child navigator, TabPartialLinkNavigator. In other words, to display an alert, one had to access and call the alert method at the Page level of each sub-feature.

We agreed that this original approach made it cumbersome to control alerts, especially when using deep links like you do. As a result, we have modified the TabLinkNavigator to allow calling the alert method directly from it.

We have updated these changes and released them in version 1.2.4. Please check this update and let us know if it resolves your issue.

homeRoute()
  .onOpenURL { url in
    let action = ActionButton(title: "OK", style: .cancel)
    let alert = Alert(title: "Deeplink URL:", message: url.absoluteString, buttons: [action], flagType: .error)
// add
    tabLinkNavigator.alert(model: alert)  
 }

Thank you for reporting the issue! :)

ngocpd-1250 commented 5 months ago

@97chos Thank you for your quick response!

I want to use more functions provided by the TabLinkNavigatorProtocol such as .next(linkItem:...), .back(isAnimated: Bool), etc. So, could you create a public variable that returns the current instance of TabPartialNavigator based on the selected tab of the Tabbar?

Like this code:

in TabLinkNavigator.swift:

// MARK: Public

 public var currentTabPartialNavigator: TabPartialNavigator {
       return tempPartialNavigators[mainController?.selectedIndex ?? 0]
 }

 // MARK: Internal

 var tempPartialNavigators: [TabPartialNavigator] = []

 extension TabLinkNavigator {
    public func launch(tagItemList: [TabItem]) -> [UINavigationController] {
          let tabPartialNavigators = tagItemList
            .reduce([(Bool, TabPartialNavigator)]()) { curr, next in
                let newNavigatorList = TabPartialNavigator(
                    rootNavigator: self,
                    tabItem: next,
                    routeBuilderItemList: routeBuilderItemList,
                    dependency: dependency)
                return curr + [(next.prefersLargeTitles, newNavigatorList)]
            }

        tempPartialNavigators = tabPartialNavigators.map { $0.1 }

        .....
    }
 }

in TabEventSubscriberApp.swift:

.onOpenURL { url in
      tabLinkNavigator. currentTabPartialNavigator.next(linkItem: ...)
      or
      tabLinkNavigator. currentTabPartialNavigator.alert(model: ...)
      ...etc
}
97chos commented 5 months ago

Dear @ngocpd-1250 ,

In the design, navigation actions such as push or back for each tab are managed by a TabPartialNavigator, which is specific to each tab, rather than by a TabLinkNavigator.

This is to ensure that each tab has its own UINavigationViewController, and the TabPartialNavigator, responsible for managing it, has separate responsibilities for navigation actions.

The TabLinkNavigator is independently designed to only handle tasks that affect the overall UI of the screen, such as sheets, alert, or moving tab.

This also applies when navigation actions are performed using the currently selected TabPartialNavigator through the TabLinkNavigator.

To perform the requested actions, you can use the send method via an EventSubscriber.

For example code regarding the EventSubscriber and send method, please refer to the example source for LinkNavigator's TabEventSubscriber!

ngocpd-1250 commented 5 months ago

@97chos

In the design, navigation actions such as push or back for each tab are managed by a TabPartialNavigator, which is specific to each tab, rather than by a TabLinkNavigator.

This is to ensure that each tab has its own UINavigationViewController, and the TabPartialNavigator, responsible for managing it, has separate responsibilities for navigation actions.

I completely agree with you, so I only want to perform navigation actions through TabPartialNavigator, but currently I don't have any way to access to obtain the desired TabPartialNavigator via TabLinkNavigator.

If you have any way to access the list of TabPartialNavigator or the TabPartialNavigator of the currently selected tab, please let me know. Thank you!

  tabLinkNavigator.currentTabPartialNavigator.alert(model: ...)
97chos commented 5 months ago

Dear @ngocpd-1250 ,

As shown in the example code provided above, in the latest version (1.2.4), alerts can be triggered through the TabLinkNavigator using the alert(model:) method.

tabLinkNavigator.alert(model: alert)

Do you have any other reasons for accessing the currentTabPartialNavigator besides navigation actions?

ngocpd-1250 commented 5 months ago

@97chos Yes, I want to navigate to another screen when the user clicks on a deeplink with currentTabPartialNavigator.

https://github.com/forXifLess/LinkNavigator/assets/49447154/769aa0cb-c7d5-438c-a155-f3a5c5be39d4

97chos commented 5 months ago

Dear @ngocpd-1250 ,

I have recognized the issue.

Currently, the TabLinkNavigator lacks a method to directly send actions to the tabs. We will discuss this internally and find the best solution to improve and revise this.

In the meantime, a temporary method involves using internal storage like UserDefaults, or @Environment to save values that come in through onOpenURL. Then, during the onAppear method of the Page you moved to via moveTab, you can retrieve these saved values to perform a push.

We recommend implementing DeepLink in this manner until internal improvements are made.

We will make the necessary adjustments and update you promptly.

Thank you for identifying this important point for improvement! 🙏

97chos commented 5 months ago

Dear @ngocpd-1250 ,

First of all, thank you for reporting the issue.

We've had extensive discussions regarding the mentioned issue.

While adhering to the principle of each Navigator's independent responsibility as mentioned above, we've considered various ways to deliver events to TabPartialNavigator. Ultimately, we've decided to provide a method in TabLinkNavigator that returns TabPartialNavigator, as mentioned by @ngocpd-1250.

Initially, during the design phase, we prioritized restricting access to TabPartialNavigator from TabLinkNavigator to ensure the independence of responsibilities. However, this raised concerns about usability degradation for deep linking support and potential hindrance to scalability in the future.

Therefore, we approached the issue differently, by allowing users to receive the TabPartialNavigator they want to control. This way, they can receive the desired Tab (or Navigator) from TabLinkNavigator and perform navigation actions.

Furthermore, while addressing this issue, we've also resolved several issues related to deep linking.

Below is an example of the changes made:

var body: some Scene {
    WindowGroup {
      TabLinkNavigationView(
        linkNavigator: tabLinkNavigator,
        isHiddenDefaultTabbar: false,
        tabItemList: tabList)
      .onOpenURL { url in
        /// You can test deep links by setting the URL Scheme to "tab-e-s".
        /// Example: 
        /// tab-e-s://navigation/tab2/step1/step2?message=opened+by+deep+link

        guard let tabPath = getTabPathByDeeplink(url: url),
              let linkItem = getLinkItemByDeepLink(url: url),
              let targetTab = tabLinkNavigator.targetPartialNavigator(tabPath: tabPath) else { return }

        tabLinkNavigator.moveTab(targetPath: tabPath)
        targetTab.replace(linkItem: linkItem, isAnimated: true)
      }
    }
  }

The entire code can be found in the 03-TabEventSubscriber example.

The patched version has been deployed as version 1.2.5, and if everything checks out, please close the issue. Feel free to raise any additional issues if they arise.

Thank you for raising important issues and providing valuable insights!

https://github.com/forXifLess/LinkNavigator/assets/59811450/80b426c1-72ad-4e62-932d-d7cedd893482

ngocpd-1250 commented 5 months ago

Thank you very much, my issue has been resolved.