Skip to content

Instantly share code, notes, and snippets.

@AliSoftware
Last active July 10, 2022 14:32
Show Gist options
  • Save AliSoftware/6d2d146f7baccb0099cc to your computer and use it in GitHub Desktop.
Save AliSoftware/6d2d146f7baccb0099cc to your computer and use it in GitHub Desktop.
Coordinators & StateMachine - Concept
struct Coordinator {
let window: UIWindow
let navCtrl: UINavigationController?
func start() {
presentWelcomeScreen()
}
private func presentWelcomeScreen() {
let vc = WelcomeScreenViewController() // Instanciate from code, XIB, Storyboard, whatever your jam is
vc.navTransitions = WelcomeScreenTransitions(
showHome: self.pushHomeScreen, // see private func below
showCreateAccountScreen: self.pushCreateAccountScreen
)
window.rootViewController = vc // or whatever
}
private func pushHomeScreen(user: User?) {
let vc = HomeScreenViewController() // Instanciate from code, XIB, Storyboard, whatever your jam is
vc.user = user // or inject a ViewModel here if you're doing MVVM, you get the idea
vc.navTransitions = HomeScreenTransitions(
showUserInfoScreen: {
// From Home, we *push* the UserInfoVC
self.navigationController.pushViewController(instanciateUserInfoScreen(user), animated: true, completion: nil)
}
showProductDetail: self.pushProductDetail // extracted in private function below
disconnectAndShowLoginScreen: self.presentLoginScreen // same
)
navCtrl = UINavigationController(rootViewController: vc)
window.rootViewController = navCtrl
}
private func pushProductDetail(product: Product) {
let vc = ProductDetailViewController() // Instanciate from code, XIB, Storyboard, whatever your jam is
vc.product = product // inject any object to the VC, like a ViewModel or whatever
vc.navTransitions = ProductDetailTransitions(
showUserInfoScreen: {
// But from ProductDetail VC, we *present* the UserInfoVC *modally*
self.presentViewController(instanciateUserInfoScreen(product.owner), animated: true, completion: nil)
}
back: { navCtrl?.popViewController(animated: true) }
)
navCtrl?.pushViewController(vc, animated: true) { _ in }
}
private func pushCreateAccountScreen() {
// Create and push a CreateAccountViewController and its CreateAccountTransitions struct
...
}
private func instanciateUserInfoScreen(user: User) -> UIViewController {
// Create, configure and return an UserDetailsViewController
...
}
}
struct HomeScreenTransitions {
let showUserInfoScreen: Void -> Void
let showProductDetail: Product -> Void
let disconnectAndShowLoginScreen: Void -> Void
}
class HomeScreenViewController: UIViewController {
var navTransitions: HomeScreenTransitions!
@IBAction func profileAction(_: AnyObject) {
navTransitions.showUserInfoScreen()
}
@IBAction func disconnectAction(_: AnyObject) {
confirm("Are you sure?") {
navTransitions.disconnectAndShowLoginScreen()
}
}
@objc func tableView(tableView: UITableView, didSelectRowAtIndexPath: NSIndexPath) {
let product = productsDataSource[indexPath.row]
navTransitions.showProductDetail(product)
}
}
struct Product {
let id: Int
let owner: User
...
}
struct ProductDetailTransitions {
let showUserInfoScreen: Void -> Void
let back: Void -> Void
}
class ProductDetailViewController: UIViewController {
var navTransitions: ProductDetailTransitions!
@IBAction func showProductOwner(_: AnyObject) {
navTransitions.showUserInfoScreen()
}
...
}
struct WelcomeScreenTransitions {
let showHome: User? -> Void
let showCreateAccountScreen: Void -> Void
}
class WelcomeScreenViewController: UIViewController {
var navTransitions: WelcomeScreenTransitions!
var webService: WebServiceAPI!
@IBAction func loginAction(_: AnyObject) {
webService.login(userTextField.text, passwordTextField.text) { (user: User?) in
navTransitions.showHome(user)
}
}
@IBAction func createAccountAction(_: AnyObject) {
navTransitions.showCreateAccountScreen()
}
}
@AliSoftware
Copy link
Author

Ok there definitely are issues with retain cycles, and a struct/value type containing reference types doesn't help making things clear on this front.

The right solution might be to make Coordinator a class instead of a struct (which actually makes sense, as we don't really want independent copies all over), and be sure then to use [unowned self] in closures when appropriate.

@remirobert
Copy link

that's really awesome ! Good job AliSoftware 👽 .

@remirobert
Copy link

How to do you handle the cas with UITabBarController with severals UIViewController ?

  1. create and instance the UITabBarController directly in the appCoordinator.
  2. group all the transitions in the UITabBarController

@AliSoftware
Copy link
Author

@remirobert good question never had that use case until know. Since that gist using closure I've finally switched back to the delegates version of that Coordinator pattern anyway, which avoids the risk of hidden retain cycles we have with closures everywhere here

@sirvon
Copy link

sirvon commented May 31, 2017

@remirobert @AliSoftware any word or example code on using uitabcontroller with coordinator pattern?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment