From 9a5c385903eb9e3c5c1e8ec81d0525ab919cc8ce Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 18:52:52 +0000 Subject: [PATCH] [App] Integrated the Feed List to the App with coordinators (#16) This PR contains the work done to implement the integration of the `FeedListViewController` view controller in the `Feed` framework to the `App` target by using coordinators. Reviewed-on: https://repo.rock-n-code.com/rock-n-code/app-reviews/pulls/16 Co-authored-by: Javier Cicchelli Co-committed-by: Javier Cicchelli --- App/Sources/{ => App}/AppDelegate.swift | 16 ++++++- App/Sources/Coordinators/AppCoordinator.swift | 40 +++++++++++++++++ .../Coordinators/FeedListCoordinator.swift | 45 +++++++++++++++++++ .../BaseNavigationRouter.swift} | 8 ++-- .../{PushRouter.swift => StackRouter.swift} | 10 ++--- .../Kit/Sources/Routers/WindowRouter.swift | 4 +- Reviews.xcodeproj/project.pbxproj | 34 +++++++++++++- 7 files changed, 143 insertions(+), 14 deletions(-) rename App/Sources/{ => App}/AppDelegate.swift (58%) create mode 100644 App/Sources/Coordinators/AppCoordinator.swift create mode 100644 Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift rename Libraries/Coordination/Kit/Sources/{Routers/NavigationRouter.swift => Classes/BaseNavigationRouter.swift} (86%) rename Libraries/Coordination/Kit/Sources/Routers/{PushRouter.swift => StackRouter.swift} (85%) diff --git a/App/Sources/AppDelegate.swift b/App/Sources/App/AppDelegate.swift similarity index 58% rename from App/Sources/AppDelegate.swift rename to App/Sources/App/AppDelegate.swift index fb1338f..e0dfd38 100644 --- a/App/Sources/AppDelegate.swift +++ b/App/Sources/App/AppDelegate.swift @@ -6,6 +6,7 @@ // Copyright © 2020 ING. All rights reserved. // +import ReviewsCoordinationKit import ReviewsFeed import UIKit @@ -13,6 +14,7 @@ import UIKit class AppDelegate: UIResponder { // MARK: Properties + var coordinator: AppCoordinator? var window: UIWindow? } @@ -26,9 +28,19 @@ extension AppDelegate: UIApplicationDelegate { didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) + coordinator = .init(router: WindowRouter(window)) + + guard let coordinator else { + fatalError("AppCoordinator should have been instantiated") + } - window?.rootViewController = UINavigationController(rootViewController: FeedListViewController()) - window?.makeKeyAndVisible() + coordinator.present(animated: false) + coordinator.present( + child: FeedListCoordinator( + router: StackRouter(coordinator.navigationController) + ), + animated: false + ) return true } diff --git a/App/Sources/Coordinators/AppCoordinator.swift b/App/Sources/Coordinators/AppCoordinator.swift new file mode 100644 index 0000000..ebafe0c --- /dev/null +++ b/App/Sources/Coordinators/AppCoordinator.swift @@ -0,0 +1,40 @@ +// +// AppCoordinator.swift +// App +// +// Created by Javier Cicchelli on 21/03/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import Foundation +import ReviewsCoordinationKit +import ReviewsFeed +import UIKit + +final class AppCoordinator: Coordinator { + + // MARK: Constants + let router: any Router + + // MARK: Properties + var children: [any Coordinator] = [] + + lazy var navigationController = UINavigationController() + + // MARK: Initialisers + init(router: any Router) { + self.router = router + } + + // MARK: Functions + func present( + animated: Bool, + onDismiss: Router.OnDismissClosure? = nil + ) { + router.present( + navigationController, + animated: false + ) + } + +} diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift new file mode 100644 index 0000000..cf494ab --- /dev/null +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift @@ -0,0 +1,45 @@ +// +// FeedListCoordinator.swift +// ReviewsFeed +// +// Created by Javier Cicchelli on 21/03/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import Foundation +import ReviewsCoordinationKit + +public final class FeedListCoordinator: Coordinator { + + // MARK: Constants + public let router: any Router + + private let sessionConfiguration: URLSessionConfiguration + + // MARK: Properties + public var children: [any Coordinator] = [] + + // MARK: Initialisers + public init( + router: any Router, + sessionConfiguration: URLSessionConfiguration = .ephemeral + ) { + self.router = router + self.sessionConfiguration = sessionConfiguration + } + + // MARK: Functions + public func present( + animated: Bool, + onDismiss: Router.OnDismissClosure? = nil + ) { + router.present( + FeedListViewController(configuration: .init( + session: sessionConfiguration + )), + animated: animated, + onDismiss: onDismiss + ) + } + +} diff --git a/Libraries/Coordination/Kit/Sources/Routers/NavigationRouter.swift b/Libraries/Coordination/Kit/Sources/Classes/BaseNavigationRouter.swift similarity index 86% rename from Libraries/Coordination/Kit/Sources/Routers/NavigationRouter.swift rename to Libraries/Coordination/Kit/Sources/Classes/BaseNavigationRouter.swift index ebccc00..b6a7b10 100644 --- a/Libraries/Coordination/Kit/Sources/Routers/NavigationRouter.swift +++ b/Libraries/Coordination/Kit/Sources/Classes/BaseNavigationRouter.swift @@ -1,5 +1,5 @@ // -// NavigationRouter.swift +// BaseNavigationRouter.swift // ReviewsCoordinationKit // // Created by Javier Cicchelli on 21/03/2024. @@ -8,14 +8,14 @@ import UIKit -public class NavigationRouter: NSObject { +open class BaseNavigationRouter: NSObject { // MARK: Properties var navigationController: UINavigationController var onDismissForViewController: [UIViewController: Router.OnDismissClosure] = [:] // MARK: Initialisers - init(navigationController: UINavigationController) { + public init(navigationController: UINavigationController) { self.navigationController = navigationController super.init() @@ -37,7 +37,7 @@ public class NavigationRouter: NSObject { } // MARK: - UINavigationControllerDelegate -extension NavigationRouter: UINavigationControllerDelegate { +extension BaseNavigationRouter: UINavigationControllerDelegate { // MARK: Functions public func navigationController( diff --git a/Libraries/Coordination/Kit/Sources/Routers/PushRouter.swift b/Libraries/Coordination/Kit/Sources/Routers/StackRouter.swift similarity index 85% rename from Libraries/Coordination/Kit/Sources/Routers/PushRouter.swift rename to Libraries/Coordination/Kit/Sources/Routers/StackRouter.swift index 78ace46..05c672d 100644 --- a/Libraries/Coordination/Kit/Sources/Routers/PushRouter.swift +++ b/Libraries/Coordination/Kit/Sources/Routers/StackRouter.swift @@ -1,5 +1,5 @@ // -// File.swift +// StackRouter.swift // ReviewsCoordinationKit // // Created by Javier Cicchelli on 21/03/2024. @@ -8,14 +8,14 @@ import UIKit -public class PushRouter: NavigationRouter { +public class StackRouter: BaseNavigationRouter { // MARK: Constants private let rootViewController: UIViewController? // MARK: Initialisers public init( - navigationController: UINavigationController, + _ navigationController: UINavigationController, rootViewController: UIViewController? = nil ) { self.rootViewController = navigationController.viewControllers.first ?? rootViewController @@ -26,13 +26,13 @@ public class PushRouter: NavigationRouter { } // MARK: - Router -extension PushRouter: Router { +extension StackRouter: Router { // MARK: Functions public func present( _ viewController: UIViewController, animated: Bool, - onDismiss: OnDismissClosure? + onDismiss: OnDismissClosure? = nil ) { onDismissForViewController[viewController] = onDismiss diff --git a/Libraries/Coordination/Kit/Sources/Routers/WindowRouter.swift b/Libraries/Coordination/Kit/Sources/Routers/WindowRouter.swift index 21c7194..8376615 100644 --- a/Libraries/Coordination/Kit/Sources/Routers/WindowRouter.swift +++ b/Libraries/Coordination/Kit/Sources/Routers/WindowRouter.swift @@ -14,7 +14,7 @@ public class WindowRouter: Router { private let window: UIWindow? // MARK: Initialisers - public init(window: UIWindow?) { + public init(_ window: UIWindow?) { self.window = window } @@ -22,7 +22,7 @@ public class WindowRouter: Router { public func present( _ viewController: UIViewController, animated: Bool, - onDismiss: OnDismissClosure? + onDismiss: OnDismissClosure? = nil ) { window?.rootViewController = viewController window?.makeKeyAndVisible() diff --git a/Reviews.xcodeproj/project.pbxproj b/Reviews.xcodeproj/project.pbxproj index f6bb822..3e3f750 100644 --- a/Reviews.xcodeproj/project.pbxproj +++ b/Reviews.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 02909E792BAB6B0200710E14 /* FilterOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E782BAB6B0200710E14 /* FilterOption.swift */; }; 02909E7B2BAB6D2E00710E14 /* Bundle+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E7A2BAB6D2E00710E14 /* Bundle+Constants.swift */; }; 02909E7D2BAB7FFE00710E14 /* Review+DTOs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E7C2BAB7FFE00710E14 /* Review+DTOs.swift */; }; + 02C1B1972BAC9BFE001781DE /* FeedListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C1B1962BAC9BFE001781DE /* FeedListCoordinator.swift */; }; + 02C1B1A92BACA722001781DE /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C1B1A82BACA722001781DE /* AppCoordinator.swift */; }; 02DA924E2BAAE3FD00C47985 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 02DA924D2BAAE3FD00C47985 /* Localizable.xcstrings */; }; 02DC7F9F2BA51793000EEEBE /* ReviewsFeed.h in Headers */ = {isa = PBXBuildFile; fileRef = 02DC7F912BA51793000EEEBE /* ReviewsFeed.h */; settings = {ATTRIBUTES = (Public, ); }; }; 02DC7FA22BA51793000EEEBE /* ReviewsFeed.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02DC7F8F2BA51793000EEEBE /* ReviewsFeed.framework */; }; @@ -63,6 +65,8 @@ 02909E782BAB6B0200710E14 /* FilterOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterOption.swift; sourceTree = ""; }; 02909E7A2BAB6D2E00710E14 /* Bundle+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Constants.swift"; sourceTree = ""; }; 02909E7C2BAB7FFE00710E14 /* Review+DTOs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Review+DTOs.swift"; sourceTree = ""; }; + 02C1B1962BAC9BFE001781DE /* FeedListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListCoordinator.swift; sourceTree = ""; }; + 02C1B1A82BACA722001781DE /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; 02DA924D2BAAE3FD00C47985 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 02DC7F8F2BA51793000EEEBE /* ReviewsFeed.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReviewsFeed.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02DC7F912BA51793000EEEBE /* ReviewsFeed.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReviewsFeed.h; sourceTree = ""; }; @@ -195,6 +199,30 @@ path = Test; sourceTree = ""; }; + 02C1B1952BAC9BE7001781DE /* Coordinators */ = { + isa = PBXGroup; + children = ( + 02C1B1962BAC9BFE001781DE /* FeedListCoordinator.swift */, + ); + path = Coordinators; + sourceTree = ""; + }; + 02C1B1A62BACA6FC001781DE /* App */ = { + isa = PBXGroup; + children = ( + 345AD11B24C6EDD9004E2EE1 /* AppDelegate.swift */, + ); + path = App; + sourceTree = ""; + }; + 02C1B1A72BACA70B001781DE /* Coordinators */ = { + isa = PBXGroup; + children = ( + 02C1B1A82BACA722001781DE /* AppCoordinator.swift */, + ); + path = Coordinators; + sourceTree = ""; + }; 02DA924B2BAAE3E500C47985 /* Resources */ = { isa = PBXGroup; children = ( @@ -232,7 +260,8 @@ 02DC7F742BA4F93B000EEEBE /* Sources */ = { isa = PBXGroup; children = ( - 345AD11B24C6EDD9004E2EE1 /* AppDelegate.swift */, + 02C1B1A62BACA6FC001781DE /* App */, + 02C1B1A72BACA70B001781DE /* Coordinators */, ); path = Sources; sourceTree = ""; @@ -273,6 +302,7 @@ 02DC7FB02BA51B4F000EEEBE /* Sources */ = { isa = PBXGroup; children = ( + 02C1B1952BAC9BE7001781DE /* Coordinators */, 02620B862BA89C0000DE7137 /* Logic */, 02620B852BA89BF900DE7137 /* UI */, ); @@ -447,6 +477,7 @@ 0220ADA32BA90646001E6A9F /* FeedItemView.swift in Sources */, 02EACF362BABB2F200FF8ECD /* TopWord+DTOs.swift in Sources */, 02DC7FAF2BA51B4C000EEEBE /* Review.swift in Sources */, + 02C1B1972BAC9BFE001781DE /* FeedListCoordinator.swift in Sources */, 02DC7FAE2BA51B4C000EEEBE /* FeedListViewController.swift in Sources */, 02EACF322BABB23A00FF8ECD /* TopWordsView.swift in Sources */, 02909E792BAB6B0200710E14 /* FilterOption.swift in Sources */, @@ -457,6 +488,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 02C1B1A92BACA722001781DE /* AppCoordinator.swift in Sources */, 345AD11C24C6EDD9004E2EE1 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0;