diff --git a/Package.swift b/Package.swift index 2b04031..f90b624 100644 --- a/Package.swift +++ b/Package.swift @@ -14,15 +14,16 @@ let package = Package( .library( name: "SwiftLibs", targets: [ - "Coordinator", - "Core" + "Coordination", + "Core", + "Dependencies" ] ), ], dependencies: [], targets: [ .target( - name: "Coordinator", + name: "Coordination", dependencies: [], exclude: excludePlatforms ), @@ -30,12 +31,16 @@ let package = Package( name: "Core", dependencies: [] ), + .target( + name: "Dependencies", + dependencies: [] + ), .testTarget( - name: "CoordinatorTests", + name: "CoordinationTests", dependencies: [ - "Coordinator" + "Coordination" ], - path: "Tests/Coordinator", + path: "Tests/Coordination", exclude: excludePlatforms ), .testTarget( @@ -45,6 +50,13 @@ let package = Package( ], path: "Tests/Core" ), + .testTarget( + name: "DependenciesTests", + dependencies: [ + "Dependencies" + ], + path: "Tests/Dependencies" + ), ] ) diff --git a/Sources/Coordinator/Platform/iOS/Routers/BaseNavigationRouter.swift b/Sources/Coordination/Platform/iOS/Routers/BaseNavigationRouter.swift similarity index 99% rename from Sources/Coordinator/Platform/iOS/Routers/BaseNavigationRouter.swift rename to Sources/Coordination/Platform/iOS/Routers/BaseNavigationRouter.swift index 5d6ad45..38bd76b 100644 --- a/Sources/Coordinator/Platform/iOS/Routers/BaseNavigationRouter.swift +++ b/Sources/Coordination/Platform/iOS/Routers/BaseNavigationRouter.swift @@ -1,6 +1,6 @@ // // BaseNavigationRouter.swift -// Coordinator +// Coordination // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Sources/Coordinator/Platform/iOS/Routers/ModalNavigationRouter.swift b/Sources/Coordination/Platform/iOS/Routers/ModalNavigationRouter.swift similarity index 99% rename from Sources/Coordinator/Platform/iOS/Routers/ModalNavigationRouter.swift rename to Sources/Coordination/Platform/iOS/Routers/ModalNavigationRouter.swift index 286167c..cf7cc1b 100644 --- a/Sources/Coordinator/Platform/iOS/Routers/ModalNavigationRouter.swift +++ b/Sources/Coordination/Platform/iOS/Routers/ModalNavigationRouter.swift @@ -1,6 +1,6 @@ // // ModalNavigationRouter.swift -// Coordinator +// Coordination // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Sources/Coordinator/Platform/iOS/Routers/PushNavigationRouter.swift b/Sources/Coordination/Platform/iOS/Routers/PushNavigationRouter.swift similarity index 99% rename from Sources/Coordinator/Platform/iOS/Routers/PushNavigationRouter.swift rename to Sources/Coordination/Platform/iOS/Routers/PushNavigationRouter.swift index ab2bc6e..3873115 100644 --- a/Sources/Coordinator/Platform/iOS/Routers/PushNavigationRouter.swift +++ b/Sources/Coordination/Platform/iOS/Routers/PushNavigationRouter.swift @@ -1,6 +1,6 @@ // // PushNavigationRouter.swift -// Coordinator +// Coordination // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Sources/Coordinator/Platform/iOS/Routers/WindowRouter.swift b/Sources/Coordination/Platform/iOS/Routers/WindowRouter.swift similarity index 98% rename from Sources/Coordinator/Platform/iOS/Routers/WindowRouter.swift rename to Sources/Coordination/Platform/iOS/Routers/WindowRouter.swift index 92a2dd3..a59a402 100644 --- a/Sources/Coordinator/Platform/iOS/Routers/WindowRouter.swift +++ b/Sources/Coordination/Platform/iOS/Routers/WindowRouter.swift @@ -1,6 +1,6 @@ // // WindowRouter.swift -// Coordinator +// Coordination // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Sources/Coordinator/Protocols/Coordinator.swift b/Sources/Coordination/Protocols/Coordinator.swift similarity index 99% rename from Sources/Coordinator/Protocols/Coordinator.swift rename to Sources/Coordination/Protocols/Coordinator.swift index 6fdd227..ae4a687 100644 --- a/Sources/Coordinator/Protocols/Coordinator.swift +++ b/Sources/Coordination/Protocols/Coordinator.swift @@ -1,6 +1,6 @@ // // Coordinator.swift -// Coordinator +// Coordination // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. diff --git a/Sources/Coordinator/Protocols/Router.swift b/Sources/Coordination/Protocols/Router.swift similarity index 97% rename from Sources/Coordinator/Protocols/Router.swift rename to Sources/Coordination/Protocols/Router.swift index 48a89ea..cdbc960 100644 --- a/Sources/Coordinator/Protocols/Router.swift +++ b/Sources/Coordination/Protocols/Router.swift @@ -1,6 +1,6 @@ // // Router.swift -// Coordinator +// Coordination // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. @@ -13,7 +13,7 @@ import UIKit /// This protocol defines how view controllers will be shown and dismissed. public protocol Router: AnyObject { - // MARK: Typealiases + // MARK: Type aliases typealias OnDismissedClosure = () -> Void diff --git a/Sources/Dependencies/Property Wrappers/Dependency.swift b/Sources/Dependencies/Property Wrappers/Dependency.swift new file mode 100644 index 0000000..ebe96fe --- /dev/null +++ b/Sources/Dependencies/Property Wrappers/Dependency.swift @@ -0,0 +1,31 @@ +// +// Dependency.swift +// Dependencies +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +/// This property wrapper provides a direct connection to the `DependencyService` service. +@propertyWrapper +public struct Dependency { + + // MARK: Properties + + private let keyPath: WritableKeyPath + + /// This property allows direct read/write access to a defined dependency attached to a selected key path. + public var wrappedValue: D { + get { DependencyService[keyPath] } + set { DependencyService[keyPath] = newValue } + } + + // MARK: Initialisers + + /// Initialise the property wrapper by setting a key path to a defined dependency. + /// - Parameter keyPath: A key path to a defined dependency in the `DependencyService` service. + public init(_ keyPath: WritableKeyPath) { + self.keyPath = keyPath + } + +} diff --git a/Sources/Dependencies/Protocols/DependencyKey.swift b/Sources/Dependencies/Protocols/DependencyKey.swift new file mode 100644 index 0000000..8afd500 --- /dev/null +++ b/Sources/Dependencies/Protocols/DependencyKey.swift @@ -0,0 +1,22 @@ +// +// DependencyKey.swift +// Dependencies +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +/// This protocol defines a key to use in the dependency service. +public protocol DependencyKey { + + // MARK: Associated types + + /// The associated type representing the type of the dependency key's value. + associatedtype Value + + // MARK: Properties + + /// The default value for the dependency key. + static var currentValue: Value { get set } + +} diff --git a/Sources/Dependencies/Services/DependencyService.swift b/Sources/Dependencies/Services/DependencyService.swift new file mode 100644 index 0000000..3915ce8 --- /dev/null +++ b/Sources/Dependencies/Services/DependencyService.swift @@ -0,0 +1,28 @@ +// +// DependencyService.swift +// Dependencies +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +/// This service provide write/read access to the injected dependencies. +public struct DependencyService { + + // MARK: Properties + + private static var current = DependencyService() + + // MARK: Subscripts + + public static subscript(key: DK.Type) -> DK.Value { + get { key.currentValue } + set { key.currentValue = newValue } + } + + public static subscript(_ keyPath: WritableKeyPath) -> D { + get { current[keyPath: keyPath] } + set { current[keyPath: keyPath] = newValue } + } + +} diff --git a/Tests/Coordinator/Platform/iOS/Helpers/TestCoordinators.swift b/Tests/Coordination/Platform/iOS/Helpers/TestCoordinators.swift similarity index 97% rename from Tests/Coordinator/Platform/iOS/Helpers/TestCoordinators.swift rename to Tests/Coordination/Platform/iOS/Helpers/TestCoordinators.swift index 7b1eb6f..c68fef0 100644 --- a/Tests/Coordinator/Platform/iOS/Helpers/TestCoordinators.swift +++ b/Tests/Coordination/Platform/iOS/Helpers/TestCoordinators.swift @@ -1,12 +1,12 @@ // // TestCoordinators.swift -// CoordinatorTests +// CoordinationTests // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. // -import Coordinator +import Coordination import UIKit // MARK: - Test coordinators diff --git a/Tests/Coordinator/Platform/iOS/Protocols/CoordinatorTests.swift b/Tests/Coordination/Platform/iOS/Protocols/CoordinatorTests.swift similarity index 99% rename from Tests/Coordinator/Platform/iOS/Protocols/CoordinatorTests.swift rename to Tests/Coordination/Platform/iOS/Protocols/CoordinatorTests.swift index 9c459e8..1e7260b 100644 --- a/Tests/Coordinator/Platform/iOS/Protocols/CoordinatorTests.swift +++ b/Tests/Coordination/Platform/iOS/Protocols/CoordinatorTests.swift @@ -1,12 +1,12 @@ // // CoordinatorTests.swift -// CoordinatorTests +// CoordinationTests // // Created by Javier Cicchelli on 11/04/2023. // Copyright © 2023 Röck+Cöde. All rights reserved. // -import Coordinator +import Coordination import UIKit import XCTest diff --git a/Tests/Dependencies/Helpers/TestServices.swift b/Tests/Dependencies/Helpers/TestServices.swift new file mode 100644 index 0000000..c5d28ad --- /dev/null +++ b/Tests/Dependencies/Helpers/TestServices.swift @@ -0,0 +1,34 @@ +// +// TestServices.swift +// DependenciesTests +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Dependencies + +// MARK: - Protocols + +protocol TestService {} + +// MARK: - Services + +struct SomeService: TestService, Equatable {} +struct SomeOtherService: TestService, Equatable {} + +// MARK: - DependencyKey + +struct TestServiceKey: DependencyKey { + static var currentValue: TestService = SomeService() +} + +// MARK: - DependencyService+Keys + +extension DependencyService { + var testService: TestService { + get { Self[TestServiceKey.self] } + set { Self[TestServiceKey.self] = newValue } + } +} + diff --git a/Tests/Dependencies/Property Wrappers/DependencyTests.swift b/Tests/Dependencies/Property Wrappers/DependencyTests.swift new file mode 100644 index 0000000..ce0e8fb --- /dev/null +++ b/Tests/Dependencies/Property Wrappers/DependencyTests.swift @@ -0,0 +1,73 @@ +// +// DependencyTests.swift +// DependenciesTests +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Dependencies +import XCTest + +final class DependencyTests: XCTestCase { + + // MARK: Properties + + private var subject: TestSubject! + + // MARK: Setup + + override func setUp() { + DependencyService[\.testService] = SomeService() + } + + // MARK: Tests + + func test_readTestService() { + // GIVEN + subject = .init() + + // WHEN + let service = subject.testService + + // THEN + XCTAssertNotNil(service) + XCTAssert(service is SomeService) + } + + func test_writeDependencyKey() async throws { + // GIVEN + subject = .init() + + subject.testService = SomeOtherService() + + // WHEN + let service = DependencyService[\.testService] + + // THEN + XCTAssertNotNil(service) + XCTAssert(service is SomeOtherService) + } + + func test_writeDependencyKeyTwice() async throws { + // GIVEN + subject = .init() + + subject.testService = SomeOtherService() + subject.testService = SomeService() + + // WHEN + let service = DependencyService[\.testService] + + // THEN + XCTAssertNotNil(service) + XCTAssert(service is SomeService) + } + +} + +// MARK: - TestSubject + +private struct TestSubject { + @Dependency(\.testService) var testService +} diff --git a/Tests/Dependencies/Services/DependencyServiceTests.swift b/Tests/Dependencies/Services/DependencyServiceTests.swift new file mode 100644 index 0000000..48bb5bc --- /dev/null +++ b/Tests/Dependencies/Services/DependencyServiceTests.swift @@ -0,0 +1,57 @@ +// +// DependencyServiceTests.swift +// DependenciesTests +// +// Created by Javier Cicchelli on 11/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import Dependencies +import XCTest + +final class DependencyServiceTests: XCTestCase { + + // MARK: Setup + + override func setUp() { + DependencyService[\.testService] = SomeService() + } + + // MARK: Tests + + func test_readDependencyKey() async throws { + // GIVEN + // WHEN + let service = DependencyService[\.testService] + + // THEN + XCTAssertNotNil(service) + XCTAssert(service is SomeService) + } + + func test_writeDependencyKey() async throws { + // GIVEN + DependencyService[\.testService] = SomeOtherService() + + // WHEN + let service = DependencyService[\.testService] + + // THEN + XCTAssertNotNil(service) + XCTAssert(service is SomeOtherService) + } + + func test_writeDependencyKeyTwice() async throws { + // GIVEN + DependencyService[\.testService] = SomeOtherService() + DependencyService[\.testService] = SomeService() + + // WHEN + let service = DependencyService[\.testService] + + // THEN + XCTAssertNotNil(service) + XCTAssert(service is SomeService) + } + +}