This repository contains a library implementing the Coordinator pattern, which is a design pattern used in iOS app development to manage app navigation flows. The library provides a set of classes and protocols that can be used to implement the Coordinator pattern in an iOS app. It works either UIKit or SwiftUI apps
Its core navigation has created with UINavigationController (UIKit) with the aim to get profit about navigation stack.
To use the Coordinator pattern library in your iOS project, you'll need to add the library files to your project and set up a Coordinator object. Here are the basic steps:
SwiftUI
enum OnboardingRouter: NavigationRouter {
case firstStep(viewModel: FirstStepViewModel)
case secondStep(viewModel: SecondStepViewModel)
// MARK: NavigationRouter
var transition: NavigationTranisitionStyle {
switch self {
case .firstStep, secondStep:
return .push
}
}
func view() -> any View {
switch self {
case .firstStep(let vm):
return FirstStepView(viewModel: vm)
case .secondStep(let vm):
return SecondStepView().environmentObject(vm)
}
}
}
class OnboardingCoordinator: CoordinatorSUI<OnboardingRouter> {
override func start(animated: Bool) {
let vm = SecondStepViewModel(coordinator: self)
show(.firstStep(viewModel: vm))
parent.startChildCoordinator(self, animated: animated)
}
func showStep2() {
let vm = SecondStepViewModel(coordinator: self)
show(.secondStep(viewModel: vm))
}
func showLoginCoordinator() {
let coordinator = LoginCoordinator()
coordinator.start()
}
}
UIKit:
class OnboardingCoordinator: BaseCoordinator {
override func start(animated: Bool) {
let vc = FirstViewController()
root.viewControllers.append(vc)
parent.startChildCoordinator(self, animated: animated)
}
func showStep2() {
let vc = SecondViewController()
push(vc)
}
func showLoginCoordinator() {
let coordinator = LoginCoordinator()
coordinator.start()
}
}
Create a router
enum HomeRouter: CaseIterable, TabbarPage {
case marketplace
case settings
// MARK: NavigationRouter
func coordinator(parent: Coordinator) -> Coordinator {
switch self {
case .settings:
return SettingCoordinator(parent: parent)
case .marketplace:
return MarketplaceCoordinator(parent: parent)
}
}
// MARK: TabbarPageDataSource
public var title: String {
switch self {
case .marketplace:
return "Marketplace"
case .settings:
return "Settings"
}
}
public var icon: String {
switch self {
case .marketplace:
return "house"
case .settings:
return "gearshape"
}
}
public var position: Int {
switch self {
case .marketplace:
return 0
case .settings:
return 1
}
}
}
Default tabbar build with UIKIT (It also works with SwiftUI)
class HomeCoordinator: TabbarCoordinator<HomeRouter> {
public init(withParent parent: Coordinator) {
let pages: [Router] = [.marketplace, .settings]
super.init(
withParent: parent,
pages: pages
)
}
}
Custom view (SwiftUI)
class HomeCoordinator: TabbarCoordinator<HomeRouter> {
public init(withParent parent: Coordinator) {
let pages: [Router] = [.marketplace, .settings]
let view = HomeTabbarView(pages: pages)
super.init(
withParent: parent,
customView: view,
pages: pages
)
view.$currentPage
.sink { [weak self] page in
self?.currentPage = page
}.store(in: &cancelables)
}
}
class MainCoordinator: BaseCoordinator {
init() {
super.init(parent: nil)
}
override func start(animated: Bool = false) {
let coordinator = OnboardingCoordinator(withParent: self)
coordinator.start(animated: animated)
}
}
import UIKit
import ALCoordinator
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var mainCoordinator: Coordinator
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()
setupCoordinator(window: window, animated: true)
}
private func setupCoordinator(window: UIWindow?, animated: Bool = false) {
mainCoordinator = .init()
window?.rootViewController = mainCoordinator.root
mainCoordinator?.start(animated: animated)
BaseCoordinator.mainCoordinator = mainCoordinator
}
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {}
// Add this method
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let sceneDelegate = SceneDelegate()
sceneDelegate.scene(windowScene, willConnectTo: session, options: connectionOptions)
}
}
Actions you can perform from the coordinator depends on the kind of coordinator used. For instance, using a BaseCoordinator, CoordinatorSUI or Coordinator some of the functions you can perform are:
Name | Description |
---|---|
root |
variable to get navigation controller. |
start() |
Starts the navigation flow managed by the coordinator. This method should be called to begin a navigation flow. Params:
animated: Bool |
finish() |
Finishes the navigation flow managed by the coordinator. This method should be called to end a navigation flow.
Params: completion: (() -> Void)?, default nil animated: Bool, default true |
push(_:) |
Pushes a view controller onto the receiver’s stack and updates the display (only for UIKit).
Params: viewController: UIViewController animated: Bool, default true |
present(_:) |
Presents a view controller modally (only for UIKit).
Params: viewController: UIViewController animated: Bool, default true completion: (() -> Void)?, default nil |
pop(_:) |
Pops the top view controller from the navigation stack and updates the display .
Params: animated: Bool, default true |
popToRoot(_:) |
Pops all the view controllers on the stack except the root view controller and updates the display.
Params: animated: Bool, default true |
popToView(_:) |
Pops view controllers until the specified view controller is at the top of the navigation stack.
if the view is onto navigation stack returns true. e.i: popToView(MyObject.self, animated: false). Params: view: Any animated: Bool, default true |
dismiss(_:) |
Dismisses the view controller that was presented modally by the view controller.
Params: completion: (() -> Void)?, default nil animated: Bool, default true |
close(_:) |
If a view controller is presented as modal, it calls the dismiss(:) > function; otherwise, pop(:) .
Params: completion: (() -> Void)?, default nil animated: Bool, default true |
topCoordinator(_:) |
Returns the last coordinator presented |
restart(_:) |
Finish all its children and finally call start() function.
params completion: (() -> Void)?, default nil animated: Bool, default true |
startChildCoordinator(_:) |
It is a faster way to initialize a secondary coordinator. Inserting a child to its child coordinators and finally it calls present(:) function.
params coordinator: Coordinator, child coordinator animated: Bool, default true |
In addition to the functions listed above, the Coordinator-pattern library provides several classes that can be used to simplify the implementation of the Coordinator pattern in an iOS app. These classes are:
BaseCoordinator
The BaseCoordinator class provides a basic implementation of the Coordinator protocol, with default implementations. This class can be subclassed to create custom coordinator objects that implement the Coordinator protocol.
Name | Description |
---|---|
restartMainCoordinator() |
Finish all its children and finally call start() function.
Params mainCoordinator: Coordinator, default BaseCoordinator.mainCoordinator completion: (() -> Void)?, default nil animated: Bool, default true |
getTopCoordinator() |
Returns the last coordinator presented
Params mainCoordinator: Coordinator, default BaseCoordinator.mainCoordinator |
TabbarCoordinator
The TabbarCoordinator class is a specialized coordinator object that is designed to manage the navigation flow of a tab bar interface. This class provides methods for adding child coordinators for each tab in the tab bar, and for managing the selection of tabs.
Name | Description |
---|---|
getCoordinatorSelected() |
Finish all its children and finally call start() function.
params mainCoordinator: Coordinator, default BaseCoordinator.mainCoordinator completion: (() -> Void)?, default nil animated: Bool, default true |
setPages(:) |
Set pages
Params pages: [TabbarPage], List of pages |
currentPage |
Variable to get or set current page |
CoordinatorSUI
The CoordinatorSUI class is a specialized coordinator object that is designed to manage the navigation flow of a SwiftUI app. This class provides methods for showing views.
Name | Description |
---|---|
show(:) |
shows a SwiftUI view.
Params router: NavigationRouter transitionStyle: NavigationTranisitionStyle, default nil animated: Bool, default true |
TabbarPage
The typealias TabbarPage is a short way to implement protocols TabbarPageDataSource & TabbarNavigationRouter
SPM
Open Xcode and your project, click File / Swift Packages / Add package dependency... . In the textfield "Enter package repository URL", write https://github.com/felilo/ALCoordinator and press Next twice
Contributions to the ALCoordinator library are welcome! To contribute, simply fork this repository and make your changes in a new branch. When your changes are ready, submit a pull request to this repository for review.
License
The ALCoordinator library is released under the MIT license. See the LICENSE file for more information.
link |
Stars: 5 |
Last commit: 1 week ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics