[Setup] App architecture (#9)
This PR contains the work done to put in the app an MVVM-C architecture plus other small fixes in the `Library` libraries. To give further details about the work done: - [x] remove the `SceneDelegate` delegate; - [x] implemented the `WindowRouter`, `PushNavigationRouter`, and `ModalNavigationRouter` routers in the `Core` library; - [x] defined the `LocationsAddCoordination` and `LocationsListCoordination` protocols; - [x] defined the `LocationsAddViewModeling` and `LocationsListViewModeling` protocols; - [x] implemented the `LocationsListCoordinator` and `LocationsAddCoordinator` coordinators; - [x] implemented the `LocationsAddViewController` view controller and `LocationsAddViewModel` view model; - [x] implemented the `LocationsListViewController` view controller and `LocationsListViewModel` view model; - [x] implemented the `BaseViewController` base view controller; - [x] implemented the `persistence` and `remote` properties in the `DependencyService+Keys` extension. Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Reviewed-on: rock-n-code/deep-linking-assignment#9
This commit is contained in:
parent
d51cc97aa4
commit
43c156a2c3
@ -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 }
|
|
||||||
|
|
||||||
}
|
|
@ -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 }
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// WindowRouter.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 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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...
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// LocationsClient.swift
|
// RemoteClient.swift
|
||||||
// Locations
|
// Remote
|
||||||
//
|
//
|
||||||
// Created by Javier Cicchelli on 10/04/2023.
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
@ -9,7 +9,7 @@
|
|||||||
import APICore
|
import APICore
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct LocationsClient {
|
struct RemoteClient {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ struct LocationsClient {
|
|||||||
|
|
||||||
// MARK: - Client
|
// MARK: - Client
|
||||||
|
|
||||||
extension LocationsClient: Client {
|
extension RemoteClient: Client {
|
||||||
|
|
||||||
// MARK: Functions
|
// MARK: Functions
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// GetLocationsEndpoint.swift
|
// GetLocationsEndpoint.swift
|
||||||
// Locations
|
// Remote
|
||||||
//
|
//
|
||||||
// Created by Javier Cicchelli on 10/04/2023.
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// String+Constants.swift
|
// String+Constants.swift
|
||||||
// Locations
|
// Remote
|
||||||
//
|
//
|
||||||
// Created by Javier Cicchelli on 10/04/2023.
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// Location.swift
|
// Location.swift
|
||||||
// Locations (Library)
|
// Remote
|
||||||
//
|
//
|
||||||
// Created by Javier Cicchelli on 10/04/2023.
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// LocationsService.swift
|
// RemoteService.swift
|
||||||
// Locations
|
// Remote
|
||||||
//
|
//
|
||||||
// Created by Javier Cicchelli on 10/04/2023.
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
@ -9,7 +9,7 @@
|
|||||||
import APICore
|
import APICore
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct LocationsService {
|
public struct RemoteService {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ public struct LocationsService {
|
|||||||
// MARK: Initialisers
|
// MARK: Initialisers
|
||||||
|
|
||||||
public init(configuration: URLSessionConfiguration = .default) {
|
public init(configuration: URLSessionConfiguration = .default) {
|
||||||
self.client = LocationsClient(configuration: configuration)
|
self.client = RemoteClient(configuration: configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Functions
|
// MARK: Functions
|
@ -1,23 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict/>
|
||||||
<key>UIApplicationSceneManifest</key>
|
|
||||||
<dict>
|
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
|
||||||
<false/>
|
|
||||||
<key>UISceneConfigurations</key>
|
|
||||||
<dict>
|
|
||||||
<key>UIWindowSceneSessionRoleApplication</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UISceneConfigurationName</key>
|
|
||||||
<string>Default Configuration</string>
|
|
||||||
<key>UISceneDelegateClassName</key>
|
|
||||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -6,40 +6,31 @@
|
|||||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Core
|
||||||
import UIKit
|
import UIKit
|
||||||
import CoreData
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
func application(
|
func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
// Override point for customization after application launch.
|
coordinator.present(animated: false, onDismiss: nil)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UISceneSession Lifecycle
|
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||||
|
// Save changes in the application's managed object context when the application transitions to the background.
|
||||||
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<UISceneSession>
|
|
||||||
) {
|
|
||||||
// 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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// 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: Coordinator {
|
||||||
|
|
||||||
|
// 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: LocationsAddViewModel(coordinator: self)
|
||||||
|
),
|
||||||
|
animated: animated,
|
||||||
|
onDismiss: onDismiss
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - LocationsAddCoordination
|
||||||
|
|
||||||
|
extension LocationsAddCoordinator: LocationsAddCoordination {}
|
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
private var viewController: UIViewController?
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
init(router: Router) {
|
||||||
|
self.router = router
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Coordinator
|
||||||
|
|
||||||
|
func present(animated: Bool, onDismiss: (() -> Void)?) {
|
||||||
|
let navigationController = UINavigationController(rootViewController: LocationsListViewController(
|
||||||
|
viewModel: LocationsListViewModel(coordinator: self)
|
||||||
|
))
|
||||||
|
|
||||||
|
viewController = navigationController
|
||||||
|
|
||||||
|
router.present(
|
||||||
|
navigationController,
|
||||||
|
animated: animated,
|
||||||
|
onDismiss: onDismiss
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - LocationsListCoordination
|
||||||
|
|
||||||
|
extension LocationsListCoordinator: LocationsListCoordination {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func openAddLocation() {
|
||||||
|
guard let viewController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
present(
|
||||||
|
child: LocationsAddCoordinator(
|
||||||
|
router: ModalNavigationRouter(parentViewController: viewController)
|
||||||
|
),
|
||||||
|
animated: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
// LocationsAddCoordination.swift
|
||||||
|
// Locations
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 11/04/2023.
|
||||||
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
protocol LocationsAddCoordination: AnyObject {}
|
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// LocationsListCoordination.swift
|
||||||
|
// Locations
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 11/04/2023.
|
||||||
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
protocol LocationsListCoordination: AnyObject {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func openAddLocation()
|
||||||
|
|
||||||
|
}
|
@ -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 }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// LocationsListViewModeling.swift
|
||||||
|
// Locations
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 11/04/2023.
|
||||||
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
protocol LocationsListViewModeling: AnyObject {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var coordinator: LocationsListCoordination? { get set }
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func openAddLocation()
|
||||||
|
|
||||||
|
}
|
@ -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.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// 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: BaseViewController {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var viewModel: LocationsAddViewModeling
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
init(viewModel: LocationsAddViewModeling) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UIViewController
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
title = "Location Add"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
weak var coordinator: LocationsAddCoordination?
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
init(coordinator: LocationsAddCoordination) {
|
||||||
|
self.coordinator = coordinator
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - LocationsAddViewModeling
|
||||||
|
|
||||||
|
extension LocationsAddViewModel: LocationsAddViewModeling {}
|
@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// 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: BaseViewController {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var viewModel: LocationsListViewModeling
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
init(viewModel: LocationsListViewModeling) {
|
||||||
|
self.viewModel = viewModel
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UIViewController
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||||
|
title: "Add",
|
||||||
|
style: .plain,
|
||||||
|
target: self,
|
||||||
|
action: #selector(addLocationPressed)
|
||||||
|
)
|
||||||
|
title = "Locations"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension LocationsListViewController {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
@objc func addLocationPressed() {
|
||||||
|
viewModel.openAddLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
weak var coordinator: LocationsListCoordination?
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
init(coordinator: LocationsListCoordination) {
|
||||||
|
self.coordinator = coordinator
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - LocationsListViewModeling
|
||||||
|
|
||||||
|
extension LocationsListViewModel: LocationsListViewModeling {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func openAddLocation() {
|
||||||
|
coordinator?.openAddLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -7,9 +7,19 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
||||||
|
46C3B7D129E5D06D00F8F57C /* LocationsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.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 */; };
|
46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331A29E1CE04001D5EAF /* AppDelegate.swift */; };
|
||||||
46EB331D29E1CE04001D5EAF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331C29E1CE04001D5EAF /* SceneDelegate.swift */; };
|
46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */; };
|
||||||
46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46EB331E29E1CE04001D5EAF /* ViewController.swift */; };
|
|
||||||
46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; };
|
46EB332729E1CE05001D5EAF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332629E1CE05001D5EAF /* Assets.xcassets */; };
|
||||||
46EB332A29E1CE05001D5EAF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332829E1CE05001D5EAF /* LaunchScreen.storyboard */; };
|
46EB332A29E1CE05001D5EAF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46EB332829E1CE05001D5EAF /* LaunchScreen.storyboard */; };
|
||||||
46EB334429E1D1EC001D5EAF /* Libraries in Frameworks */ = {isa = PBXBuildFile; productRef = 46EB334329E1D1EC001D5EAF /* Libraries */; };
|
46EB334429E1D1EC001D5EAF /* Libraries in Frameworks */ = {isa = PBXBuildFile; productRef = 46EB334329E1D1EC001D5EAF /* Libraries */; };
|
||||||
@ -111,11 +121,21 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModeling.swift; sourceTree = "<group>"; };
|
||||||
|
02031EC529E5FEE4003C108C /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
|
||||||
|
02031EC829E60B29003C108C /* DependencyService+Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DependencyService+Keys.swift"; sourceTree = "<group>"; };
|
||||||
|
46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewController.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7D529E5E50500F8F57C /* LocationsListViewModeling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModeling.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7D729E5E55000F8F57C /* LocationsListCoordination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordination.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordination.swift; sourceTree = "<group>"; };
|
||||||
|
46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddCoordinator.swift; sourceTree = "<group>"; };
|
||||||
46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = "<group>"; };
|
46EB325829E1BD5C001D5EAF /* Wikipedia.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Wikipedia.xcodeproj; path = Wikipedia/Wikipedia.xcodeproj; sourceTree = "<group>"; };
|
||||||
46EB331829E1CE04001D5EAF /* Locations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Locations.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
46EB331A29E1CE04001D5EAF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
46EB331C29E1CE04001D5EAF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewController.swift; sourceTree = "<group>"; };
|
||||||
46EB331E29E1CE04001D5EAF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
|
||||||
46EB332629E1CE05001D5EAF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
46EB332629E1CE05001D5EAF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
46EB332929E1CE05001D5EAF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
46EB332929E1CE05001D5EAF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
46EB332B29E1CE05001D5EAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
46EB332B29E1CE05001D5EAF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
@ -138,6 +158,85 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
02031EC429E5FEB1003C108C /* View Controllers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
02031EC529E5FEE4003C108C /* BaseViewController.swift */,
|
||||||
|
);
|
||||||
|
path = "View Controllers";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
02031EC729E60ADB003C108C /* Extensions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
02031EC829E60B29003C108C /* DependencyService+Keys.swift */,
|
||||||
|
);
|
||||||
|
path = Extensions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
0276C96029E5F5DC000B62AF /* Protocols */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0276C96229E5F5ED000B62AF /* Coordination */,
|
||||||
|
0276C96129E5F5E5000B62AF /* ViewModeling */,
|
||||||
|
);
|
||||||
|
path = Protocols;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
0276C96129E5F5E5000B62AF /* ViewModeling */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
02031EBE29E5F949003C108C /* LocationsAddViewModeling.swift */,
|
||||||
|
46C3B7D529E5E50500F8F57C /* LocationsListViewModeling.swift */,
|
||||||
|
);
|
||||||
|
path = ViewModeling;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
0276C96229E5F5ED000B62AF /* Coordination */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
46C3B7DB29E5ED2300F8F57C /* LocationsAddCoordination.swift */,
|
||||||
|
46C3B7D729E5E55000F8F57C /* LocationsListCoordination.swift */,
|
||||||
|
);
|
||||||
|
path = Coordination;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
46C3B7C429E5BEE900F8F57C /* Coordinators */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
46C3B7DD29E5ED2E00F8F57C /* LocationsAddCoordinator.swift */,
|
||||||
|
46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */,
|
||||||
|
);
|
||||||
|
path = Coordinators;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
46C3B7C929E5CB8F00F8F57C /* Screens */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
46C3B7CD29E5CFCD00F8F57C /* LocationsAdd */,
|
||||||
|
46C3B7CC29E5CFBB00F8F57C /* LocationsList */,
|
||||||
|
);
|
||||||
|
path = Screens;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
46C3B7CC29E5CFBB00F8F57C /* LocationsList */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
46EB331E29E1CE04001D5EAF /* LocationsListViewController.swift */,
|
||||||
|
46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = LocationsList;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
46C3B7CD29E5CFCD00F8F57C /* LocationsAdd */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
46C3B7D029E5D06D00F8F57C /* LocationsAddViewController.swift */,
|
||||||
|
46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = LocationsAdd;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
46EB325029E1BBD1001D5EAF = {
|
46EB325029E1BBD1001D5EAF = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -200,8 +299,11 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
46EB331A29E1CE04001D5EAF /* AppDelegate.swift */,
|
46EB331A29E1CE04001D5EAF /* AppDelegate.swift */,
|
||||||
46EB331C29E1CE04001D5EAF /* SceneDelegate.swift */,
|
0276C96029E5F5DC000B62AF /* Protocols */,
|
||||||
46EB331E29E1CE04001D5EAF /* ViewController.swift */,
|
02031EC729E60ADB003C108C /* Extensions */,
|
||||||
|
46C3B7C429E5BEE900F8F57C /* Coordinators */,
|
||||||
|
46C3B7C929E5CB8F00F8F57C /* Screens */,
|
||||||
|
02031EC429E5FEB1003C108C /* View Controllers */,
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -407,9 +509,19 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
46EB331F29E1CE04001D5EAF /* ViewController.swift in Sources */,
|
46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */,
|
||||||
|
46EB331F29E1CE04001D5EAF /* LocationsListViewController.swift in Sources */,
|
||||||
|
02031EC629E5FEE4003C108C /* BaseViewController.swift in Sources */,
|
||||||
46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */,
|
46EB331B29E1CE04001D5EAF /* AppDelegate.swift in Sources */,
|
||||||
46EB331D29E1CE04001D5EAF /* SceneDelegate.swift in Sources */,
|
02031EBF29E5F949003C108C /* LocationsAddViewModeling.swift in Sources */,
|
||||||
|
46C3B7DE29E5ED2E00F8F57C /* LocationsAddCoordinator.swift in Sources */,
|
||||||
|
46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordination.swift in Sources */,
|
||||||
|
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 */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user