From c3fc42d7246ff4b0c294e1259ef6d4748e23a29e Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 18:11:57 +0200 Subject: [PATCH 01/18] Removed the SceneDelegate delegate. --- Apps/Locations/Resources/Info.plist | 20 +------ Apps/Locations/Sources/AppDelegate.swift | 34 ++++------- Apps/Locations/Sources/SceneDelegate.swift | 67 ---------------------- DeepLinking.xcodeproj/project.pbxproj | 4 -- 4 files changed, 13 insertions(+), 112 deletions(-) delete mode 100644 Apps/Locations/Sources/SceneDelegate.swift diff --git a/Apps/Locations/Resources/Info.plist b/Apps/Locations/Resources/Info.plist index 0eb786d..0c67376 100644 --- a/Apps/Locations/Resources/Info.plist +++ b/Apps/Locations/Resources/Info.plist @@ -1,23 +1,5 @@ - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - - - - - + diff --git a/Apps/Locations/Sources/AppDelegate.swift b/Apps/Locations/Sources/AppDelegate.swift index b40c8ba..974d077 100644 --- a/Apps/Locations/Sources/AppDelegate.swift +++ b/Apps/Locations/Sources/AppDelegate.swift @@ -11,35 +11,25 @@ import CoreData @main class AppDelegate: UIResponder, UIApplicationDelegate { + + // MARK: Properties + + var window: UIWindow? = .init(frame: UIScreen.main.bounds) + + // MARK: UIApplicationDelegate func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - // Override point for customization after application launch. + window?.rootViewController = ViewController() + window?.makeKeyAndVisible() + return true } - - // MARK: UISceneSession Lifecycle - - func application( - _ application: UIApplication, - configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions - ) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application( - _ application: UIApplication, - didDiscardSceneSessions sceneSessions: Set - ) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + + func applicationDidEnterBackground(_ application: UIApplication) { + // Save changes in the application's managed object context when the application transitions to the background. } } - diff --git a/Apps/Locations/Sources/SceneDelegate.swift b/Apps/Locations/Sources/SceneDelegate.swift deleted file mode 100644 index c95cee8..0000000 --- a/Apps/Locations/Sources/SceneDelegate.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// SceneDelegate.swift -// Locations -// -// Created by Javier Cicchelli on 08/04/2023. -// Copyright © 2023 Röck+Cöde. All rights reserved. -// - -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - 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 windowScene = scene as? UIWindowScene else { - return - } - - window = { - let window = UIWindow(windowScene: windowScene) - - window.rootViewController = ViewController() - window.makeKeyAndVisible() - - return window - }() - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - - // Save changes in the application's managed object context when the application transitions to the background. - } - -} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 6181e6f..99dee7a 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; - 46EB331D29E1CE04001D5EAF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331C29E1CE04001D5EAF /* SceneDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* ViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; 46EB332A29E1CE05001D5EAF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332829E1CE05001D5EAF /* LaunchScreen.storyboard */; }; @@ -114,7 +113,6 @@ 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 46EB331C29E1CE04001D5EAF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 46EB331E29E1CE04001D5EAF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 46EB332629E1CE05001D5EAF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46EB332929E1CE05001D5EAF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -200,7 +198,6 @@ isa = PBXGroup; children = ( 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */, - 46EB331C29E1CE04001D5EAF /* SceneDelegate.swift */, 46EB331E29E1CE04001D5EAF /* ViewController.swift */, ); path = Sources; @@ -409,7 +406,6 @@ files = ( 46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, - 46EB331D29E1CE04001D5EAF /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -- 2.47.1 From 30bb62e795e4dba4233964c3c58d2c71c274a6d7 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 18:14:20 +0200 Subject: [PATCH 02/18] Implemented the WindowRouter router in the Core library. --- .../Sources/Core/Routers/WindowRouter.swift | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift diff --git a/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift b/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift new file mode 100644 index 0000000..6cbe31b --- /dev/null +++ b/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift @@ -0,0 +1,39 @@ +// +// WindowRouter.swift +// Core +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +public class WindowRouter: Router { + + // MARK: Properties + + private let window: UIWindow? + + // MARK: Initialisers + + public init(window: UIWindow?) { + self.window = window + } + + // MARK: Functions + + public func present( + _ viewController: UIViewController, + animated: Bool, + onDismiss: OnDismissedClosure? + ) { + window?.rootViewController = viewController + + window?.makeKeyAndVisible() + } + + public func dismiss(animated: Bool) { + // Nothing to do here... + } + +} -- 2.47.1 From 0fe40095c707ab07a8be2913d47fadfb5d32b954 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:20:25 +0200 Subject: [PATCH 03/18] Created the LocationsListViewModel view model. --- .../Screens/LocationsListViewModel.swift | 24 +++++++++++++++++++ DeepLinking.xcodeproj/project.pbxproj | 12 ++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Apps/Locations/Sources/Screens/LocationsListViewModel.swift diff --git a/Apps/Locations/Sources/Screens/LocationsListViewModel.swift b/Apps/Locations/Sources/Screens/LocationsListViewModel.swift new file mode 100644 index 0000000..dbc29c5 --- /dev/null +++ b/Apps/Locations/Sources/Screens/LocationsListViewModel.swift @@ -0,0 +1,24 @@ +// +// LocationsListViewModel.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Combine +import Core + +class LocationsListViewModel: ObservableObject, ViewModel { + + // MARK: Properties + + var coordinator: Coordinator + + // MARK: Initialisers + + init(coordinator: Coordinator) { + self.coordinator = coordinator + } + +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 99dee7a..f9db80a 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* ViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; @@ -110,6 +111,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -136,6 +138,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 46C3B7C929E5CB8F00F8F57C /* Screens */ = { + isa = PBXGroup; + children = ( + 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */, + ); + path = Screens; + sourceTree = ""; + }; 46EB325029E1BBD1001D5EAF = { isa = PBXGroup; children = ( @@ -197,6 +207,7 @@ 46EB332F29E1CE1E001D5EAF /* Sources */ = { isa = PBXGroup; children = ( + 46C3B7C929E5CB8F00F8F57C /* Screens */, 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */, 46EB331E29E1CE04001D5EAF /* ViewController.swift */, ); @@ -406,6 +417,7 @@ files = ( 46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, + 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -- 2.47.1 From 229bd1b8782c65c1ab4926407668994ae2b887c3 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:21:52 +0200 Subject: [PATCH 04/18] Renamed the ViewController view controller as LocationsListViewController and conformed it to the View protocol from the Core library. --- .../Screens/LocationsListViewController.swift | 40 +++++++++++++++++++ Apps/Locations/Sources/ViewController.swift | 19 --------- DeepLinking.xcodeproj/project.pbxproj | 8 ++-- 3 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 Apps/Locations/Sources/Screens/LocationsListViewController.swift delete mode 100644 Apps/Locations/Sources/ViewController.swift diff --git a/Apps/Locations/Sources/Screens/LocationsListViewController.swift b/Apps/Locations/Sources/Screens/LocationsListViewController.swift new file mode 100644 index 0000000..954fb8b --- /dev/null +++ b/Apps/Locations/Sources/Screens/LocationsListViewController.swift @@ -0,0 +1,40 @@ +// +// LocationsListViewController.swift +// Locations +// +// Created by Javier Cicchelli on 08/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Core +import UIKit + +class LocationsListViewController: UIViewController, View { + + // MARK: Properties + + var viewModel: ViewModel + + // MARK: Initialisers + + init(viewModel: ViewModel) { + self.viewModel = viewModel + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Locations" + + view.backgroundColor = .white + } + +} diff --git a/Apps/Locations/Sources/ViewController.swift b/Apps/Locations/Sources/ViewController.swift deleted file mode 100644 index 8c3be97..0000000 --- a/Apps/Locations/Sources/ViewController.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ViewController.swift -// Locations -// -// Created by Javier Cicchelli on 08/04/2023. -// Copyright © 2023 Röck+Cöde. All rights reserved. -// - -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - - view.backgroundColor = .red - } - -} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index f9db80a..5d03cec 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; - 46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* ViewController.swift */; }; + 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; 46EB332A29E1CE05001D5EAF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332829E1CE05001D5EAF /* LaunchScreen.storyboard */; }; 46EB334429E1D1EC001D5EAF /* Libraries in Frameworks */ = {isa = PBXBuildFile; productRef = 46EB334329E1D1EC001D5EAF /* Libraries */; }; @@ -115,7 +115,7 @@ 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 46EB331E29E1CE04001D5EAF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewController.swift; sourceTree = ""; }; 46EB332629E1CE05001D5EAF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46EB332929E1CE05001D5EAF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46EB332B29E1CE05001D5EAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -141,6 +141,7 @@ 46C3B7C929E5CB8F00F8F57C /* Screens */ = { isa = PBXGroup; children = ( + 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */, 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */, ); path = Screens; @@ -209,7 +210,6 @@ children = ( 46C3B7C929E5CB8F00F8F57C /* Screens */, 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */, - 46EB331E29E1CE04001D5EAF /* ViewController.swift */, ); path = Sources; sourceTree = ""; @@ -415,7 +415,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */, + 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, ); -- 2.47.1 From a55ba672dad5d7df6d454c9d8465417e1987b262 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:23:12 +0200 Subject: [PATCH 05/18] Implemented a bare-bone LocationsListsCoordinator coordinator. --- .../LocationsListCoordinator.swift | 37 +++++++++++++++++++ DeepLinking.xcodeproj/project.pbxproj | 12 ++++++ 2 files changed, 49 insertions(+) create mode 100644 Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift diff --git a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift new file mode 100644 index 0000000..31c5577 --- /dev/null +++ b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift @@ -0,0 +1,37 @@ +// +// LocationsListCoordinator.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Core +import UIKit + +class LocationsListCoordinator: Coordinator { + + // MARK: Properties + + var children: [Coordinator] = [] + var router: Router + + // MARK: Initialisers + + init(router: Router) { + self.router = router + } + + // MARK: Functions + + func present(animated: Bool, onDismiss: (() -> Void)?) { + router.present( + UINavigationController(rootViewController: LocationsListViewController( + viewModel: LocationsListViewModel(coordinator: self) + )), + animated: animated, + onDismiss: onDismiss + ) + } + +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 5d03cec..d61e837 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */; }; 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; @@ -111,6 +112,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = ""; }; 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -138,6 +140,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 46C3B7C429E5BEE900F8F57C /* Coordinators */ = { + isa = PBXGroup; + children = ( + 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */, + ); + path = Coordinators; + sourceTree = ""; + }; 46C3B7C929E5CB8F00F8F57C /* Screens */ = { isa = PBXGroup; children = ( @@ -209,6 +219,7 @@ isa = PBXGroup; children = ( 46C3B7C929E5CB8F00F8F57C /* Screens */, + 46C3B7C429E5BEE900F8F57C /* Coordinators */, 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */, ); path = Sources; @@ -415,6 +426,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */, 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, -- 2.47.1 From b4b99dfa8773578527ba3a11210df38dfd3913ad Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:24:58 +0200 Subject: [PATCH 06/18] Integrated a WindowRouter router and a LocationsListCoordinator coordinator into the AppDelegate delegate to present the coordinator at launch time. --- Apps/Locations/Sources/AppDelegate.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Apps/Locations/Sources/AppDelegate.swift b/Apps/Locations/Sources/AppDelegate.swift index 974d077..ae859b7 100644 --- a/Apps/Locations/Sources/AppDelegate.swift +++ b/Apps/Locations/Sources/AppDelegate.swift @@ -6,15 +6,17 @@ // Copyright © 2023 Röck+Cöde. All rights reserved. // +import Core import UIKit -import CoreData @main class AppDelegate: UIResponder, UIApplicationDelegate { // MARK: Properties - var window: UIWindow? = .init(frame: UIScreen.main.bounds) + lazy var coordinator: LocationsListCoordinator = .init(router: router) + lazy var router: WindowRouter = .init(window: window) + lazy var window: UIWindow? = .init(frame: UIScreen.main.bounds) // MARK: UIApplicationDelegate @@ -22,8 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - window?.rootViewController = ViewController() - window?.makeKeyAndVisible() + coordinator.present(animated: true, onDismiss: nil) return true } -- 2.47.1 From 0dfbe62603d576ff58ac6c15514787e3e9d12fa1 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:35:23 +0200 Subject: [PATCH 07/18] Moved the LocationsListViewModel view model and LocationsListViewController view controller into its own folder inside the Screens group. --- .../LocationsListViewController.swift | 0 .../LocationsListViewModel.swift | 0 DeepLinking.xcodeproj/project.pbxproj | 13 ++++++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) rename Apps/Locations/Sources/Screens/{ => LocationsList}/LocationsListViewController.swift (100%) rename Apps/Locations/Sources/Screens/{ => LocationsList}/LocationsListViewModel.swift (100%) diff --git a/Apps/Locations/Sources/Screens/LocationsListViewController.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift similarity index 100% rename from Apps/Locations/Sources/Screens/LocationsListViewController.swift rename to Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift diff --git a/Apps/Locations/Sources/Screens/LocationsListViewModel.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift similarity index 100% rename from Apps/Locations/Sources/Screens/LocationsListViewModel.swift rename to Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index d61e837..098a514 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -149,12 +149,23 @@ sourceTree = ""; }; 46C3B7C929E5CB8F00F8F57C /* Screens */ = { + isa = PBXGroup; + children = ( + 46C3B7CC29E5CFBB00F8F57C /* LocationsList */, + 46C3B7CD29E5CFCD00F8F57C /* LocationsAdd */, + ); + path = Screens; + sourceTree = ""; + }; + 46C3B7CC29E5CFBB00F8F57C /* LocationsList */ = { isa = PBXGroup; children = ( 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */, 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */, ); - path = Screens; + path = LocationsList; + sourceTree = ""; + }; sourceTree = ""; }; 46EB325029E1BBD1001D5EAF = { -- 2.47.1 From 2d068de8ed6371420ac6cf258d35e29379528ef8 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:36:36 +0200 Subject: [PATCH 08/18] Created the LocationsAddViewModel view model. --- .../LocationsAdd/LocationsAddViewModel.swift | 24 +++++++++++++++++++ DeepLinking.xcodeproj/project.pbxproj | 9 +++++++ 2 files changed, 33 insertions(+) create mode 100644 Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift new file mode 100644 index 0000000..15be2f2 --- /dev/null +++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift @@ -0,0 +1,24 @@ +// +// LocationsAddViewModel.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Combine +import Core + +class LocationsAddViewModel: ObservableObject, ViewModel { + + // MARK: Properties + + var coordinator: Coordinator + + // MARK: Initialisers + + init(coordinator: Coordinator) { + self.coordinator = coordinator + } + +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 098a514..84a887a 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */; }; 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; + 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; @@ -114,6 +115,7 @@ /* Begin PBXFileReference section */ 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = ""; }; 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; + 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -166,6 +168,12 @@ path = LocationsList; sourceTree = ""; }; + 46C3B7CD29E5CFCD00F8F57C /* LocationsAdd */ = { + isa = PBXGroup; + children = ( + 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */, + ); + path = LocationsAdd; sourceTree = ""; }; 46EB325029E1BBD1001D5EAF = { @@ -440,6 +448,7 @@ 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */, 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, + 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; -- 2.47.1 From 8deadffe2ae0b8fb25a04467ce767c4abab235b3 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 19:36:55 +0200 Subject: [PATCH 09/18] Created the LocationsAddViewController view controller. --- .../LocationsAddViewController.swift | 40 +++++++++++++++++++ DeepLinking.xcodeproj/project.pbxproj | 4 ++ 2 files changed, 44 insertions(+) create mode 100644 Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift new file mode 100644 index 0000000..4dd47c3 --- /dev/null +++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift @@ -0,0 +1,40 @@ +// +// LocationsAddViewController.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Core +import UIKit + +class LocationsAddViewController: UIViewController, View { + + // MARK: Properties + + var viewModel: ViewModel + + // MARK: Initialisers + + init(viewModel: ViewModel) { + self.viewModel = viewModel + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Location Add" + + view.backgroundColor = .white + } + +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 84a887a..1ae4ae5 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */; }; 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; }; + 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; @@ -116,6 +117,7 @@ 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = ""; }; 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; }; + 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewController.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -172,6 +174,7 @@ isa = PBXGroup; children = ( 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */, + 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */, ); path = LocationsAdd; sourceTree = ""; @@ -449,6 +452,7 @@ 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */, + 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; -- 2.47.1 From 8a0c7d95e012f5b1131492380536b33e7bea6ecb Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 20:34:51 +0200 Subject: [PATCH 10/18] Implemented the BaseNavigationRouter class in the Core library. --- .../Core/Routers/BaseNavigationRouter.swift | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Apps/Locations/Libraries/Sources/Core/Routers/BaseNavigationRouter.swift diff --git a/Apps/Locations/Libraries/Sources/Core/Routers/BaseNavigationRouter.swift b/Apps/Locations/Libraries/Sources/Core/Routers/BaseNavigationRouter.swift new file mode 100644 index 0000000..411b13a --- /dev/null +++ b/Apps/Locations/Libraries/Sources/Core/Routers/BaseNavigationRouter.swift @@ -0,0 +1,68 @@ +// +// BaseNavigationRouter.swift +// Core +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +/// This is a base class for the `NavigationRouter` concrete router implementations. +public class BaseNavigationRouter: NSObject { + + // MARK: Properties + + /// A navigation controller to use within this concrete router. + var navigationController: UINavigationController + + /// Dictionary that persist `onDismiss` closure for its respective view controllers until one of the later is dismissed. + var onDismissForViewController: [UIViewController: Router.OnDismissedClosure] = [:] + + // MARK: Initialisers + + /// Initialise this router. + /// - Parameter navigationController: A `UINavigationController` navigation controller instance to use in this router. + init(navigationController: UINavigationController) { + self.navigationController = navigationController + + super.init() + + self.navigationController.delegate = self + } + + // MARK: Functions + + /// Executes the `onDismiss` closure for a given view controller. + /// - Parameter viewController: A `UIViewController` view controller instance for which the on dismiss closure will be executed. + func performOnDismissed(for viewController: UIViewController) { + guard let onDismiss = onDismissForViewController[viewController] else { + return + } + + onDismiss() + + onDismissForViewController[viewController] = nil + } + +} + +// MARK: - UINavigationControllerDelegate + +extension BaseNavigationRouter: UINavigationControllerDelegate { + + // MARK: Functions + + public func navigationController( + _ navigationController: UINavigationController, + didShow viewController: UIViewController, + animated: Bool + ) { + guard let dismissedViewController = navigationController.transitionCoordinator?.viewController(forKey: .from) else { + return + } + + performOnDismissed(for: dismissedViewController) + } + +} -- 2.47.1 From 0d824637aeb4cff31821a0bb62af27160a828a67 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 20:36:12 +0200 Subject: [PATCH 11/18] Renamed the NavigationRouter router in the Core library as PushNavigationRouter router and implemented the inheritance from the BaseNavigationRouter class. --- .../Core/Routers/NavigationRouter.swift | 105 ------------------ .../Core/Routers/PushNavigationRouter.swift | 64 +++++++++++ 2 files changed, 64 insertions(+), 105 deletions(-) delete mode 100644 Apps/Locations/Libraries/Sources/Core/Routers/NavigationRouter.swift create mode 100644 Apps/Locations/Libraries/Sources/Core/Routers/PushNavigationRouter.swift diff --git a/Apps/Locations/Libraries/Sources/Core/Routers/NavigationRouter.swift b/Apps/Locations/Libraries/Sources/Core/Routers/NavigationRouter.swift deleted file mode 100644 index 688c8ce..0000000 --- a/Apps/Locations/Libraries/Sources/Core/Routers/NavigationRouter.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// NavigationRouter.swift -// Core -// -// Created by Javier Cicchelli on 11/04/2023. -// Copyright © 2023 Röck+Cöde. All rights reserved. -// - -import UIKit - -/// This class is responsible for presenting view controllers, as it is a concrete implementation of the `Router` protocol, but it won't know what view controller or which view controller is next. -public class NavigationRouter: NSObject { - - // MARK: Properties - - /// A navigation controller to use within this concrete router. - private let navigationController: UINavigationController - - /// A root view controller coming in from the navigation controller, if any. - private let rootViewController: UIViewController? - - /// Dictionary that persist `onDismiss` closure for its respective view controllers until one of the later is dismissed. - private var onDismissForViewController: [UIViewController: Router.OnDismissedClosure] = [:] - - // MARK: Initialisers - - /// Initialise this router. - /// - Parameter navigationController: A `UINavigationController` navigation controller instance to use in this router. - public init(navigationController: UINavigationController) { - self.navigationController = navigationController - self.rootViewController = navigationController.viewControllers.first - - super.init() - - self.navigationController.delegate = self - } - -} - -// MARK: - Router - -extension NavigationRouter: Router { - - // MARK: Functions - - public func present( - _ viewController: UIViewController, - animated: Bool, - onDismiss: OnDismissedClosure? - ) { - onDismissForViewController[viewController] = onDismiss - - navigationController.pushViewController(viewController, animated: animated) - } - - public func dismiss(animated: Bool) { - guard let rootViewController else { - navigationController.popViewController(animated: animated) - return - } - - performOnDismissed(for: rootViewController) - - navigationController.popToViewController(rootViewController, animated: animated) - } - -} - -// MARK: - UINavigationControllerDelegate - -extension NavigationRouter: UINavigationControllerDelegate { - - // MARK: Functions - - public func navigationController( - _ navigationController: UINavigationController, - didShow viewController: UIViewController, - animated: Bool - ) { - guard let dismissedViewController = navigationController.transitionCoordinator?.viewController(forKey: .from) else { - return - } - - performOnDismissed(for: dismissedViewController) - } - -} - -// MARK: - Helpers - -private extension NavigationRouter { - - // MARK: Functions - - func performOnDismissed(for viewController: UIViewController) { - guard let onDismiss = onDismissForViewController[viewController] else { - return - } - - onDismiss() - - onDismissForViewController[viewController] = nil - } - -} diff --git a/Apps/Locations/Libraries/Sources/Core/Routers/PushNavigationRouter.swift b/Apps/Locations/Libraries/Sources/Core/Routers/PushNavigationRouter.swift new file mode 100644 index 0000000..4d9b77b --- /dev/null +++ b/Apps/Locations/Libraries/Sources/Core/Routers/PushNavigationRouter.swift @@ -0,0 +1,64 @@ +// +// PushNavigationRouter.swift +// Core +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +/// This class is responsible for presenting view controllers, as it is a concrete implementation of the `Router` protocol, but it won't know what view controller or which view controller is next. +public class PushNavigationRouter: BaseNavigationRouter { + + // MARK: Properties + + /// A root view controller coming in from the navigation controller, if any. + private let rootViewController: UIViewController? + + // MARK: Initialisers + + /// Initialise this router. + /// - Parameters: + /// - navigationController: A `UINavigationController` navigation controller instance to use in this router. + /// - rootViewController: A `UIViewController` view controller instance to define as a root view controller of the navigation controller. + /// - Note This initialiser added the `rootViewController` parameter although it is not really needed to differentiate itself from the `.init(navigationController:)` implemented for the `BaseNavigationRouter` base class. + public init( + navigationController: UINavigationController, + rootViewController: UIViewController? = nil + ) { + self.rootViewController = navigationController.viewControllers.first ?? rootViewController + + super.init(navigationController: navigationController) + } + +} + +// MARK: - Router + +extension PushNavigationRouter: Router { + + // MARK: Functions + + public func present( + _ viewController: UIViewController, + animated: Bool, + onDismiss: OnDismissedClosure? + ) { + onDismissForViewController[viewController] = onDismiss + + navigationController.pushViewController(viewController, animated: animated) + } + + public func dismiss(animated: Bool) { + guard let rootViewController else { + navigationController.popViewController(animated: animated) + return + } + + performOnDismissed(for: rootViewController) + + navigationController.popToViewController(rootViewController, animated: animated) + } + +} -- 2.47.1 From c16b40652f87476871d61050e1c345695454144a Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 20:36:53 +0200 Subject: [PATCH 12/18] implemented the ModalNavigationRouter router in the Core library that inherits from the BaseNavigationRouter class. --- .../Core/Routers/ModalNavigationRouter.swift | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Apps/Locations/Libraries/Sources/Core/Routers/ModalNavigationRouter.swift diff --git a/Apps/Locations/Libraries/Sources/Core/Routers/ModalNavigationRouter.swift b/Apps/Locations/Libraries/Sources/Core/Routers/ModalNavigationRouter.swift new file mode 100644 index 0000000..b80f2df --- /dev/null +++ b/Apps/Locations/Libraries/Sources/Core/Routers/ModalNavigationRouter.swift @@ -0,0 +1,85 @@ +// +// ModalNavigationRouter.swift +// Core +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +public class ModalNavigationRouter: BaseNavigationRouter { + + // MARK: Properties + + /// The parent view controller from where this router is being called from. + public unowned let parentViewController: UIViewController + + // MARK: Initialisers + + /// Initialise this router. + /// - Parameter parentViewController: A `UIViewController` view controller instance from where this router is originated. + public init(parentViewController: UIViewController) { + self.parentViewController = parentViewController + + super.init(navigationController: .init()) + } + +} + +extension ModalNavigationRouter: Router { + public func present( + _ viewController: UIViewController, + animated: Bool, + onDismiss: OnDismissedClosure? + ) { + onDismissForViewController[viewController] = onDismiss + + if navigationController.viewControllers.isEmpty { + presentModally(viewController, animated: animated) + } else { + navigationController.pushViewController(viewController, animated: animated) + } + } + + public func dismiss(animated: Bool) { + guard let firstViewController = navigationController.viewControllers.first else { + return + } + + performOnDismissed(for: firstViewController) + + parentViewController.dismiss(animated: animated) + } + +} + +// MARK: - Helpers + +private extension ModalNavigationRouter { + + // MARK: Functions + + func presentModally(_ viewController: UIViewController, animated: Bool) { + viewController.navigationItem.leftBarButtonItem = UIBarButtonItem( + title: "Cancel", + style: .plain, + target: self, + action: #selector(onCancelPressed) + ) + + navigationController.setViewControllers([viewController], animated: false) + + parentViewController.present(navigationController, animated: animated) + } + + @objc func onCancelPressed() { + guard let firstViewController = navigationController.viewControllers.first else { + return + } + + performOnDismissed(for: firstViewController) + dismiss(animated: true) + } + +} -- 2.47.1 From 5e5653bbe2cc86dc6a6622ebc7cbd41fd67bc165 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 20:47:58 +0200 Subject: [PATCH 13/18] Removed the View and ViewModel protocols from the Core library. --- .../Libraries/Sources/Core/Protocols/View.swift | 17 ----------------- .../Sources/Core/Protocols/ViewModel.swift | 17 ----------------- 2 files changed, 34 deletions(-) delete mode 100644 Apps/Locations/Libraries/Sources/Core/Protocols/View.swift delete mode 100644 Apps/Locations/Libraries/Sources/Core/Protocols/ViewModel.swift diff --git a/Apps/Locations/Libraries/Sources/Core/Protocols/View.swift b/Apps/Locations/Libraries/Sources/Core/Protocols/View.swift deleted file mode 100644 index 43c0b70..0000000 --- a/Apps/Locations/Libraries/Sources/Core/Protocols/View.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// View.swift -// Core -// -// Created by Javier Cicchelli on 11/04/2023. -// Copyright © 2023 Röck+Cöde. All rights reserved. -// - -/// This protocol defines the view of the **MVVM** architecture. -public protocol View { - - // MARK: Properties - - /// The view model related to the view. - var viewModel: ViewModel { get set } - -} diff --git a/Apps/Locations/Libraries/Sources/Core/Protocols/ViewModel.swift b/Apps/Locations/Libraries/Sources/Core/Protocols/ViewModel.swift deleted file mode 100644 index a550329..0000000 --- a/Apps/Locations/Libraries/Sources/Core/Protocols/ViewModel.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// ViewModel.swift -// Core -// -// Created by Javier Cicchelli on 11/04/2023. -// Copyright © 2023 Röck+Cöde. All rights reserved. -// - -/// This protocol defines the view model of the **MVVM** architecture. -public protocol ViewModel: AnyObject { - - // MARK: Properties - - /// The reference to the coordinator that initialised the view model. - var coordinator: Coordinator { get set } - -} -- 2.47.1 From 9b14b79ea20145f2aa0d1a85934050775e5a9edd Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 21:12:03 +0200 Subject: [PATCH 14/18] Defined the LocationsListCoordinable protocol and conformed the LocationsListCoordinator coordinator to it. --- .../Coordinators/LocationsListCoordinator.swift | 2 +- .../Protocols/LocationsListCoordinable.swift | 11 +++++++++++ DeepLinking.xcodeproj/project.pbxproj | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift diff --git a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift index 31c5577..8efe66e 100644 --- a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift +++ b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift @@ -9,7 +9,7 @@ import Core import UIKit -class LocationsListCoordinator: Coordinator { +class LocationsListCoordinator: LocationsListCoordinable { // MARK: Properties diff --git a/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift b/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift new file mode 100644 index 0000000..b805fab --- /dev/null +++ b/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift @@ -0,0 +1,11 @@ +// +// LocationsListCoordinable.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Core + +protocol LocationsListCoordinable: Coordinator {} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 1ae4ae5..1e86b9a 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; }; 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */; }; + 46C3B7D829E5E55000F8F57C /* LocationsListCoordinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; @@ -118,6 +119,7 @@ 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; }; 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewController.swift; sourceTree = ""; }; + 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinable.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -148,6 +150,7 @@ isa = PBXGroup; children = ( 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */, + 46C3B7D929E5E7F900F8F57C /* Protocols */, ); path = Coordinators; sourceTree = ""; @@ -179,6 +182,14 @@ path = LocationsAdd; sourceTree = ""; }; + 46C3B7D929E5E7F900F8F57C /* Protocols */ = { + isa = PBXGroup; + children = ( + 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 46EB325029E1BBD1001D5EAF = { isa = PBXGroup; children = ( @@ -451,6 +462,7 @@ 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */, 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, + 46C3B7D829E5E55000F8F57C /* LocationsListCoordinable.swift in Sources */, 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */, 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, -- 2.47.1 From b9417244a402f08865a95648f8ed82d6e8c98ab3 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 21:36:16 +0200 Subject: [PATCH 15/18] Work done so far in the app architecture setup. --- .../LocationsAddCoordinator.swift | 35 +++++++++++++++++++ .../LocationsListCoordinator.swift | 28 ++++++++++++--- .../Protocols/LocationsAddCoordinable.swift | 11 ++++++ .../Protocols/LocationsListCoordinable.swift | 8 ++++- .../LocationsAddViewController.swift | 6 ++-- .../LocationsAdd/LocationsAddViewModel.swift | 2 +- .../LocationsListViewController.swift | 22 +++++++++--- .../LocationsListViewModel.swift | 14 +++++--- .../LocationsListViewModelable.swift | 19 ++++++++++ DeepLinking.xcodeproj/project.pbxproj | 22 +++++++++++- 10 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift create mode 100644 Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift create mode 100644 Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift diff --git a/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift new file mode 100644 index 0000000..bf4c5b5 --- /dev/null +++ b/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift @@ -0,0 +1,35 @@ +// +// LocationsAddCoordinator.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Core +import UIKit + +class LocationsAddCoordinator: LocationsAddCoordinable { + + // MARK: Properties + + var children: [Coordinator] = [] + var router: Router + + // MARK: Initialisers + + init(router: Router) { + self.router = router + } + + // MARK: Coordinator + + func present(animated: Bool, onDismiss: (() -> Void)?) { + router.present( + LocationsAddViewController(viewModel: .init(coordinator: self)), + animated: animated, + onDismiss: onDismiss + ) + } + +} diff --git a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift index 8efe66e..f7af6dc 100644 --- a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift +++ b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift @@ -12,26 +12,44 @@ import UIKit class LocationsListCoordinator: LocationsListCoordinable { // MARK: Properties - + var children: [Coordinator] = [] var router: Router + private var viewController: UIViewController? + // MARK: Initialisers init(router: Router) { self.router = router } - // MARK: Functions + // MARK: Coordinator func present(animated: Bool, onDismiss: (() -> Void)?) { + let navigationController = UINavigationController(rootViewController: LocationsListViewController( + viewModel: LocationsListViewModel(coordinator: self) + )) + + viewController = navigationController + router.present( - UINavigationController(rootViewController: LocationsListViewController( - viewModel: LocationsListViewModel(coordinator: self) - )), + navigationController, animated: animated, onDismiss: onDismiss ) } + // MARK: LocationsListCoordinable + + func showAddLocation() { + guard let viewController else { + return + } + + let router = ModalNavigationRouter(parentViewController: viewController) + let child = + print("ADD LOCATION PRESSED!") + } + } diff --git a/Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift b/Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift new file mode 100644 index 0000000..dacf48a --- /dev/null +++ b/Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift @@ -0,0 +1,11 @@ +// +// LocationsAddCoordinable.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Core + +protocol LocationsAddCoordinable: Coordinator {} diff --git a/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift b/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift index b805fab..ed435e4 100644 --- a/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift +++ b/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift @@ -8,4 +8,10 @@ import Core -protocol LocationsListCoordinable: Coordinator {} +protocol LocationsListCoordinable: Coordinator { + + // MARK: Functions + + func showAddLocation() + +} diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift index 4dd47c3..e69b32f 100644 --- a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift +++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift @@ -9,15 +9,15 @@ import Core import UIKit -class LocationsAddViewController: UIViewController, View { +class LocationsAddViewController: UIViewController { // MARK: Properties - var viewModel: ViewModel + var viewModel: LocationsAddViewModel // MARK: Initialisers - init(viewModel: ViewModel) { + init(viewModel: LocationsAddViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift index 15be2f2..692251c 100644 --- a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift +++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift @@ -9,7 +9,7 @@ import Combine import Core -class LocationsAddViewModel: ObservableObject, ViewModel { +class LocationsAddViewModel: ObservableObject { // MARK: Properties diff --git a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift index 954fb8b..90267b1 100644 --- a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift +++ b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift @@ -9,15 +9,15 @@ import Core import UIKit -class LocationsListViewController: UIViewController, View { +class LocationsListViewController: UIViewController { // MARK: Properties - var viewModel: ViewModel + var viewModel: LocationsListViewModelable // MARK: Initialisers - init(viewModel: ViewModel) { + init(viewModel: LocationsListViewModelable) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) @@ -32,9 +32,23 @@ class LocationsListViewController: UIViewController, View { override func viewDidLoad() { super.viewDidLoad() + navigationItem.rightBarButtonItem = UIBarButtonItem( + title: "Add", + style: .plain, + target: self, + action: #selector(addLocationPressed) + ) title = "Locations" - view.backgroundColor = .white + view.backgroundColor = .systemBackground } } + +// MARK: - Helpers + +private extension LocationsListViewController { + @objc func addLocationPressed() { + viewModel.addLocationPressed() + } +} diff --git a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift index dbc29c5..0448757 100644 --- a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift +++ b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift @@ -9,16 +9,22 @@ import Combine import Core -class LocationsListViewModel: ObservableObject, ViewModel { - +class LocationsListViewModel: ObservableObject, LocationsListViewModelable { + // MARK: Properties - var coordinator: Coordinator + weak var coordinator: LocationsListCoordinable? // MARK: Initialisers - init(coordinator: Coordinator) { + init(coordinator: LocationsListCoordinable) { self.coordinator = coordinator } + + // MARK: Functions + + func addLocationPressed() { + coordinator?.showAddLocation() + } } diff --git a/Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift b/Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift new file mode 100644 index 0000000..6375001 --- /dev/null +++ b/Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift @@ -0,0 +1,19 @@ +// +// LocationsListViewModelable.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +protocol LocationsListViewModelable { + + // MARK: Properties + + var coordinator: LocationsListCoordinable? { get set } + + // MARK: Functions + + func addLocationPressed() + +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 1e86b9a..7f53c13 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -11,7 +11,10 @@ 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; }; 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */; }; + 46C3B7D629E5E50500F8F57C /* LocationsListViewModelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D529E5E50500F8F57C /* LocationsListViewModelable.swift */; }; 46C3B7D829E5E55000F8F57C /* LocationsListCoordinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */; }; + 46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordinable.swift */; }; + 46C3B7DE29E5ED2E00F8F57C /* LocationsAddCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; 46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; }; @@ -119,7 +122,10 @@ 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; }; 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewController.swift; sourceTree = ""; }; + 46C3B7D529E5E50500F8F57C /* LocationsListViewModelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModelable.swift; sourceTree = ""; }; 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinable.swift; sourceTree = ""; }; + 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordinable.swift; sourceTree = ""; }; + 46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordinator.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -150,6 +156,7 @@ isa = PBXGroup; children = ( 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */, + 46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */, 46C3B7D929E5E7F900F8F57C /* Protocols */, ); path = Coordinators; @@ -169,6 +176,7 @@ children = ( 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */, 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */, + 46C3B7DA29E5E80B00F8F57C /* Protocols */, ); path = LocationsList; sourceTree = ""; @@ -176,8 +184,8 @@ 46C3B7CD29E5CFCD00F8F57C /* LocationsAdd */ = { isa = PBXGroup; children = ( - 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */, 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */, + 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */, ); path = LocationsAdd; sourceTree = ""; @@ -186,6 +194,15 @@ isa = PBXGroup; children = ( 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */, + 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordinable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + 46C3B7DA29E5E80B00F8F57C /* Protocols */ = { + isa = PBXGroup; + children = ( + 46C3B7D529E5E50500F8F57C /* LocationsListViewModelable.swift */, ); path = Protocols; sourceTree = ""; @@ -462,7 +479,10 @@ 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */, 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, + 46C3B7DE29E5ED2E00F8F57C /* LocationsAddCoordinator.swift in Sources */, + 46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordinable.swift in Sources */, 46C3B7D829E5E55000F8F57C /* LocationsListCoordinable.swift in Sources */, + 46C3B7D629E5E50500F8F57C /* LocationsListViewModelable.swift in Sources */, 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */, 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, -- 2.47.1 From b73a8e10101272ec4adf594ee961bdf6c2bf999c Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 23:34:07 +0200 Subject: [PATCH 16/18] Implemented the coordinator flow for LocationsList and LocationAdd screens. --- Apps/Locations/Sources/AppDelegate.swift | 2 +- .../LocationsAddCoordinator.swift | 10 ++- .../LocationsListCoordinator.swift | 27 ++++-- .../LocationsAddCoordination.swift} | 6 +- .../LocationsListCoordination.swift} | 8 +- .../LocationsAddViewModeling.swift | 15 ++++ .../LocationsListViewModeling.swift} | 8 +- .../LocationsAddViewController.swift | 10 +-- .../LocationsAdd/LocationsAddViewModel.swift | 8 +- .../LocationsListViewController.swift | 16 ++-- .../LocationsListViewModel.swift | 24 +++-- .../View Controllers/BaseViewController.swift | 31 +++++++ DeepLinking.xcodeproj/project.pbxproj | 88 ++++++++++++------- 13 files changed, 172 insertions(+), 81 deletions(-) rename Apps/Locations/Sources/{Coordinators/Protocols/LocationsAddCoordinable.swift => Protocols/Coordination/LocationsAddCoordination.swift} (56%) rename Apps/Locations/Sources/{Coordinators/Protocols/LocationsListCoordinable.swift => Protocols/Coordination/LocationsListCoordination.swift} (56%) create mode 100644 Apps/Locations/Sources/Protocols/ViewModeling/LocationsAddViewModeling.swift rename Apps/Locations/Sources/{Screens/LocationsList/Protocols/LocationsListViewModelable.swift => Protocols/ViewModeling/LocationsListViewModeling.swift} (54%) create mode 100644 Apps/Locations/Sources/View Controllers/BaseViewController.swift diff --git a/Apps/Locations/Sources/AppDelegate.swift b/Apps/Locations/Sources/AppDelegate.swift index ae859b7..55a61df 100644 --- a/Apps/Locations/Sources/AppDelegate.swift +++ b/Apps/Locations/Sources/AppDelegate.swift @@ -24,7 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - coordinator.present(animated: true, onDismiss: nil) + coordinator.present(animated: false, onDismiss: nil) return true } diff --git a/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift index bf4c5b5..61dd9c0 100644 --- a/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift +++ b/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift @@ -9,7 +9,7 @@ import Core import UIKit -class LocationsAddCoordinator: LocationsAddCoordinable { +class LocationsAddCoordinator: Coordinator { // MARK: Properties @@ -26,10 +26,16 @@ class LocationsAddCoordinator: LocationsAddCoordinable { func present(animated: Bool, onDismiss: (() -> Void)?) { router.present( - LocationsAddViewController(viewModel: .init(coordinator: self)), + LocationsAddViewController( + viewModel: LocationsAddViewModel(coordinator: self) + ), animated: animated, onDismiss: onDismiss ) } } + +// MARK: - LocationsAddCoordination + +extension LocationsAddCoordinator: LocationsAddCoordination {} diff --git a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift index f7af6dc..a7d2592 100644 --- a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift +++ b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift @@ -9,15 +9,15 @@ import Core import UIKit -class LocationsListCoordinator: LocationsListCoordinable { - +class LocationsListCoordinator: Coordinator { + // MARK: Properties - + var children: [Coordinator] = [] var router: Router private var viewController: UIViewController? - + // MARK: Initialisers init(router: Router) { @@ -40,16 +40,25 @@ class LocationsListCoordinator: LocationsListCoordinable { ) } - // MARK: LocationsListCoordinable +} + +// MARK: - LocationsListCoordination + +extension LocationsListCoordinator: LocationsListCoordination { - func showAddLocation() { + // MARK: Functions + + func openAddLocation() { guard let viewController else { return } - let router = ModalNavigationRouter(parentViewController: viewController) - let child = - print("ADD LOCATION PRESSED!") + present( + child: LocationsAddCoordinator( + router: ModalNavigationRouter(parentViewController: viewController) + ), + animated: true + ) } } diff --git a/Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift b/Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift similarity index 56% rename from Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift rename to Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift index dacf48a..6dd5bee 100644 --- a/Apps/Locations/Sources/Coordinators/Protocols/LocationsAddCoordinable.swift +++ b/Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift @@ -1,11 +1,9 @@ // -// LocationsAddCoordinable.swift +// LocationsAddCoordination.swift // Locations // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. // -import Core - -protocol LocationsAddCoordinable: Coordinator {} +protocol LocationsAddCoordination: AnyObject {} diff --git a/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift b/Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift similarity index 56% rename from Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift rename to Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift index ed435e4..bafe329 100644 --- a/Apps/Locations/Sources/Coordinators/Protocols/LocationsListCoordinable.swift +++ b/Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift @@ -1,17 +1,15 @@ // -// LocationsListCoordinable.swift +// LocationsListCoordination.swift // Locations // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. // -import Core - -protocol LocationsListCoordinable: Coordinator { +protocol LocationsListCoordination: AnyObject { // MARK: Functions - func showAddLocation() + func openAddLocation() } diff --git a/Apps/Locations/Sources/Protocols/ViewModeling/LocationsAddViewModeling.swift b/Apps/Locations/Sources/Protocols/ViewModeling/LocationsAddViewModeling.swift new file mode 100644 index 0000000..b4e707c --- /dev/null +++ b/Apps/Locations/Sources/Protocols/ViewModeling/LocationsAddViewModeling.swift @@ -0,0 +1,15 @@ +// +// LocationsAddViewModeling.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +protocol LocationsAddViewModeling: AnyObject { + + // MARK: Properties + + var coordinator: LocationsAddCoordination? { get set } + +} diff --git a/Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift b/Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift similarity index 54% rename from Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift rename to Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift index 6375001..90deeab 100644 --- a/Apps/Locations/Sources/Screens/LocationsList/Protocols/LocationsListViewModelable.swift +++ b/Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift @@ -1,19 +1,19 @@ // -// LocationsListViewModelable.swift +// LocationsListViewModeling.swift // Locations // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. // -protocol LocationsListViewModelable { +protocol LocationsListViewModeling: AnyObject { // MARK: Properties - var coordinator: LocationsListCoordinable? { get set } + var coordinator: LocationsListCoordination? { get set } // MARK: Functions - func addLocationPressed() + func openAddLocation() } diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift index e69b32f..86f4421 100644 --- a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift +++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewController.swift @@ -9,18 +9,18 @@ import Core import UIKit -class LocationsAddViewController: UIViewController { +class LocationsAddViewController: BaseViewController { // MARK: Properties - var viewModel: LocationsAddViewModel + var viewModel: LocationsAddViewModeling // MARK: Initialisers - init(viewModel: LocationsAddViewModel) { + init(viewModel: LocationsAddViewModeling) { self.viewModel = viewModel - super.init(nibName: nil, bundle: nil) + super.init() } required init?(coder: NSCoder) { @@ -33,8 +33,6 @@ class LocationsAddViewController: UIViewController { super.viewDidLoad() title = "Location Add" - - view.backgroundColor = .white } } diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift index 692251c..84e9678 100644 --- a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift +++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift @@ -13,12 +13,16 @@ class LocationsAddViewModel: ObservableObject { // MARK: Properties - var coordinator: Coordinator + weak var coordinator: LocationsAddCoordination? // MARK: Initialisers - init(coordinator: Coordinator) { + init(coordinator: LocationsAddCoordination) { self.coordinator = coordinator } } + +// MARK: - LocationsAddViewModeling + +extension LocationsAddViewModel: LocationsAddViewModeling {} diff --git a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift index 90267b1..cc35c26 100644 --- a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift +++ b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift @@ -9,18 +9,18 @@ import Core import UIKit -class LocationsListViewController: UIViewController { +class LocationsListViewController: BaseViewController { // MARK: Properties - var viewModel: LocationsListViewModelable + var viewModel: LocationsListViewModeling // MARK: Initialisers - init(viewModel: LocationsListViewModelable) { + init(viewModel: LocationsListViewModeling) { self.viewModel = viewModel - super.init(nibName: nil, bundle: nil) + super.init() } required init?(coder: NSCoder) { @@ -39,8 +39,6 @@ class LocationsListViewController: UIViewController { action: #selector(addLocationPressed) ) title = "Locations" - - view.backgroundColor = .systemBackground } } @@ -48,7 +46,11 @@ class LocationsListViewController: UIViewController { // MARK: - Helpers private extension LocationsListViewController { + + // MARK: Functions + @objc func addLocationPressed() { - viewModel.addLocationPressed() + viewModel.openAddLocation() } + } diff --git a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift index 0448757..c3644fa 100644 --- a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift +++ b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift @@ -9,22 +9,28 @@ import Combine import Core -class LocationsListViewModel: ObservableObject, LocationsListViewModelable { +class LocationsListViewModel: ObservableObject { // MARK: Properties - weak var coordinator: LocationsListCoordinable? + weak var coordinator: LocationsListCoordination? // MARK: Initialisers - init(coordinator: LocationsListCoordinable) { + init(coordinator: LocationsListCoordination) { self.coordinator = coordinator } - - // MARK: Functions - - func addLocationPressed() { - coordinator?.showAddLocation() - } } + +// MARK: - LocationsListViewModeling + +extension LocationsListViewModel: LocationsListViewModeling { + + // MARK: Functions + + func openAddLocation() { + coordinator?.openAddLocation() + } + +} diff --git a/Apps/Locations/Sources/View Controllers/BaseViewController.swift b/Apps/Locations/Sources/View Controllers/BaseViewController.swift new file mode 100644 index 0000000..ea3fec4 --- /dev/null +++ b/Apps/Locations/Sources/View Controllers/BaseViewController.swift @@ -0,0 +1,31 @@ +// +// BaseViewController.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +class BaseViewController: UIViewController { + + // MARK: Initialisers + + init() { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .systemBackground + } + +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index 7f53c13..b824d70 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -7,13 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + 02031EBF29E5F949003C108C /* LocationsAddViewModeling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */; }; + 02031EC629E5FEE4003C108C /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EC529E5FEE4003C108C /* BaseViewController.swift */; }; 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */; }; 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; }; 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */; }; - 46C3B7D629E5E50500F8F57C /* LocationsListViewModelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D529E5E50500F8F57C /* LocationsListViewModelable.swift */; }; - 46C3B7D829E5E55000F8F57C /* LocationsListCoordinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */; }; - 46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordinable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordinable.swift */; }; + 46C3B7D629E5E50500F8F57C /* LocationsListViewModeling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D529E5E50500F8F57C /* LocationsListViewModeling.swift */; }; + 46C3B7D829E5E55000F8F57C /* LocationsListCoordination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D729E5E55000F8F57C /* LocationsListCoordination.swift */; }; + 46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordination.swift */; }; 46C3B7DE29E5ED2E00F8F57C /* LocationsAddCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */; }; 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; }; 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; }; @@ -118,13 +120,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModeling.swift; sourceTree = ""; }; + 02031EC529E5FEE4003C108C /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = ""; }; 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; }; 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewController.swift; sourceTree = ""; }; - 46C3B7D529E5E50500F8F57C /* LocationsListViewModelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModelable.swift; sourceTree = ""; }; - 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinable.swift; sourceTree = ""; }; - 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordinable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordinable.swift; sourceTree = ""; }; + 46C3B7D529E5E50500F8F57C /* LocationsListViewModeling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModeling.swift; sourceTree = ""; }; + 46C3B7D729E5E55000F8F57C /* LocationsListCoordination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordination.swift; sourceTree = ""; }; + 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordination.swift; sourceTree = ""; }; 46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordinator.swift; sourceTree = ""; }; 46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = ""; }; 46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -152,12 +156,46 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 02031EC429E5FEB1003C108C /* View Controllers */ = { + isa = PBXGroup; + children = ( + 02031EC529E5FEE4003C108C /* BaseViewController.swift */, + ); + path = "View Controllers"; + sourceTree = ""; + }; + 0276C96029E5F5DC000B62AF /* Protocols */ = { + isa = PBXGroup; + children = ( + 0276C96229E5F5ED000B62AF /* Coordination */, + 0276C96129E5F5E5000B62AF /* ViewModeling */, + ); + path = Protocols; + sourceTree = ""; + }; + 0276C96129E5F5E5000B62AF /* ViewModeling */ = { + isa = PBXGroup; + children = ( + 02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */, + 46C3B7D529E5E50500F8F57C /* LocationsListViewModeling.swift */, + ); + path = ViewModeling; + sourceTree = ""; + }; + 0276C96229E5F5ED000B62AF /* Coordination */ = { + isa = PBXGroup; + children = ( + 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordination.swift */, + 46C3B7D729E5E55000F8F57C /* LocationsListCoordination.swift */, + ); + path = Coordination; + sourceTree = ""; + }; 46C3B7C429E5BEE900F8F57C /* Coordinators */ = { isa = PBXGroup; children = ( - 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */, 46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */, - 46C3B7D929E5E7F900F8F57C /* Protocols */, + 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */, ); path = Coordinators; sourceTree = ""; @@ -165,8 +203,8 @@ 46C3B7C929E5CB8F00F8F57C /* Screens */ = { isa = PBXGroup; children = ( - 46C3B7CC29E5CFBB00F8F57C /* LocationsList */, 46C3B7CD29E5CFCD00F8F57C /* LocationsAdd */, + 46C3B7CC29E5CFBB00F8F57C /* LocationsList */, ); path = Screens; sourceTree = ""; @@ -176,7 +214,6 @@ children = ( 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */, 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */, - 46C3B7DA29E5E80B00F8F57C /* Protocols */, ); path = LocationsList; sourceTree = ""; @@ -190,23 +227,6 @@ path = LocationsAdd; sourceTree = ""; }; - 46C3B7D929E5E7F900F8F57C /* Protocols */ = { - isa = PBXGroup; - children = ( - 46C3B7D729E5E55000F8F57C /* LocationsListCoordinable.swift */, - 46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordinable.swift */, - ); - path = Protocols; - sourceTree = ""; - }; - 46C3B7DA29E5E80B00F8F57C /* Protocols */ = { - isa = PBXGroup; - children = ( - 46C3B7D529E5E50500F8F57C /* LocationsListViewModelable.swift */, - ); - path = Protocols; - sourceTree = ""; - }; 46EB325029E1BBD1001D5EAF = { isa = PBXGroup; children = ( @@ -268,9 +288,11 @@ 46EB332F29E1CE1E001D5EAF /* Sources */ = { isa = PBXGroup; children = ( - 46C3B7C929E5CB8F00F8F57C /* Screens */, - 46C3B7C429E5BEE900F8F57C /* Coordinators */, 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */, + 0276C96029E5F5DC000B62AF /* Protocols */, + 46C3B7C429E5BEE900F8F57C /* Coordinators */, + 46C3B7C929E5CB8F00F8F57C /* Screens */, + 02031EC429E5FEB1003C108C /* View Controllers */, ); path = Sources; sourceTree = ""; @@ -478,11 +500,13 @@ files = ( 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */, 46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */, + 02031EC629E5FEE4003C108C /* BaseViewController.swift in Sources */, 46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */, + 02031EBF29E5F949003C108C /* LocationsAddViewModeling.swift in Sources */, 46C3B7DE29E5ED2E00F8F57C /* LocationsAddCoordinator.swift in Sources */, - 46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordinable.swift in Sources */, - 46C3B7D829E5E55000F8F57C /* LocationsListCoordinable.swift in Sources */, - 46C3B7D629E5E50500F8F57C /* LocationsListViewModelable.swift in Sources */, + 46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordination.swift in Sources */, + 46C3B7D829E5E55000F8F57C /* LocationsListCoordination.swift in Sources */, + 46C3B7D629E5E50500F8F57C /* LocationsListViewModeling.swift in Sources */, 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */, 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, -- 2.47.1 From 83ed85b75527b1d4f906e2ad75e209c06089d601 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 23:50:30 +0200 Subject: [PATCH 17/18] Renamed the LocationsService service and LocationsClient client in the Remote library as RemoteService and RemoteClient respectively. --- .../Libraries/Sources/Core/Routers/WindowRouter.swift | 4 ++++ .../Clients/{LocationsClient.swift => RemoteClient.swift} | 8 ++++---- .../Sources/Remote/Endpoints/GetLocationsEndpoint.swift | 2 +- .../Sources/Remote/Extensions/String+Constants.swift | 2 +- .../Libraries/Sources/Remote/Models/Location.swift | 2 +- .../{LocationsService.swift => RemoteService.swift} | 8 ++++---- 6 files changed, 15 insertions(+), 11 deletions(-) rename Apps/Locations/Libraries/Sources/Remote/Clients/{LocationsClient.swift => RemoteClient.swift} (93%) rename Apps/Locations/Libraries/Sources/Remote/Services/{LocationsService.swift => RemoteService.swift} (82%) diff --git a/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift b/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift index 6cbe31b..332ffac 100644 --- a/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift +++ b/Apps/Locations/Libraries/Sources/Core/Routers/WindowRouter.swift @@ -8,14 +8,18 @@ import UIKit +/// This class is responsible for populating the window of an application. public class WindowRouter: Router { // MARK: Properties + /// The window to set manually with a `UIViewController` view controller instance. private let window: UIWindow? // MARK: Initialisers + /// Initialise this router. + /// - Parameter window: A `UIWindow` window instance to be set manually. public init(window: UIWindow?) { self.window = window } diff --git a/Apps/Locations/Libraries/Sources/Remote/Clients/LocationsClient.swift b/Apps/Locations/Libraries/Sources/Remote/Clients/RemoteClient.swift similarity index 93% rename from Apps/Locations/Libraries/Sources/Remote/Clients/LocationsClient.swift rename to Apps/Locations/Libraries/Sources/Remote/Clients/RemoteClient.swift index 9ed37f2..cdb996c 100644 --- a/Apps/Locations/Libraries/Sources/Remote/Clients/LocationsClient.swift +++ b/Apps/Locations/Libraries/Sources/Remote/Clients/RemoteClient.swift @@ -1,6 +1,6 @@ // -// LocationsClient.swift -// Locations +// RemoteClient.swift +// Remote // // Created by Javier Cicchelli on 10/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. @@ -9,7 +9,7 @@ import APICore import Foundation -struct LocationsClient { +struct RemoteClient { // MARK: Properties @@ -27,7 +27,7 @@ struct LocationsClient { // MARK: - Client -extension LocationsClient: Client { +extension RemoteClient: Client { // MARK: Functions diff --git a/Apps/Locations/Libraries/Sources/Remote/Endpoints/GetLocationsEndpoint.swift b/Apps/Locations/Libraries/Sources/Remote/Endpoints/GetLocationsEndpoint.swift index 1755734..3d58fa8 100644 --- a/Apps/Locations/Libraries/Sources/Remote/Endpoints/GetLocationsEndpoint.swift +++ b/Apps/Locations/Libraries/Sources/Remote/Endpoints/GetLocationsEndpoint.swift @@ -1,6 +1,6 @@ // // GetLocationsEndpoint.swift -// Locations +// Remote // // Created by Javier Cicchelli on 10/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Apps/Locations/Libraries/Sources/Remote/Extensions/String+Constants.swift b/Apps/Locations/Libraries/Sources/Remote/Extensions/String+Constants.swift index 3eff3f9..a839fa5 100644 --- a/Apps/Locations/Libraries/Sources/Remote/Extensions/String+Constants.swift +++ b/Apps/Locations/Libraries/Sources/Remote/Extensions/String+Constants.swift @@ -1,6 +1,6 @@ // // String+Constants.swift -// Locations +// Remote // // Created by Javier Cicchelli on 10/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Apps/Locations/Libraries/Sources/Remote/Models/Location.swift b/Apps/Locations/Libraries/Sources/Remote/Models/Location.swift index 52567a0..0342f96 100644 --- a/Apps/Locations/Libraries/Sources/Remote/Models/Location.swift +++ b/Apps/Locations/Libraries/Sources/Remote/Models/Location.swift @@ -1,6 +1,6 @@ // // Location.swift -// Locations (Library) +// Remote // // Created by Javier Cicchelli on 10/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Apps/Locations/Libraries/Sources/Remote/Services/LocationsService.swift b/Apps/Locations/Libraries/Sources/Remote/Services/RemoteService.swift similarity index 82% rename from Apps/Locations/Libraries/Sources/Remote/Services/LocationsService.swift rename to Apps/Locations/Libraries/Sources/Remote/Services/RemoteService.swift index 2e102ab..b9bca95 100644 --- a/Apps/Locations/Libraries/Sources/Remote/Services/LocationsService.swift +++ b/Apps/Locations/Libraries/Sources/Remote/Services/RemoteService.swift @@ -1,6 +1,6 @@ // -// LocationsService.swift -// Locations +// RemoteService.swift +// Remote // // Created by Javier Cicchelli on 10/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. @@ -9,7 +9,7 @@ import APICore import Foundation -public struct LocationsService { +public struct RemoteService { // MARK: Properties @@ -18,7 +18,7 @@ public struct LocationsService { // MARK: Initialisers public init(configuration: URLSessionConfiguration = .default) { - self.client = LocationsClient(configuration: configuration) + self.client = RemoteClient(configuration: configuration) } // MARK: Functions -- 2.47.1 From cb5b4743f236222021b773f1701398a42734744e Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 11 Apr 2023 23:52:52 +0200 Subject: [PATCH 18/18] Implemented the "persistence" and "remote" properties of the DependencyService service in the Dependency+Keys extension. --- .../Extensions/DependencyService+Keys.swift | 35 +++++++++++++++++++ DeepLinking.xcodeproj/project.pbxproj | 12 +++++++ 2 files changed, 47 insertions(+) create mode 100644 Apps/Locations/Sources/Extensions/DependencyService+Keys.swift diff --git a/Apps/Locations/Sources/Extensions/DependencyService+Keys.swift b/Apps/Locations/Sources/Extensions/DependencyService+Keys.swift new file mode 100644 index 0000000..b1286d0 --- /dev/null +++ b/Apps/Locations/Sources/Extensions/DependencyService+Keys.swift @@ -0,0 +1,35 @@ +// +// DependencyService+Keys.swift +// Locations +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Dependency +import Persistence +import Remote + +// MARK: - DependencyService+Keys + +extension DependencyService { + var persistence: PersistenceService { + get { Self[PersistenceKey.self] } + set { Self[PersistenceKey.self] = newValue } + } + + var remote: RemoteService { + get { Self[RemoteKey.self] } + set { Self[RemoteKey.self] = newValue } + } +} + +// MARK: - Dependency keys + +struct PersistenceKey: DependencyKey { + static var currentValue: PersistenceService = .shared +} + +struct RemoteKey: DependencyKey { + static var currentValue: RemoteService = .init() +} diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj index b824d70..33b73ae 100644 --- a/DeepLinking.xcodeproj/project.pbxproj +++ b/DeepLinking.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 02031EBF29E5F949003C108C /* LocationsAddViewModeling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */; }; 02031EC629E5FEE4003C108C /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EC529E5FEE4003C108C /* BaseViewController.swift */; }; + 02031EC929E60B29003C108C /* DependencyService+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EC829E60B29003C108C /* DependencyService+Keys.swift */; }; 46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */; }; 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; }; 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; }; @@ -122,6 +123,7 @@ /* Begin PBXFileReference section */ 02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModeling.swift; sourceTree = ""; }; 02031EC529E5FEE4003C108C /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; + 02031EC829E60B29003C108C /* DependencyService+Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DependencyService+Keys.swift"; sourceTree = ""; }; 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = ""; }; 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; }; 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; }; @@ -164,6 +166,14 @@ path = "View Controllers"; sourceTree = ""; }; + 02031EC729E60ADB003C108C /* Extensions */ = { + isa = PBXGroup; + children = ( + 02031EC829E60B29003C108C /* DependencyService+Keys.swift */, + ); + path = Extensions; + sourceTree = ""; + }; 0276C96029E5F5DC000B62AF /* Protocols */ = { isa = PBXGroup; children = ( @@ -290,6 +300,7 @@ children = ( 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */, 0276C96029E5F5DC000B62AF /* Protocols */, + 02031EC729E60ADB003C108C /* Extensions */, 46C3B7C429E5BEE900F8F57C /* Coordinators */, 46C3B7C929E5CB8F00F8F57C /* Screens */, 02031EC429E5FEB1003C108C /* View Controllers */, @@ -508,6 +519,7 @@ 46C3B7D829E5E55000F8F57C /* LocationsListCoordination.swift in Sources */, 46C3B7D629E5E50500F8F57C /* LocationsListViewModeling.swift in Sources */, 46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */, + 02031EC929E60B29003C108C /* DependencyService+Keys.swift in Sources */, 46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */, 46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */, ); -- 2.47.1