SceneCoordinator Transition
serjooo opened this issue · comments
First of all I would like to thank you for this amazing project. Truly your project has become an educational resource for me!
My question is about the SceneCoordinator
. I implemented the Coordinator with the same logic without RX, because I don't have the enough confidence to start using RX in projects for production. I have only one missing link to intercept when the View Controllers pop
or a tab bar item is changed you make the SceneCoordinator
the navigationControllerDelegate
and tabBarControllerDelegate
. I am wondering what the difference between this:
currentViewController.navigationController?.rx.delegate.setForwardToDelegate(self, retainDelegate: false)
currentViewController.tabBarController?.rx.delegate.setForwardToDelegate(self, retainDelegate: false)
And this code is:
currentViewController.navigationController?.delegate = self
currentViewController.tabBarController?.delegate = self
I'm guessing RX has a fancy way of preventing memory leaks in this case. How can I make my own implementation of setForwardToDelegate
? Again thank you very much!
Thanks for raising this as an issue 😊 @serjooo
Actually you don't need to implement setForwardToDelegate
by your own. It is not necessary.
The following non-Rx implantation of SceneCoordiantor
should work.
class SceneCoordinator: NSObject, SceneCoordinatorType {
static var shared: SceneCoordinator!
fileprivate var window: UIWindow
fileprivate var currentViewController: UIViewController {
didSet {
currentViewController.tabBarController?.delegate = self
}
}
required init(window: UIWindow) {
self.window = window
currentViewController = window.rootViewController!
}
static func actualViewController(for viewController: UIViewController) -> UIViewController {
if let navigationController = viewController as? UINavigationController {
return navigationController.viewControllers.first!
}
return viewController
}
func transition(to scene: TargetScene) {
switch scene.transition {
case let .tabBar(tabBarController):
guard let selectedViewController = tabBarController.selectedViewController else {
fatalError("Selected view controller doesn't exists")
}
currentViewController = SceneCoordinator.actualViewController(for: selectedViewController)
window.rootViewController = tabBarController
case let .root(viewController):
currentViewController = SceneCoordinator.actualViewController(for: viewController)
window.rootViewController = viewController
case let .push(viewController):
guard let navigationController = currentViewController.navigationController else {
fatalError("Can't push a view controller without a current navigation controller")
}
navigationController.pushViewController(SceneCoordinator.actualViewController(for: viewController), animated: true)
case let .present(viewController):
currentViewController.present(viewController, animated: true)
currentViewController = SceneCoordinator.actualViewController(for: viewController)
case let .alert(viewController):
currentViewController.present(viewController, animated: true)
}
}
func pop(animated: Bool) {
if let presentingViewController = currentViewController.presentingViewController {
currentViewController.dismiss(animated: animated) {
self.currentViewController = SceneCoordinator.actualViewController(for: presentingViewController)
}
} else if let navigationController = currentViewController.navigationController {
guard navigationController.popViewController(animated: animated) != nil else {
fatalError("can't navigate back from \(currentViewController)")
}
currentViewController = SceneCoordinator.actualViewController(for: navigationController.viewControllers.last!)
} else {
fatalError("Not a modal, no navigation controller: can't navigate back from \(currentViewController)")
}
}
}
// MARK: - UITabBarControllerDelegate
extension SceneCoordinator: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
currentViewController = SceneCoordinator.actualViewController(for: viewController)
}
}
I am closing this issue for now. However, feel free to reopen it if it's necessary.
@jdisho That's exactly what I have! I really love that you thought about the coordinator pattern as a Universal Coordinator instead of really having to write a coordinator for each Controller. I really did not see the benefit of that. This solution is really clear, does the job, and removes a lot of boilerplate code! Good job! Again thank you!
@jdisho I might have found a bug and here is the fix for it. While Presenting Modally
from a selected UITabBarController
then dismissing the Controller
, the actualViewController
doesn't return the UITabBarController selectedViewController
as the actualViewController
. It instead returns the UITabBarController
, so trying to Push
another Controller
while staying on the same Tab
would crash because the fatalError.
I did this to fix this bug:
static func actualViewController(for viewController: UIViewController) -> UIViewController {
var controller = viewController
if let tabBarController = controller as? UITabBarController {
guard let selectedViewController = tabBarController.selectedViewController else {
return tabBarController
}
controller = selectedViewController
}
if let navigationController = controller as? UINavigationController {
return navigationController.viewControllers.first!
}
return viewController
}
@serjooo Nice, thank you 👍
Can you create a PR regarding this fix?
@jdisho will do!