Swift — How to handle multiple coordinators and avoid memory leaks

Ignacio Molina Portoles
3 min readMar 27, 2024

Episode 4 of developing my own app — Solboard

So, in this post, we got 3 main things to cover:

  • How to implement the TabBar.
  • How to switch root coordinator.
  • How to deallocate the Login flow viewControllers.

So before to start, this is the current flow of Solboard, when you enter the address to import the wallet, you switch to the home flow.

Now you have some context, lets dive into the 3 topics.

Implementing the TabBar

Basically, you create the coordinator with the tabIcon for each tab and assign to the viewControllers of the UITarBarController.

Utils: Figma with SF Icons

class TabbarController: UITabBarController {
let home = HomeCoordinator(navigationController: UINavigationController())

override func viewDidLoad() {
super.viewDidLoad()

home.start()
viewControllers = [home.navigationController]
}
}

class HomeCoordinator: Coordinator {
var childCoordinators = [Coordinator]()

var navigationController: UINavigationController

init(navigationController: UINavigationController) {
self.navigationController = navigationController
}

func start() {
let vc = HomeViewController()
vc.tabBarItem = UITabBarItem(title: "Home",
image: UIImage(systemName: "house.fill"),
tag: 0)
navigationController.pushViewController(vc, animated: false)
}
}

Switching root coordinator and deallocating viewControllers

Disclaimer: This implementation works only if you dont support multiples scenes.

For this, we’ll use a extension to access the SceneDelegate and a setRootViewController helper.

So the extension look like this:

extension UIApplication {
static var sceneDelegate: SceneDelegate? {
shared.connectedScenes.first?.delegate as? SceneDelegate
}
}

the helper like this:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?
var currentScene: UIWindowScene?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let uiWindowScene = (scene as? UIWindowScene) else { return }
currentScene = uiWindowScene

let navigationController = UINavigationController()

let mainCoordinator = LoginCoordinator(navigationController: navigationController)
mainCoordinator.start()

setRootViewController(navigationController)
}

//Mark: Helpers
func setRootViewController(_ viewController: UIViewController) {
guard let scene = currentScene else { return }

window = UIWindow(windowScene: scene)
window?.rootViewController = viewController
window?.makeKeyAndVisible()
}

//Rest of SceneDelegate...

And lastly, to route to the Home flow, we:

  1. Remove all the unused viewControllers form the current coordinator
  2. Create the TabBarController
  3. Get the SceneDelegate and assign the new root
class LoginCoordinator: Coordinator {
var childCoordinators = [Coordinator]()
var navigationController: UINavigationController

init(navigationController: UINavigationController) {
self.navigationController = navigationController
}

func start() {
// Start code...
}

func buildHome() {
// Step 1
navigationController.viewControllers.removeAll()

// Step 2
let vc = TabbarController()

// Step 3
guard let sceneDelegate = UIApplication.sceneDelegate else { return }
sceneDelegate.setRootViewController(vc)
}
}

If you have any improvement or doubt, please let me know, im always open to help and learn new things! 😁

LinkedIn

Solboard source code

--

--

Ignacio Molina Portoles
0 Followers

I like to break things apart to know how they are made. iOS Developer