diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift b/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIError.swift similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift rename to Modules/Data/Sources/NetworkAPI/Core/NetworkAPIError.swift diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift b/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIProtocol.swift similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift rename to Modules/Data/Sources/NetworkAPI/Core/NetworkAPIProtocol.swift diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift b/Modules/Data/Sources/NetworkAPI/Core/RequestConfiguration.swift similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift rename to Modules/Data/Sources/NetworkAPI/Core/RequestConfiguration.swift diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors/.gitkeep b/Modules/Data/Sources/NetworkAPI/Interceptors/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors/.gitkeep rename to Modules/Data/Sources/NetworkAPI/Interceptors/.gitkeep diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Models/.gitkeep b/Modules/Data/Sources/NetworkAPI/Models/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Models/.gitkeep rename to Modules/Data/Sources/NetworkAPI/Models/.gitkeep diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift b/Modules/Data/Sources/NetworkAPI/NetworkAPI.swift similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift rename to Modules/Data/Sources/NetworkAPI/NetworkAPI.swift diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep b/Modules/Data/Sources/NetworkAPI/RequestConfigurations/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep rename to Modules/Data/Sources/NetworkAPI/RequestConfigurations/.gitkeep diff --git a/{PROJECT_NAME}/Sources/Data/Repositories/.gitkeep b/Modules/Data/Sources/Repositories/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Data/Repositories/.gitkeep rename to Modules/Data/Sources/Repositories/.gitkeep diff --git a/{PROJECT_NAME}/Sources/Domain/Entities/.gitkeep b/Modules/Data/Tests/Resources/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Domain/Entities/.gitkeep rename to Modules/Data/Tests/Resources/.gitkeep diff --git a/{PROJECT_NAME}Tests/Sources/Dummy/Data/DummyNetworkModel.swift b/Modules/Data/Tests/Sources/Dummies/DummyNetworkModel.swift similarity index 100% rename from {PROJECT_NAME}Tests/Sources/Dummy/Data/DummyNetworkModel.swift rename to Modules/Data/Tests/Sources/Dummies/DummyNetworkModel.swift diff --git a/{PROJECT_NAME}Tests/Sources/Dummy/Data/DummyRequestConfiguration.swift b/Modules/Data/Tests/Sources/Dummies/DummyRequestConfiguration.swift similarity index 88% rename from {PROJECT_NAME}Tests/Sources/Dummy/Data/DummyRequestConfiguration.swift rename to Modules/Data/Tests/Sources/Dummies/DummyRequestConfiguration.swift index 1359b72b..b5a3d920 100644 --- a/{PROJECT_NAME}Tests/Sources/Dummy/Data/DummyRequestConfiguration.swift +++ b/Modules/Data/Tests/Sources/Dummies/DummyRequestConfiguration.swift @@ -4,7 +4,7 @@ import Alamofire -@testable import {PROJECT_NAME} +@testable import Data struct DummyRequestConfiguration: RequestConfiguration { @@ -24,6 +24,6 @@ extension DummyRequestConfiguration: RequestConfigurationStubable { } var path: String { - (try? url.asURL().path).string + (try? url.asURL().path) ?? "" } } diff --git a/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift b/Modules/Data/Tests/Sources/Specs/NetworkAPI/NetworkAPISpec.swift similarity index 98% rename from {PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift rename to Modules/Data/Tests/Sources/Specs/NetworkAPI/NetworkAPISpec.swift index d65996e5..adbb01a0 100644 --- a/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift +++ b/Modules/Data/Tests/Sources/Specs/NetworkAPI/NetworkAPISpec.swift @@ -5,7 +5,7 @@ import Nimble import Quick -@testable import {PROJECT_NAME} +@testable import Data final class NetworkAPISpec: AsyncSpec { diff --git a/{PROJECT_NAME}Tests/Sources/Utilities/NetworkStubber.swift b/Modules/Data/Tests/Sources/Utilities/NetworkStubber.swift similarity index 100% rename from {PROJECT_NAME}Tests/Sources/Utilities/NetworkStubber.swift rename to Modules/Data/Tests/Sources/Utilities/NetworkStubber.swift diff --git a/{PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep b/Modules/Domain/Sources/Entities/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep rename to Modules/Domain/Sources/Entities/.gitkeep diff --git a/Modules/Domain/Sources/Interfaces/.gitkeep b/Modules/Domain/Sources/Interfaces/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/{PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift b/Modules/Domain/Sources/UseCases/UseCaseFactoryProtocol.swift similarity index 100% rename from {PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift rename to Modules/Domain/Sources/UseCases/UseCaseFactoryProtocol.swift diff --git a/Modules/Domain/Tests/Resources/.gitkeep b/Modules/Domain/Tests/Resources/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Domain/Tests/Sources/Specs/DummySpec.swift b/Modules/Domain/Tests/Sources/Specs/DummySpec.swift new file mode 100644 index 00000000..5fc7cdc7 --- /dev/null +++ b/Modules/Domain/Tests/Sources/Specs/DummySpec.swift @@ -0,0 +1,23 @@ +// TODO: Remove this file + +import Nimble +import Quick + +@testable import Domain + +final class DummySpec: QuickSpec { + + override class func spec() { + + describe("A Dummy") { + + context("given a dummy message") { + let message = "Hello" + + it("equals Hello") { + expect(message) == "Hello" + } + } + } + } +} diff --git a/Project.swift b/Project.swift index 92d63a5e..c58b5cd2 100644 --- a/Project.swift +++ b/Project.swift @@ -6,6 +6,8 @@ let project = Project.project(name: "{PROJECT_NAME}", bundleId: "${PRODUCT_BUNDL extension Project { static func project(name: String, bundleId: String) -> Project { + let targets = Target.makeTargets(name: name, bundleId: bundleId) + return Project( name: name, organizationName: "Nimble", @@ -16,11 +18,7 @@ extension Project { settings: .settings( configurations: BuildConfiguration.allCases.map { $0.createConfiguration(projectName: name) } ), - targets: [ - .mainTarget(name: name, bundleId: bundleId), - .testsTarget(name: name, bundleId: bundleId), - .kifUITestsTarget(name: name, bundleId: bundleId), - ], + targets: targets, schemes: [ .productionScheme(name: name), .stagingScheme(name: name), diff --git a/Tuist/Interfaces/SwiftUI/Project/Podfile b/Tuist/Interfaces/SwiftUI/Project/Podfile index 7f9d0daf..e39e56c6 100644 --- a/Tuist/Interfaces/SwiftUI/Project/Podfile +++ b/Tuist/Interfaces/SwiftUI/Project/Podfile @@ -46,6 +46,26 @@ target '{PROJECT_NAME}' do end end +def data_dependencies + pod 'Alamofire' + pod 'JSONAPIMapper', :git => 'https://github.com/nimblehq/JSONMapper', :tag => '1.1.1' +end + +target 'Data' do + data_dependencies + + target 'DataTests' do + data_dependencies + testing_pods + end +end + +target 'Domain' do + target 'DomainTests' do + testing_pods + end +end + post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| diff --git a/Tuist/Interfaces/UIKit/Project/Podfile b/Tuist/Interfaces/UIKit/Project/Podfile index 653dc1a4..bb31ac75 100644 --- a/Tuist/Interfaces/UIKit/Project/Podfile +++ b/Tuist/Interfaces/UIKit/Project/Podfile @@ -15,10 +15,6 @@ target '{PROJECT_NAME}' do pod 'Kingfisher' pod 'SnapKit' - # Backend - pod 'Alamofire' - pod 'JSONAPIMapper', :git => 'https://github.com/nimblehq/JSONMapper', :tag => '1.1.1' - # Storage pod 'KeychainAccess' @@ -48,6 +44,25 @@ target '{PROJECT_NAME}' do end end +def data_dependencies + pod 'Alamofire' + pod 'JSONAPIMapper', :git => 'https://github.com/nimblehq/JSONMapper', :tag => '1.1.1' +end + +target 'Data' do + data_dependencies + target 'DataTests' do + data_dependencies + testing_pods + end +end + +target 'Domain' do + target 'DomainTests' do + testing_pods + end +end + post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| diff --git a/Tuist/ProjectDescriptionHelpers/Constant.swift b/Tuist/ProjectDescriptionHelpers/Constant.swift new file mode 100644 index 00000000..1d7de6e2 --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Constant.swift @@ -0,0 +1,15 @@ +// +// Constant.swift +// ProjectDescriptionHelpers +// +// Created by Phong on 22/10/2023. +// + +public enum Constant { + + static let plistsPath = "Configurations/Plists" + static let modulesRootPath = "Modules" + static let sourcesPath = "Sources" + static let resourcesPath = "Resources" + static let testsPath = "Tests" +} diff --git a/Tuist/ProjectDescriptionHelpers/Module.swift b/Tuist/ProjectDescriptionHelpers/Module.swift new file mode 100644 index 00000000..28a6a00d --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Module.swift @@ -0,0 +1,64 @@ +// +// Modules.swift +// ProjectDescriptionHelpers +// +// Created by Phong on 16/10/2023. +// + +import ProjectDescription + +public enum Module: CaseIterable { + + case domain + case data + + public var name: String { + switch self { + case .domain: + return "Domain" + case .data: + return "Data" + } + } + + public var dependencies: [TargetDependency] { + switch self { + case .domain: + return [] + case .data: + return [.target(name: Module.domain.name)] + } + } + + public var frameworkPath: String { + "\(Constant.modulesRootPath)/\(name)" + } + + public var sources: ProjectDescription.SourceFilesList { + ["\(frameworkPath)/\(Constant.sourcesPath)/**"] + } + + public var resources: ProjectDescription.ResourceFileElements { + [] + } + + public var testsSources: ProjectDescription.SourceFilesList { + ["\(frameworkPath)/\(Constant.testsPath)/**"] + } + + public var testsResources: ProjectDescription.ResourceFileElements { + [ + "\(frameworkPath)/\(Constant.testsPath)/**/.gitkeep", + "\(frameworkPath)/\(Constant.testsPath)/\(Constant.resourcesPath)/**" + ] + } + + + public func getBundleId(mainBundleId: String) -> String { + "\(mainBundleId).\(name)" + } + + public func getTestBundleId(mainBundleId: String) -> String { + "\(mainBundleId).\(name)\(Constant.testsPath)" + } +} diff --git a/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift index 8694c4c9..cda644fd 100644 --- a/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift @@ -5,14 +5,13 @@ extension Scheme { public static func productionScheme(name: String) -> Scheme { let debugConfigName = BuildConfiguration.debugProduction.name let releaseConfigName = BuildConfiguration.releaseProduction.name + let testModules = testSchemes(name) + return Scheme( name: name, shared: true, buildAction: .buildAction(targets: ["\(name)"]), - testAction: .targets( - ["\(name)Tests", "\(name)KIFUITests"], - configuration: debugConfigName - ), + testAction: .targets(testModules, configuration: debugConfigName), runAction: .runAction(configuration: debugConfigName), archiveAction: .archiveAction(configuration: releaseConfigName), profileAction: .profileAction(configuration: debugConfigName), @@ -23,14 +22,13 @@ extension Scheme { public static func stagingScheme(name: String) -> Scheme { let debugConfigName = BuildConfiguration.debugStaging.name let releaseConfigName = BuildConfiguration.releaseStaging.name + let testModules = testSchemes(name) + return Scheme( name: "\(name) Staging", shared: true, buildAction: .buildAction(targets: ["\(name)"]), - testAction: .targets( - ["\(name)Tests", "\(name)KIFUITests"], - configuration: debugConfigName - ), + testAction: .targets(testModules, configuration: debugConfigName), runAction: .runAction(configuration: debugConfigName), archiveAction: .archiveAction(configuration: releaseConfigName), profileAction: .profileAction(configuration: debugConfigName), @@ -39,12 +37,16 @@ extension Scheme { } public static func kifUITestsScheme(name: String) -> Scheme { - let debugConfigName = BuildConfiguration.debugStaging.name - let releaseConfigName = BuildConfiguration.releaseStaging.name return Scheme( name: "\(name)KIFUITests", shared: false, hidden: true ) } + + private static func testSchemes(_ name: String) -> [TestableTarget] { + var modules = Module.allCases.map { TestableTarget("\($0.name)\(Constant.testsPath)") } + modules.append(contentsOf: ["\(name)Tests", "\(name)KIFUITests"]) + return modules + } } diff --git a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift index 5e2158db..5d974c07 100644 --- a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift @@ -2,19 +2,41 @@ import ProjectDescription extension Target { - private static let plistsPath: String = "Configurations/Plists" + public static func makeTargets(name: String, bundleId: String) -> [Target] { + var targets: [Target] = [] - public static func mainTarget(name: String, bundleId: String) -> Target { + let frameworks = Module.allCases + .flatMap { Target.frameworkTargets(module: $0, bundleId: bundleId) } + + targets.append(contentsOf: frameworks) + + let mainTargets: [Target] = [ + .mainTarget(name: name, bundleId: bundleId), + .testsTarget(name: name, bundleId: bundleId), + .kifUITestsTarget(name: name, bundleId: bundleId) + ] + + targets.append(contentsOf: mainTargets) + + return targets + } +} + +// MARK: - Main Targets + +extension Target { + + fileprivate static func mainTarget(name: String, bundleId: String) -> Target { return Target( name: name, platform: .iOS, product: .app, bundleId: bundleId, deploymentTarget: .iOS( - targetVersion: "{TARGET_VERSION}", + targetVersion: "{TARGET_VERSION}", devices: [.iphone] ), - infoPlist: "\(name)/\(plistsPath)/Info.plist", + infoPlist: "\(name)/\(Constant.plistsPath)/Info.plist", sources: ["\(name)/Sources/**"], resources: [ "\(name)/Resources/**", @@ -26,18 +48,22 @@ extension Target { .swiftLintScript(), .swiftFormatLintScript(), .firebaseScript() + ], + dependencies: [ + .target(name: Module.data.name), + .target(name: Module.domain.name) ] ) } - public static func testsTarget(name: String, bundleId: String) -> Target { + fileprivate static func testsTarget(name: String, bundleId: String) -> Target { let targetName = "\(name)Tests" return Target( name: targetName, platform: .iOS, product: .unitTests, bundleId: bundleId, - infoPlist: "\(targetName)/\(plistsPath)/Info.plist", + infoPlist: "\(targetName)/\(Constant.plistsPath)/Info.plist", sources: ["\(targetName)/**"], resources: [ "\(targetName)/**/.gitkeep", // To include empty folders @@ -48,19 +74,51 @@ extension Target { ) } - public static func kifUITestsTarget(name: String, bundleId: String) -> Target { + fileprivate static func kifUITestsTarget(name: String, bundleId: String) -> Target { let targetName = "\(name)KIFUITests" return Target( name: targetName, platform: .iOS, product: .unitTests, bundleId: bundleId, - infoPlist: "\(targetName)/\(plistsPath)/Info.plist", + infoPlist: "\(targetName)/\(Constant.plistsPath)/Info.plist", sources: ["\(targetName)/**"], resources: [ "\(targetName)/**/.gitkeep", // To include empty folders - ], + ], dependencies: [.target(name: name)] ) } } + +// MARK: - Dependencies + +extension Target { + + fileprivate static func frameworkTargets(module: Module, bundleId: String) -> [Target] { + let framework = Target( + name: module.name, + platform: .iOS, + product: .framework, + bundleId: module.getBundleId(mainBundleId: bundleId), + deploymentTarget: .iOS( + targetVersion: "{TARGET_VERSION}", + devices: [.iphone] + ), + sources: module.sources, + resources: module.resources, + dependencies: module.dependencies + ) + + let testTarget = Target( + name: "\(module.name)\(Constant.testsPath)", + platform: .iOS, + product: .unitTests, + bundleId: module.getTestBundleId(mainBundleId: bundleId), + sources: module.testsSources, + resources: module.testsResources, + dependencies: [.target(name: module.name)] + ) + return [framework, testTarget] + } +} diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index 3e557829..110dde40 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -75,11 +75,6 @@ enum Constant { // MARK: - Device static let devices = ["iPhone 12 Pro Max"] - - // MARK: - Test - - static let testTarget: String = "\(projectName)Tests" - static let kifUITestTarget: String = "\(projectName)KIFUITests" } extension Constant { diff --git a/fastlane/Fastfile.swift b/fastlane/Fastfile.swift index 9da86063..5052b7ad 100644 --- a/fastlane/Fastfile.swift +++ b/fastlane/Fastfile.swift @@ -150,10 +150,6 @@ class Fastfile: LaneFile { desc("Build and Test project") Test.buildAndTest( environment: .staging, - targets: [ - Constant.testTarget, - Constant.kifUITestTarget - ], devices: Constant.devices ) } diff --git a/fastlane/Helpers/Test.swift b/fastlane/Helpers/Test.swift index bb4188d8..3ce377fa 100644 --- a/fastlane/Helpers/Test.swift +++ b/fastlane/Helpers/Test.swift @@ -10,13 +10,13 @@ enum Test { static func buildAndTest( environment: Constant.Environment, - targets: [String], + onlyTesting: [String] = [], devices: [String] ) { scan( scheme: .userDefined(environment.scheme), devices: .userDefined(devices), - onlyTesting: targets, + onlyTesting: onlyTesting, codeCoverage: .userDefined(true), outputDirectory: Constant.testOutputDirectoryPath, xcodebuildFormatter: "Pods/xcbeautify/xcbeautify",