From 32aa23b698a32d06ade1e6fc0fa101258891ddfa Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Thu, 5 Oct 2023 13:01:55 +0700 Subject: [PATCH 01/29] [#528] No time-out keychain --- fastlane/Helpers/Keychain.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Helpers/Keychain.swift b/fastlane/Helpers/Keychain.swift index 522a8704..f401b8ab 100644 --- a/fastlane/Helpers/Keychain.swift +++ b/fastlane/Helpers/Keychain.swift @@ -16,7 +16,7 @@ enum Keychain { password: Secret.keychainPassword, defaultKeychain: .userDefined(true), unlock: .userDefined(true), - timeout: 3600 + timeout: 0 ) } From b581840d5f24f20b637b8d3a2357da14f4966048 Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Thu, 5 Oct 2023 14:24:04 +0700 Subject: [PATCH 02/29] [#528] Make timeout 1.3 days --- fastlane/Helpers/Keychain.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Helpers/Keychain.swift b/fastlane/Helpers/Keychain.swift index f401b8ab..539e1eb5 100644 --- a/fastlane/Helpers/Keychain.swift +++ b/fastlane/Helpers/Keychain.swift @@ -16,7 +16,7 @@ enum Keychain { password: Secret.keychainPassword, defaultKeychain: .userDefined(true), unlock: .userDefined(true), - timeout: 0 + timeout: 115_200 ) } From d9d2f440aa1a8ba9462219cbd1a3a8ebe7db5526 Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Fri, 6 Oct 2023 11:06:29 +0700 Subject: [PATCH 03/29] [#530] Add SwiftUI file organization wiki --- .github/wiki/Standard-File-Organization.md | 74 +++++++++++++++++----- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/.github/wiki/Standard-File-Organization.md b/.github/wiki/Standard-File-Organization.md index 762f7cb9..c0677c91 100644 --- a/.github/wiki/Standard-File-Organization.md +++ b/.github/wiki/Standard-File-Organization.md @@ -2,6 +2,8 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS project’s file organization by following this below structure: +### Common + ``` . ├── README.md @@ -15,9 +17,7 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro │   │   └── LaunchScreen │   └── Sources │   ├── Application -│   │   ├── AppDelegate.swift -│   │   ├── Application.swift -│   │   └── SceneDelegate.swift +│   │   └── Varies by UI Interface │   ├── Constants │   │   ├── Constants+API.swift │   │   └── Constants.swift @@ -49,18 +49,7 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro │   │   ├── Authentication │   │   └── User │   ├── Presentation -│   │   ├── Modules -│   │   │   ├── Home -│   │   │   └── Login -│   │   ├── Navigator -│   │   │   ├── Navigator+Scene.swift -│   │   │   ├── Navigator+Transition.swift -│   │   │   └── Navigator.swift -│   │   └── Views -│   │   ├── Button -│   │   ├── CollectionView -│   │   ├── TextField -│   │   └── Transition +│   │   └── Varies by UI Interface │   └── Supports │   ├── Builder │   │   └── Builder.swift @@ -91,6 +80,7 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro │   │   └── HomeViewModelProtocolMock+Equatable.swift │   ├── Specs │   │   ├── Data +│   │   │   └── Datasources │   │   │   └── Repositories │   │   ├── Domain │   │   │   └── UseCases @@ -123,6 +113,60 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro    └── KIF+Swift.swift ``` +### SwiftUI + +``` +. +└── {ProjectName} +    └── Sources +    ├── Application +    │   ├── {ProjectName}App.swift +    │   └── AppDelegate.swift +    └── Presentation +       ├── Models +            │   └── ProductUIModel.swift +       ├── Coordinators +            │   └── AppCoordinator.swift +       ├── Modules +       │   ├── Home +       │   └── Login +       ├── Styles +       │   └── RoundedButtonStyle.swift +       ├── ViewModifiers +       │   └── View+PrimaryNavigationBar.swift +       ├── Views +       │   └── SearchBarView.swift +       └── ViewIds +          └── ViewId.swift +``` + +### UIKit + +``` +. +└── {ProjectName} +    └── Sources +    ├── Application +    │   ├── AppDelegate.swift +    │   ├── Application.swift +    │   └── SceneDelegate.swift +    └── Presentation +       ├── Modules +       │   ├── Home +       │   └── Login +       ├── Navigator +       │   ├── Navigator+Scene.swift +       │   ├── Navigator+Transition.swift +       │   └── Navigator.swift +       └── Views +       │ ├── Button +       │ ├── CollectionView +       │ ├── TextField +       │ └── Transition +       └── ViewIds +          └── ViewId.swift +``` + ## README.md `README.md` introduces the overview of the project, for example: From 5304d621cabfdd5a93a49bf7f6be9aca0d8a3817 Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Fri, 6 Oct 2023 11:17:08 +0700 Subject: [PATCH 04/29] [#530] Fix build Rx --- .github/wiki/Standard-File-Organization.md | 2 -- Tuist/Interfaces/UIKit/Project/.sourcery.yml | 3 --- 2 files changed, 5 deletions(-) diff --git a/.github/wiki/Standard-File-Organization.md b/.github/wiki/Standard-File-Organization.md index c0677c91..6008ef78 100644 --- a/.github/wiki/Standard-File-Organization.md +++ b/.github/wiki/Standard-File-Organization.md @@ -55,10 +55,8 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro │   │   └── Builder.swift │   ├── Extensions │   │   ├── Foundation -│   │   ├── Rx │   │   └── UIKit │   └── Helpers -│   ├── Rx │   ├── Typealias │   └── UIKit ├── {ProjectName}Tests diff --git a/Tuist/Interfaces/UIKit/Project/.sourcery.yml b/Tuist/Interfaces/UIKit/Project/.sourcery.yml index 29469316..492262a4 100644 --- a/Tuist/Interfaces/UIKit/Project/.sourcery.yml +++ b/Tuist/Interfaces/UIKit/Project/.sourcery.yml @@ -7,6 +7,3 @@ output: args: autoMockableTestableImports: - {PROJECT_NAME} - autoMockableImports: - - RxSwift - - RxCocoa From 7962466de498bf2e344c96619b13f1d4f2c33f0c Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Fri, 6 Oct 2023 15:33:26 +0700 Subject: [PATCH 05/29] [#530] Fix build --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 2b20351d..1b8283e2 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,8 @@ gem "danger-swiftlint" gem "danger-xcode_summary" gem 'danger-swiftformat' gem 'danger-xcov' +# Fix issue with Cocoapods 13.0 when activesupport is 7.1.0 +gem 'activesupport', '~> 7.0.0', '>= 7.0.8' plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) From fa8b0d7d2647d9b941cfe3b16da8120ac5fbe8ca Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Mon, 9 Oct 2023 14:38:04 +0700 Subject: [PATCH 06/29] [#522] Use FIREBASE_SERVICE_ACCOUNT instead of FIREBASE_TOKEN --- .../project_workflows/deploy_production_firebase.yml | 11 +++++++++-- .github/project_workflows/deploy_staging_firebase.yml | 11 +++++++++-- .github/wiki/CodeMagic.md | 4 ++-- .github/wiki/Github-Actions.md | 4 ++-- .github/workflows/test_upload_build_to_firebase.yml | 11 +++++++++-- codemagic.yaml | 2 ++ fastlane/Constants/Secret.swift | 2 -- 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/.github/project_workflows/deploy_production_firebase.yml b/.github/project_workflows/deploy_production_firebase.yml index ad71aed9..23f71113 100644 --- a/.github/project_workflows/deploy_production_firebase.yml +++ b/.github/project_workflows/deploy_production_firebase.yml @@ -3,7 +3,7 @@ name: Deploy Production Build To Firebase # SECRETS needed: ### SSH_PRIVATE_KEY for Match Repo ### MATCH_PASS -### FIREBASE_TOKEN +### FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 on: push: @@ -50,6 +50,13 @@ jobs: touch .env echo $ENV | base64 --decode > .env + - name: Read Google Service Account + id: firebase_service_account + uses: timheuer/base64-to-file@v1.2 + with: + fileName: 'firebase_service_account.json' + encodedString: ${{ secrets.FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} + - name: Bundle install run: bundle install @@ -80,7 +87,7 @@ jobs: - name: Build Production App and Distribute to Firebase run: bundle exec fastlane buildProductionAndUploadToFirebase env: - FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.firebase_service_account.outputs.filePath }} - name: Upload Artifacts uses: actions/upload-artifact@v3 diff --git a/.github/project_workflows/deploy_staging_firebase.yml b/.github/project_workflows/deploy_staging_firebase.yml index fff0630a..ee517172 100644 --- a/.github/project_workflows/deploy_staging_firebase.yml +++ b/.github/project_workflows/deploy_staging_firebase.yml @@ -3,7 +3,7 @@ name: Deploy Staging Build To Firebase # SECRETS needed: ### SSH_PRIVATE_KEY for Match Repo ### MATCH_PASS -### FIREBASE_TOKEN +### FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 on: push: @@ -55,6 +55,13 @@ jobs: touch .env echo $ENV | base64 --decode > .env + - name: Read Google Service Account + id: firebase_service_account + uses: timheuer/base64-to-file@v1.2 + with: + fileName: 'firebase_service_account.json' + encodedString: ${{ secrets.FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} + - name: Bundle install # if: steps.bundleCache.outputs.cache-hit != 'true' run: bundle install @@ -86,7 +93,7 @@ jobs: - name: Build App and Distribute to Firebase run: bundle exec fastlane buildStagingAndUploadToFirebase env: - FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.firebase_service_account.outputs.filePath }} - name: Upload Artifacts uses: actions/upload-artifact@v3 diff --git a/.github/wiki/CodeMagic.md b/.github/wiki/CodeMagic.md index f12bc0c6..420988f4 100644 --- a/.github/wiki/CodeMagic.md +++ b/.github/wiki/CodeMagic.md @@ -33,7 +33,7 @@ Out of the box, the CodeMagic Template has the following workflows and steps: | MATCH_PASSWORD | The password is used to encrypt/decrypt the Match repository to store the distribution certificates and provisioning profiles. | | MATCH_SSH_KEY | The SSH private key is used for cloning the Match repository that contains your distribution certificates and provisioning. | | KEYCHAIN_PASSWORD | The password to access the keychain. | -| FIREBASE_CLI_TOKEN | [Firebase token](https://firebase.google.com/docs/cli#cli-ci-systems) for uploading build to Firebase Distributions and Analytics. | +| FIREBASE_SERVICE_ACCOUNT | [Google Service Firebase Account](https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane#service-acc-fastlane) for uploading build to Firebase Distributions and Analytics. | | APPSTORE_CONNECT_API_KEY | [App Store Connect API](https://docs.fastlane.tools/actions/app_store_connect_api_key/) for uploading build to TestFlight or App Store. It should be `base64` encoded. | | API_KEY_ID | The key identifier of your App Store Connect API key. | | ISSUER_ID | The issuer of your App Store Connect API key. | @@ -57,4 +57,4 @@ ROOT ├──... ``` -4. Push changes to SCM. \ No newline at end of file +4. Push changes to SCM. diff --git a/.github/wiki/Github-Actions.md b/.github/wiki/Github-Actions.md index 07693092..e96e33ad 100644 --- a/.github/wiki/Github-Actions.md +++ b/.github/wiki/Github-Actions.md @@ -53,7 +53,7 @@ Make sure the following secrets are set up. |SSH_PRIVATE_KEY |SSH key connected to a user with access to the match repo for check out the match repo. |- |✅ |✅ |✅ | |MATCH_PASS |Fastlane Match Passphrase for decrypting a match repository. |- |✅ |✅ |✅ | |APPSTORE_CONNECT_API_KEY|App Store Connect API https://docs.fastlane.tools/actions/app_store_connect_api_key/ for uploading build to TestFlight or App Store. Should be `base64` encoded.|- |- |- |✅ | -|FIREBASE_TOKEN |Firebase token https://firebase.google.com/docs/cli#cli-ci-systems for uploading build to Firebase Distributions and Analytics. |- |✅ |✅ |✅ For uploading dSYM to Crashlytics| +|FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64|Google Service Firebase Account https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane#service-acc-fastlane for uploading build to Firebase Distributions and Analytics. Should be `base64` encoded.|- |✅ |✅ |✅ For uploading dSYM to Crashlytics| ## Installation @@ -63,4 +63,4 @@ Make sure the following secrets are set up. - fastlane/Constants/Constants.rb 3. Get APPSTORE_CONNECT_API_KEY base64 from AuthKey file (.p8) with `cat AuthKey_ABCDEFGH.p8 | base64`. 4. Provide SECRETS noted in `yml` file in [Github Project's Setting](https://docs.github.com/en/actions/reference/encrypted-secrets) -4. Push changes to Github \ No newline at end of file +4. Push changes to Github diff --git a/.github/workflows/test_upload_build_to_firebase.yml b/.github/workflows/test_upload_build_to_firebase.yml index 8b0becfc..8aff8251 100644 --- a/.github/workflows/test_upload_build_to_firebase.yml +++ b/.github/workflows/test_upload_build_to_firebase.yml @@ -3,7 +3,7 @@ name: Test Upload Build to Firebase # SECRETS needed: ### SSH_PRIVATE_KEY for Match Repo ### MATCH_PASS -### FIREBASE_TOKEN +### FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 ### STAGING_FIREBASE_APP_ID ### TEAM_ID @@ -34,6 +34,13 @@ jobs: yarn global add firebase-tools echo "$(yarn global bin)" >> $GITHUB_PATH + - name: Read Google Service Account + id: firebase_service_account + uses: timheuer/base64-to-file@v1.2 + with: + fileName: 'firebase_service_account.json' + encodedString: ${{ secrets.FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} + - name: Bundle install run: bundle install @@ -67,7 +74,7 @@ jobs: - name: Build App and Distribute to Firebase run: bundle exec fastlane buildStagingAndUploadToFirebase env: - FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.firebase_service_account.outputs.filePath }} - name: Upload Artifacts uses: actions/upload-artifact@v3 diff --git a/codemagic.yaml b/codemagic.yaml index 58272b88..a38fd69f 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -7,6 +7,7 @@ workflows: - fastlane xcode: latest cocoapods: default + firebase_service_account: $FIREBASE_SERVICE_ACCOUNT cache: cache_paths: - $HOME/Library/Caches/CocoaPods @@ -71,6 +72,7 @@ workflows: - fastlane xcode: latest cocoapods: default + firebase_service_account: $FIREBASE_SERVICE_ACCOUNT cache: cache_paths: - $HOME/Library/Caches/CocoaPods diff --git a/fastlane/Constants/Secret.swift b/fastlane/Constants/Secret.swift index 9385dd07..12f37911 100644 --- a/fastlane/Constants/Secret.swift +++ b/fastlane/Constants/Secret.swift @@ -11,8 +11,6 @@ enum Secret { static let keychainPassword = EnvironmentParser.string(key: "KEYCHAIN_PASSWORD") - static let firebaseCLIToken = EnvironmentParser.string(key: "FIREBASE_TOKEN") - static let appstoreConnectAPIKey = EnvironmentParser.string(key: "APPSTORE_CONNECT_API_KEY") static let appStoreKeyIdKey = EnvironmentParser.string(key: "API_KEY_ID") From ce963cad2ea1f50e430d471019313abd5cdba461 Mon Sep 17 00:00:00 2001 From: Bliss Pisit Wetcha Date: Mon, 9 Oct 2023 15:44:28 +0700 Subject: [PATCH 07/29] [#522] Use FIREBASE_SERVICE_ACCOUNT instead of FIREBASE_TOKEN --- fastlane/Helpers/Distribution.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/fastlane/Helpers/Distribution.swift b/fastlane/Helpers/Distribution.swift index 9361dd4c..24548781 100644 --- a/fastlane/Helpers/Distribution.swift +++ b/fastlane/Helpers/Distribution.swift @@ -21,7 +21,6 @@ enum Distribution { app: .userDefined(environment.firebaseAppId), groups: .userDefined(groups), releaseNotes: .userDefined(releaseNotes), - firebaseCliToken: .userDefined(Secret.firebaseCLIToken), debug: .userDefined(true) ) } From 39e917cd9cc00612dfe01e74e0cc184a89e2c7dc Mon Sep 17 00:00:00 2001 From: David Date: Fri, 27 Oct 2023 16:16:28 +0700 Subject: [PATCH 08/29] [#482] Repace Podfile hard-code version with {TARGET_VERSION} --- Tuist/Interfaces/SwiftUI/Project/Podfile | 2 +- Tuist/Interfaces/UIKit/Project/Podfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tuist/Interfaces/SwiftUI/Project/Podfile b/Tuist/Interfaces/SwiftUI/Project/Podfile index 5c5a1e9a..7f9d0daf 100644 --- a/Tuist/Interfaces/SwiftUI/Project/Podfile +++ b/Tuist/Interfaces/SwiftUI/Project/Podfile @@ -1,4 +1,4 @@ -platform :ios, '14.0' +platform :ios, '{TARGET_VERSION}' use_frameworks! inhibit_all_warnings! diff --git a/Tuist/Interfaces/UIKit/Project/Podfile b/Tuist/Interfaces/UIKit/Project/Podfile index 1d90e584..653dc1a4 100644 --- a/Tuist/Interfaces/UIKit/Project/Podfile +++ b/Tuist/Interfaces/UIKit/Project/Podfile @@ -1,4 +1,4 @@ -platform :ios, '13.0' +platform :ios, '{TARGET_VERSION}' use_frameworks! inhibit_all_warnings! From 57834b8bd3d63a027cf2f8aa4bdb760249491ed4 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 27 Oct 2023 16:43:53 +0700 Subject: [PATCH 09/29] [#521] Update norio-nomura/action-swiftlint to latest version 3.2.1 --- .github/project_workflows/deploy_app_store.yml | 2 +- .github/project_workflows/deploy_production_firebase.yml | 2 +- .github/project_workflows/deploy_staging_firebase.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/project_workflows/deploy_app_store.yml b/.github/project_workflows/deploy_app_store.yml index a054675e..004d8447 100644 --- a/.github/project_workflows/deploy_app_store.yml +++ b/.github/project_workflows/deploy_app_store.yml @@ -27,7 +27,7 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.1.0 + uses: norio-nomura/action-swiftlint@3.2.1 with: args: --strict diff --git a/.github/project_workflows/deploy_production_firebase.yml b/.github/project_workflows/deploy_production_firebase.yml index ad71aed9..68078f3c 100644 --- a/.github/project_workflows/deploy_production_firebase.yml +++ b/.github/project_workflows/deploy_production_firebase.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.1.0 + uses: norio-nomura/action-swiftlint@3.2.1 with: args: --strict diff --git a/.github/project_workflows/deploy_staging_firebase.yml b/.github/project_workflows/deploy_staging_firebase.yml index fff0630a..67c706cd 100644 --- a/.github/project_workflows/deploy_staging_firebase.yml +++ b/.github/project_workflows/deploy_staging_firebase.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.1.0 + uses: norio-nomura/action-swiftlint@3.2.1 with: args: --strict From 10c83d96f3d6eb9a02f16675a4f5609445431bff Mon Sep 17 00:00:00 2001 From: David Date: Tue, 31 Oct 2023 09:33:16 +0700 Subject: [PATCH 10/29] [#521] Update norio-nomura/action-swiftlint to master branch --- .github/project_workflows/deploy_app_store.yml | 2 +- .github/project_workflows/deploy_production_firebase.yml | 2 +- .github/project_workflows/deploy_staging_firebase.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/project_workflows/deploy_app_store.yml b/.github/project_workflows/deploy_app_store.yml index 004d8447..409c3d59 100644 --- a/.github/project_workflows/deploy_app_store.yml +++ b/.github/project_workflows/deploy_app_store.yml @@ -27,7 +27,7 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.2.1 + uses: norio-nomura/action-swiftlint@master with: args: --strict diff --git a/.github/project_workflows/deploy_production_firebase.yml b/.github/project_workflows/deploy_production_firebase.yml index 68078f3c..1681371a 100644 --- a/.github/project_workflows/deploy_production_firebase.yml +++ b/.github/project_workflows/deploy_production_firebase.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.2.1 + uses: norio-nomura/action-swiftlint@master with: args: --strict diff --git a/.github/project_workflows/deploy_staging_firebase.yml b/.github/project_workflows/deploy_staging_firebase.yml index 67c706cd..06262e36 100644 --- a/.github/project_workflows/deploy_staging_firebase.yml +++ b/.github/project_workflows/deploy_staging_firebase.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.2.1 + uses: norio-nomura/action-swiftlint@master with: args: --strict From a571c2300a96eee2f25ccd5c58838379d605cb92 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 15 Nov 2023 09:18:44 +0700 Subject: [PATCH 11/29] [#521] Update Mark's approach --- .github/project_workflows/deploy_app_store.yml | 4 ++-- .github/project_workflows/deploy_production_firebase.yml | 4 ++-- .github/project_workflows/deploy_staging_firebase.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/project_workflows/deploy_app_store.yml b/.github/project_workflows/deploy_app_store.yml index 409c3d59..640ab706 100644 --- a/.github/project_workflows/deploy_app_store.yml +++ b/.github/project_workflows/deploy_app_store.yml @@ -27,9 +27,9 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@master + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 with: - args: --strict + args: swiftlint --strict build: name: Build diff --git a/.github/project_workflows/deploy_production_firebase.yml b/.github/project_workflows/deploy_production_firebase.yml index 1681371a..44ffb866 100644 --- a/.github/project_workflows/deploy_production_firebase.yml +++ b/.github/project_workflows/deploy_production_firebase.yml @@ -25,9 +25,9 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@master + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 with: - args: --strict + args: swiftlint --strict build: name: Build diff --git a/.github/project_workflows/deploy_staging_firebase.yml b/.github/project_workflows/deploy_staging_firebase.yml index 06262e36..20f26919 100644 --- a/.github/project_workflows/deploy_staging_firebase.yml +++ b/.github/project_workflows/deploy_staging_firebase.yml @@ -25,9 +25,9 @@ jobs: fetch-depth: 0 - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@master + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 with: - args: --strict + args: swiftlint --strict build: name: Build From e27fa3ae17ec7b7276544f7bd64d0f33241cdfb8 Mon Sep 17 00:00:00 2001 From: Abu Taher Date: Thu, 16 Nov 2023 19:00:39 +0700 Subject: [PATCH 12/29] [#519] Updae configuration --- {PROJECT_NAME}/Configurations/Plists/Info.plist | 2 +- .../Configurations/XCConfigs/DebugProduction.xcconfig | 1 + {PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig | 3 ++- .../Configurations/XCConfigs/ReleaseProduction.xcconfig | 1 + .../Configurations/XCConfigs/ReleaseStaging.xcconfig | 3 ++- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/{PROJECT_NAME}/Configurations/Plists/Info.plist b/{PROJECT_NAME}/Configurations/Plists/Info.plist index b18749da..8913a934 100644 --- a/{PROJECT_NAME}/Configurations/Plists/Info.plist +++ b/{PROJECT_NAME}/Configurations/Plists/Info.plist @@ -11,7 +11,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - $(PRODUCT_NAME) + $(APP_DISPLAY_NAME) CFBundlePackageType APPL CFBundleShortVersionString diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig index 80622ed9..3bf77958 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig @@ -7,6 +7,7 @@ ONLY_ACTIVE_ARCH = YES SWIFT_OPTIMIZATION_LEVEL = -Onone GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 PRODUCTION=1 +APP_DISPLAY_NAME = $(TARGET_NAME) PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_PRODUCTION} SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG PRODUCTION diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig index ee4be644..6a788e8c 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig @@ -7,7 +7,8 @@ ONLY_ACTIVE_ARCH = YES SWIFT_OPTIMIZATION_LEVEL = -Onone GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 STAGING=1 -PRODUCT_NAME = $(TARGET_NAME) Staging +APP_DISPLAY_NAME = $(TARGET_NAME) Staging +PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_STAGING} SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG STAGING OTHER_SWIFT_FLAGS = $(inherited) -Xfrontend -warn-long-expression-type-checking=300 -Xfrontend -warn-long-function-bodies=300 diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig index 227a6cc4..1dd4dd23 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig @@ -6,6 +6,7 @@ SWIFT_OPTIMIZATION_LEVEL = -O ENABLE_BITCODE = NO GCC_PREPROCESSOR_DEFINITIONS = $(inherited) PRODUCTION=1 +APP_DISPLAY_NAME = $(TARGET_NAME) PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_PRODUCTION} SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODUCTION RELEASE diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig index 27daeaea..b0473f12 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig @@ -7,6 +7,7 @@ ENABLE_BITCODE = NO ENABLE_BITCODE = NO GCC_PREPROCESSOR_DEFINITIONS = $(inherited) STAGING=1 -PRODUCT_NAME = $(TARGET_NAME) Staging +APP_DISPLAY_NAME = $(TARGET_NAME) Staging +PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_STAGING} SWIFT_ACTIVE_COMPILATION_CONDITIONS = STAGING RELEASE From bf74dd1a4a361f261b1ec755887ec5ec14e8ecc7 Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Sun, 29 Oct 2023 21:46:35 +0700 Subject: [PATCH 13/29] [#454] Add apiKey --- fastlane/Fastfile.swift | 8 ++++++++ fastlane/Helpers/Match.swift | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/fastlane/Fastfile.swift b/fastlane/Fastfile.swift index a5377234..39128524 100644 --- a/fastlane/Fastfile.swift +++ b/fastlane/Fastfile.swift @@ -172,6 +172,14 @@ class Fastfile: LaneFile { registerDevice( name: deviceName, udid: deviceUDID, + apiKey: .userDefined( + [ + "key_id" : EnvironmentParser.string(key: "KEY_ID"), + "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), + "key": EnvironmentParser.string(key: "KEY"), + "in_house": false + ] + ), teamId: .userDefined(Constant.appleStagingTeamId) ) diff --git a/fastlane/Helpers/Match.swift b/fastlane/Helpers/Match.swift index 2bcdd392..44c67153 100644 --- a/fastlane/Helpers/Match.swift +++ b/fastlane/Helpers/Match.swift @@ -27,6 +27,14 @@ enum Match { type: type.match, readonly: .userDefined(!isForce), appIdentifier: [environment.bundleId], + apiKey: .userDefined( + [ + "key_id" : EnvironmentParser.string(key: "KEY_ID"), + "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), + "key": EnvironmentParser.string(key: "KEY"), + "in_house": false + ] + ), username: .userDefined(environment.appleUsername), teamId: .userDefined(environment.appleTeamId), gitUrl: Constant.matchURL, From 181fbe33743b959b00804d889618a8b7f2e0d948 Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Sun, 29 Oct 2023 21:58:56 +0700 Subject: [PATCH 14/29] [#454] Add variables to env --- .env.example | 3 +++ fastlane/Constants/Constant.swift | 6 ++++++ fastlane/Fastfile.swift | 9 +-------- fastlane/Helpers/Match.swift | 9 +-------- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index e69de29b..bb5e91d3 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1,3 @@ +KEY_ID="" +ISSUER_ID="" +KEY="" diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index eb134fb6..552873e3 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -28,6 +28,12 @@ enum Constant { static let appleProductionTeamId = "<#teamId#>" static let keychainName = "{PROJECT_NAME}_keychain" static let matchURL = "git@github.com:{organization}/{repo}.git" + static let apiKey: [String: Any] = [ + "key_id" : EnvironmentParser.string(key: "KEY_ID"), + "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), + "key": EnvironmentParser.string(key: "KEY"), + "in_house": false + ] // MARK: - Path diff --git a/fastlane/Fastfile.swift b/fastlane/Fastfile.swift index 39128524..35fe6bbf 100644 --- a/fastlane/Fastfile.swift +++ b/fastlane/Fastfile.swift @@ -172,14 +172,7 @@ class Fastfile: LaneFile { registerDevice( name: deviceName, udid: deviceUDID, - apiKey: .userDefined( - [ - "key_id" : EnvironmentParser.string(key: "KEY_ID"), - "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), - "key": EnvironmentParser.string(key: "KEY"), - "in_house": false - ] - ), + apiKey: .userDefined(Constant.apiKey), teamId: .userDefined(Constant.appleStagingTeamId) ) diff --git a/fastlane/Helpers/Match.swift b/fastlane/Helpers/Match.swift index 44c67153..fd42d025 100644 --- a/fastlane/Helpers/Match.swift +++ b/fastlane/Helpers/Match.swift @@ -27,14 +27,7 @@ enum Match { type: type.match, readonly: .userDefined(!isForce), appIdentifier: [environment.bundleId], - apiKey: .userDefined( - [ - "key_id" : EnvironmentParser.string(key: "KEY_ID"), - "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), - "key": EnvironmentParser.string(key: "KEY"), - "in_house": false - ] - ), + apiKey: .userDefined(Constant.apiKey), username: .userDefined(environment.appleUsername), teamId: .userDefined(environment.appleTeamId), gitUrl: Constant.matchURL, From 0c5017b57dbde700e66caaa129a817a5db5c083a Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Mon, 6 Nov 2023 12:40:48 +0700 Subject: [PATCH 15/29] [#454] Rename --- .env.example | 4 ++-- fastlane/Constants/Constant.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index bb5e91d3..0096e2ae 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,3 @@ -KEY_ID="" +API_KEY_ID="" ISSUER_ID="" -KEY="" +APPSTORE_CONNECT_API_KEY="" diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index 552873e3..16da936a 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -29,9 +29,9 @@ enum Constant { static let keychainName = "{PROJECT_NAME}_keychain" static let matchURL = "git@github.com:{organization}/{repo}.git" static let apiKey: [String: Any] = [ - "key_id" : EnvironmentParser.string(key: "KEY_ID"), + "key_id" : EnvironmentParser.string(key: "API_KEY_ID"), "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), - "key": EnvironmentParser.string(key: "KEY"), + "key": EnvironmentParser.string(key: "APPSTORE_CONNECT_API_KEY"), "in_house": false ] From dd1965a8f0535d92ac74fe697538cc4dd01dab29 Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Mon, 20 Nov 2023 14:21:04 +0700 Subject: [PATCH 16/29] [#454] Update keys --- fastlane/Constants/Constant.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index 16da936a..c167b9f2 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -29,9 +29,9 @@ enum Constant { static let keychainName = "{PROJECT_NAME}_keychain" static let matchURL = "git@github.com:{organization}/{repo}.git" static let apiKey: [String: Any] = [ - "key_id" : EnvironmentParser.string(key: "API_KEY_ID"), - "issuer_id": EnvironmentParser.string(key: "ISSUER_ID"), - "key": EnvironmentParser.string(key: "APPSTORE_CONNECT_API_KEY"), + "key_id" : Secret.appStoreKeyIdKey, + "issuer_id": Secret.appStoreIssuerIdKey, + "key": Secret.appstoreConnectAPIKey, "in_house": false ] From 1b86e6331dda457d06a483bb4a55210328a893e7 Mon Sep 17 00:00:00 2001 From: Abu Taher Date: Wed, 22 Nov 2023 10:59:20 +0700 Subject: [PATCH 17/29] [#519] Remove product name from xcconfig --- {PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig | 1 - {PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig | 1 - .../Configurations/XCConfigs/ReleaseProduction.xcconfig | 1 - {PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig | 1 - 4 files changed, 4 deletions(-) diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig index 3bf77958..1c6b8f7e 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig @@ -8,7 +8,6 @@ SWIFT_OPTIMIZATION_LEVEL = -Onone GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 PRODUCTION=1 APP_DISPLAY_NAME = $(TARGET_NAME) -PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_PRODUCTION} SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG PRODUCTION OTHER_SWIFT_FLAGS = $(inherited) -Xfrontend -warn-long-expression-type-checking=300 -Xfrontend -warn-long-function-bodies=300 diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig index 6a788e8c..4136f867 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig @@ -8,7 +8,6 @@ SWIFT_OPTIMIZATION_LEVEL = -Onone GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 STAGING=1 APP_DISPLAY_NAME = $(TARGET_NAME) Staging -PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_STAGING} SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG STAGING OTHER_SWIFT_FLAGS = $(inherited) -Xfrontend -warn-long-expression-type-checking=300 -Xfrontend -warn-long-function-bodies=300 diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig index 1dd4dd23..b2858736 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig @@ -7,6 +7,5 @@ ENABLE_BITCODE = NO GCC_PREPROCESSOR_DEFINITIONS = $(inherited) PRODUCTION=1 APP_DISPLAY_NAME = $(TARGET_NAME) -PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_PRODUCTION} SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODUCTION RELEASE diff --git a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig index b0473f12..f857f34b 100644 --- a/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig @@ -8,6 +8,5 @@ ENABLE_BITCODE = NO ENABLE_BITCODE = NO GCC_PREPROCESSOR_DEFINITIONS = $(inherited) STAGING=1 APP_DISPLAY_NAME = $(TARGET_NAME) Staging -PRODUCT_NAME = $(TARGET_NAME) PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_STAGING} SWIFT_ACTIVE_COMPILATION_CONDITIONS = STAGING RELEASE From 8de90be77944a0b9bd5d2237645f17b11396c6ab Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Wed, 29 Nov 2023 23:01:32 +0700 Subject: [PATCH 18/29] Fix lints --- .swiftformat | 2 +- .swiftlint.yml | 2 +- fastlane/Constants/Constant.swift | 14 +++++++------- fastlane/Fastfile.swift | 4 ++-- fastlane/Helpers/Keychain.swift | 4 ++-- fastlane/Helpers/Match.swift | 10 +++++----- .../Sources/Utilities/KIF+Swift.swift | 4 ++-- .../Specs/Data/NetworkAPI/NetworkAPISpec.swift | 2 ++ 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.swiftformat b/.swiftformat index 472eefd4..62c8f1e6 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,5 +1,5 @@ # file options ---exclude Pods, Generated, **/*.generated.swift, fastlane/swift +--exclude Pods, Generated, **/*.generated.swift, fastlane/swift, ArkanaKeys # rules --disable fileHeader diff --git a/.swiftlint.yml b/.swiftlint.yml index e7d3d235..d0be68c4 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -9,9 +9,9 @@ excluded: - Pods - Derived - DerivedData + - ArkanaKeys opt_in_rules: - - anyobject_protocol - array_init - attributes - closure_body_length diff --git a/fastlane/Constants/Constant.swift b/fastlane/Constants/Constant.swift index eb134fb6..b530bff9 100644 --- a/fastlane/Constants/Constant.swift +++ b/fastlane/Constants/Constant.swift @@ -119,14 +119,14 @@ extension Constant { let outputDirectoryURL = URL(fileURLWithPath: Constant.outputPath) return outputDirectoryURL.appendingPathComponent(productName + ".app" + Constant.dSYMSuffix).relativePath } - + var appleUsername: String { switch self { case .staging: return Constant.appleStagingUserName case .production: return Constant.appleProductionUserName } } - + var appleTeamId: String { switch self { case .staging: return Constant.appleStagingTeamId @@ -142,7 +142,7 @@ extension Constant { case appStore = "app-store" var value: String { return rawValue } - + var match: String { switch self { case .development: return "development" @@ -150,21 +150,21 @@ extension Constant { case .appStore: return "appstore" } } - + var configuration: String { switch self { case .development: return "Debug" case .adHoc, .appStore: return "Release" } } - + var codeSignIdentity: String { switch self { case .development: return "iPhone Developer" - case .adHoc, . appStore: return "iPhone Distribution" + case .adHoc, .appStore: return "iPhone Distribution" } } - + var method: String { switch self { case .development: return "Development" diff --git a/fastlane/Fastfile.swift b/fastlane/Fastfile.swift index a5377234..ce2928bf 100644 --- a/fastlane/Fastfile.swift +++ b/fastlane/Fastfile.swift @@ -19,7 +19,7 @@ class Fastfile: LaneFile { environment: .staging ) } - + func syncDevelopmentProductionCodeSigningLane() { desc("Sync the Development match signing for the Production build") Match.syncCodeSigning( @@ -51,7 +51,7 @@ class Fastfile: LaneFile { environment: .production ) } - + func removeKeychainLane() { desc("Delete keychain") Keychain.remove() diff --git a/fastlane/Helpers/Keychain.swift b/fastlane/Helpers/Keychain.swift index 539e1eb5..3ba60e06 100644 --- a/fastlane/Helpers/Keychain.swift +++ b/fastlane/Helpers/Keychain.swift @@ -7,7 +7,7 @@ // import Foundation - + enum Keychain { static func create() { @@ -19,7 +19,7 @@ enum Keychain { timeout: 115_200 ) } - + static func remove() { deleteKeychain( name: .userDefined(Constant.keychainName) diff --git a/fastlane/Helpers/Match.swift b/fastlane/Helpers/Match.swift index 2bcdd392..a235ecc6 100644 --- a/fastlane/Helpers/Match.swift +++ b/fastlane/Helpers/Match.swift @@ -35,7 +35,7 @@ enum Match { } updateCodeSigning(type: type, environment: environment) } - + static func updateCodeSigning(type: Constant.BuildType, environment: Constant.Environment) { // Update Code signing from automatic to manual updateCodeSigningSettings( @@ -43,16 +43,16 @@ enum Match { useAutomaticSigning: .userDefined(false), teamId: .userDefined(environment.appleTeamId), targets: .userDefined([Constant.projectName]), - buildConfigurations: .userDefined([Self.createBuildConfiguration(type: type, environment: environment)]), + buildConfigurations: .userDefined([createBuildConfiguration(type: type, environment: environment)]), codeSignIdentity: .userDefined(type.codeSignIdentity), - profileName: .userDefined(Self.createProfileName(type: type, environment: environment)) + profileName: .userDefined(createProfileName(type: type, environment: environment)) ) } - + static func createBuildConfiguration(type: Constant.BuildType, environment: Constant.Environment) -> String { "\(type.configuration) \(environment.rawValue)" } - + static func createProfileName(type: Constant.BuildType, environment: Constant.Environment) -> String { "match \(type.method) \(environment.bundleId)" } diff --git a/{PROJECT_NAME}KIFUITests/Sources/Utilities/KIF+Swift.swift b/{PROJECT_NAME}KIFUITests/Sources/Utilities/KIF+Swift.swift index 66696b28..f5b3b712 100644 --- a/{PROJECT_NAME}KIFUITests/Sources/Utilities/KIF+Swift.swift +++ b/{PROJECT_NAME}KIFUITests/Sources/Utilities/KIF+Swift.swift @@ -8,10 +8,10 @@ import KIF extension KIFSpec { static func tester(file: String = #file, _ line: Int = #line) -> KIFUITestActor { - return KIFUITestActor(inFile: file, atLine: line, delegate: kifDelegate) + KIFUITestActor(inFile: file, atLine: line, delegate: kifDelegate) } static func system(file: String = #file, _ line: Int = #line) -> KIFSystemTestActor { - return KIFSystemTestActor(inFile: file, atLine: line, delegate: kifDelegate) + KIFSystemTestActor(inFile: file, atLine: line, delegate: kifDelegate) } } diff --git a/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift b/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift index 23e388c7..d65996e5 100644 --- a/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift +++ b/{PROJECT_NAME}Tests/Sources/Specs/Data/NetworkAPI/NetworkAPISpec.swift @@ -11,6 +11,7 @@ final class NetworkAPISpec: AsyncSpec { override class func spec() { + // swiftlint:disable closure_body_length describe("a NetworkAPI") { var networkAPI: NetworkAPI! @@ -60,5 +61,6 @@ final class NetworkAPISpec: AsyncSpec { } } } + // swiftlint:enable closure_body_length } } From a3d7e1fdcc8cefd41513e9cb1094bdc8bf1647b0 Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Thu, 30 Nov 2023 11:46:04 +0700 Subject: [PATCH 19/29] [#454] Check isForce for apiKey in syncCodeSigning --- fastlane/Helpers/Match.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Helpers/Match.swift b/fastlane/Helpers/Match.swift index fd42d025..2cf6c2a2 100644 --- a/fastlane/Helpers/Match.swift +++ b/fastlane/Helpers/Match.swift @@ -27,7 +27,7 @@ enum Match { type: type.match, readonly: .userDefined(!isForce), appIdentifier: [environment.bundleId], - apiKey: .userDefined(Constant.apiKey), + apiKey: isForce ? .userDefined(Constant.apiKey) : .nil, username: .userDefined(environment.appleUsername), teamId: .userDefined(environment.appleTeamId), gitUrl: Constant.matchURL, From 7db4dcb5ecc006318164b12ba622bd8ece324db7 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 15 Nov 2023 09:57:17 +0700 Subject: [PATCH 20/29] [#498] Update Script: SetUpCICDService to ask for runner type Wwe create 2 folders for each runner type with the same workflows but different runs-on. When the developer chooses a runner type, the chosen type will be copied to the workflow directory. --- .../automatic_pull_request_review.yml | 64 ++++++++++ .../deploy_app_store.yml | 104 ++++++++++++++++ .../deploy_production_firebase.yml | 105 +++++++++++++++++ .../deploy_staging_firebase.yml | 111 ++++++++++++++++++ .../draft_a_new_release.yml | 18 +++ .../publish_docs_to_wiki.yml | 19 +++ .../iOSTemplateMaker/SetUpCICDService.swift | 20 +++- 7 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 .github/self_hosted_project_workflows/automatic_pull_request_review.yml create mode 100644 .github/self_hosted_project_workflows/deploy_app_store.yml create mode 100644 .github/self_hosted_project_workflows/deploy_production_firebase.yml create mode 100644 .github/self_hosted_project_workflows/deploy_staging_firebase.yml create mode 100644 .github/self_hosted_project_workflows/draft_a_new_release.yml create mode 100644 .github/self_hosted_project_workflows/publish_docs_to_wiki.yml diff --git a/.github/self_hosted_project_workflows/automatic_pull_request_review.yml b/.github/self_hosted_project_workflows/automatic_pull_request_review.yml new file mode 100644 index 00000000..d539c914 --- /dev/null +++ b/.github/self_hosted_project_workflows/automatic_pull_request_review.yml @@ -0,0 +1,64 @@ +name: Automatic pull request review + +on: + pull_request: + types: [opened, reopened, edited, synchronize] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + review_pull_request: + name: Pull request review by Danger + runs-on: [self-hosted, macOS] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/cache@v3 + id: bunlderCache + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + + - name: Setup ENV file + env: + ENV: ${{ secrets.ENV }} + run: | + touch .env + echo $ENV | base64 --decode > .env + + - name: Bundle install + run: bundle install --path vendor/bundle + + - name: Run Arkana + run: bundle exec arkana + + - name: Cache Pods + uses: actions/cache@v3 + id: cocoapodCache + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Install Pods Dependencies + run: bundle exec pod install + + - name: Build and Test + run: bundle exec fastlane buildAndTest + env: + CI: true + + - name: Clean up previous code coverage report + run: bundle exec fastlane cleanUpOutput + + - name: Review pull request by Danger + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bundle exec danger diff --git a/.github/self_hosted_project_workflows/deploy_app_store.yml b/.github/self_hosted_project_workflows/deploy_app_store.yml new file mode 100644 index 00000000..a29955de --- /dev/null +++ b/.github/self_hosted_project_workflows/deploy_app_store.yml @@ -0,0 +1,104 @@ +name: Deploy Build To App Store + +# SECRETS needed: +### SSH_PRIVATE_KEY for Match Repo +### MATCH_PASS +### APPSTORE_CONNECT_API_KEY +### API_KEY_ID +### ISSUER_ID + +on: + push: + branches: [ master, main ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Run SwiftLint + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 + with: + args: swiftlint --strict + + build: + name: Build + runs-on: [self-hosted, macOS] + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + # Set fetch-depth (default: 1) to get whole tree + with: + fetch-depth: 0 + + - name: Install SSH key + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Setup ENV file + env: + ENV: ${{ secrets.ENV }} + run: | + touch .env + echo $ENV | base64 --decode > .env + + - name: Bundle install + run: bundle install + + - name: Run Arkana + run: bundle exec arkana + + - name: Cache Pods + uses: actions/cache@v3 + id: cocoapodCache + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Install Pods Dependencies + run: bundle exec pod install + shell: bash + + - name: Build and Test + run: bundle exec fastlane buildAndTest + + - name: Match AppStore + run: bundle exec fastlane syncAppStoreCodeSigning + env: + MATCH_PASSWORD: ${{ secrets.MATCH_PASS }} + + - name: Build App and Distribute to AppStore + run: bundle exec fastlane buildAndUploadToAppStore + env: + APPSTORE_CONNECT_API_KEY: ${{ secrets.APPSTORE_CONNECT_API_KEY }} + API_KEY_ID: ${{ secrets.API_KEY_ID }} + ISSUER_ID: ${{ secrets.ISSUER_ID }} + BUMP_APP_STORE_BUILD_NUMBER: "true" + + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ format('v{0}({1})-{2}', env.VERSION_NUMBER, env.BUILD_NUMBER, env.TAG_TYPE) }} + path: | + ${{ env.IPA_OUTPUT_PATH }} + ${{ env.DSYM_OUTPUT_PATH }} + env: + TAG_TYPE: App_Store + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/self_hosted_project_workflows/deploy_production_firebase.yml b/.github/self_hosted_project_workflows/deploy_production_firebase.yml new file mode 100644 index 00000000..461bdd01 --- /dev/null +++ b/.github/self_hosted_project_workflows/deploy_production_firebase.yml @@ -0,0 +1,105 @@ +name: Deploy Production Build To Firebase + +# SECRETS needed: +### SSH_PRIVATE_KEY for Match Repo +### MATCH_PASS +### FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 + +on: + push: + branches: [ release/** ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + Lint: + name: lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Run SwiftLint + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 + with: + args: swiftlint --strict + + build: + name: Build + runs-on: [self-hosted, macOS] + steps: + - uses: actions/checkout@v3 + # Set fetch-depth (default: 1) to get whole tree + with: + fetch-depth: 0 + + - name: Install SSH key + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Setup ENV file + env: + ENV: ${{ secrets.ENV }} + run: | + touch .env + echo $ENV | base64 --decode > .env + + - name: Read Google Service Account + id: firebase_service_account + uses: timheuer/base64-to-file@v1.2 + with: + fileName: 'firebase_service_account.json' + encodedString: ${{ secrets.FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} + + - name: Bundle install + run: bundle install + + - name: Run Arkana + run: bundle exec arkana + + - name: Cache Pods + uses: actions/cache@v3 + id: cocoapodCache + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Install Pods Dependencies + run: bundle exec pod install + shell: bash + + - name: Build and Test + run: bundle exec fastlane buildAndTest + + - name: Match Ad-hoc + run: bundle exec fastlane syncAdHocProductionCodeSigning + env: + MATCH_PASSWORD: ${{ secrets.MATCH_PASS }} + + - name: Build Production App and Distribute to Firebase + run: bundle exec fastlane buildProductionAndUploadToFirebase + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.firebase_service_account.outputs.filePath }} + + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ format('v{0}({1})-{2}', env.VERSION_NUMBER, env.BUILD_NUMBER, env.TAG_TYPE) }} + path: | + ${{ env.IPA_OUTPUT_PATH }} + ${{ env.DSYM_OUTPUT_PATH }} + env: + TAG_TYPE: Production_Firebase + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/self_hosted_project_workflows/deploy_staging_firebase.yml b/.github/self_hosted_project_workflows/deploy_staging_firebase.yml new file mode 100644 index 00000000..21ce2095 --- /dev/null +++ b/.github/self_hosted_project_workflows/deploy_staging_firebase.yml @@ -0,0 +1,111 @@ +name: Deploy Staging Build To Firebase + +# SECRETS needed: +### SSH_PRIVATE_KEY for Match Repo +### MATCH_PASS +### FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 + +on: + push: + branches: [ develop ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Run SwiftLint + uses: docker://norionomura/swiftlint:0.53.0_swift-5.7 + with: + args: swiftlint --strict + + build: + name: Build + runs-on: [self-hosted, macOS] + steps: + - uses: actions/checkout@v3 + # Set fetch-depth (default: 1) to get whole tree + with: + fetch-depth: 0 + + - name: Install SSH key + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Install Firebase-Tools + run: | + yarn global add firebase-tools + echo "$(yarn global bin)" >> $GITHUB_PATH + + - name: Setup ENV file + env: + ENV: ${{ secrets.ENV }} + run: | + touch .env + echo $ENV | base64 --decode > .env + + - name: Read Google Service Account + id: firebase_service_account + uses: timheuer/base64-to-file@v1.2 + with: + fileName: 'firebase_service_account.json' + encodedString: ${{ secrets.FIREBASE_GOOGLE_APPLICATION_CREDENTIALS_BASE64 }} + + - name: Bundle install + # if: steps.bundleCache.outputs.cache-hit != 'true' + run: bundle install + + - name: Run Arkana + run: bundle exec arkana + + - name: Cache Pods + uses: actions/cache@v2 + id: cocoapodCache + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + + - name: Install Pods Dependencies + run: bundle exec pod install + shell: bash + + - name: Build and Test + run: bundle exec fastlane buildAndTest + + - name: Match Ad-hoc + run: bundle exec fastlane syncAdHocStagingCodeSigning + env: + MATCH_PASSWORD: ${{ secrets.MATCH_PASS }} + + - name: Build App and Distribute to Firebase + run: bundle exec fastlane buildStagingAndUploadToFirebase + env: + GOOGLE_APPLICATION_CREDENTIALS: ${{ steps.firebase_service_account.outputs.filePath }} + + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ format('v{0}({1})-{2}', env.VERSION_NUMBER, env.BUILD_NUMBER, env.TAG_TYPE) }} + path: | + ${{ env.IPA_OUTPUT_PATH }} + ${{ env.DSYM_OUTPUT_PATH }} + env: + TAG_TYPE: Staging_Firebase + + - name: Remove keychain + if: ${{ always() }} + run: bundle exec fastlane removeKeychain + continue-on-error: true diff --git a/.github/self_hosted_project_workflows/draft_a_new_release.yml b/.github/self_hosted_project_workflows/draft_a_new_release.yml new file mode 100644 index 00000000..5550e4bc --- /dev/null +++ b/.github/self_hosted_project_workflows/draft_a_new_release.yml @@ -0,0 +1,18 @@ +name: Draft a new release + +on: + push: + branches: + - main +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/self_hosted_project_workflows/publish_docs_to_wiki.yml b/.github/self_hosted_project_workflows/publish_docs_to_wiki.yml new file mode 100644 index 00000000..dc71b021 --- /dev/null +++ b/.github/self_hosted_project_workflows/publish_docs_to_wiki.yml @@ -0,0 +1,19 @@ +name: Publish docs to Wiki + +on: + push: + paths: + - .github/wiki/** + branches: + - main + - master + +jobs: + publish_docs_to_wiki: + name: Publish Wiki + uses: nimblehq/github-actions-workflows/.github/workflows/publish_wiki.yml@0.1.0 + with: + USER_NAME: team-nimblehq + USER_EMAIL: dev@nimblehq.co + secrets: + USER_TOKEN: ${{ secrets.NIMBLE_DEV_TOKEN }} diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift index 06436a46..3f830069 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift @@ -33,13 +33,29 @@ struct SetUpCICDService { switch service { case .github: + var runnerType: String? + while runnerType == nil { + print("Which workflow runner do you want to use? [(m)acos-latest/(s)elf-hosted]: ") + runnerType = readLine()?.lowercased() + if runnerType != "m" && runnerType != "s" { + print("Invalid input. Please enter 'm' for macOS-latest or 's' for self-hosted.") + runnerType = nil + } + } print("Setting template for Github Actions") fileManager.removeItems(in: "bitrise.yml") fileManager.removeItems(in: "codemagic.yaml") fileManager.removeItems(in: ".github/workflows") fileManager.createDirectory(path: ".github/workflows") - fileManager.moveFiles(in: ".github/project_workflows", to: ".github/workflows") - fileManager.removeItems(in: ".github/project_workflows") + if runnerType == "s" { + fileManager.moveFiles(in: ".github/self_hosted_project_workflows", to: ".github/workflows") + fileManager.removeItems(in: ".github/project_workflows") + fileManager.removeItems(in: ".github/self_hosted_project_workflows") + } else { + fileManager.moveFiles(in: ".github/project_workflows", to: ".github/workflows") + fileManager.removeItems(in: ".github/project_workflows") + fileManager.removeItems(in: ".github/self_hosted_project_workflows") + } case .bitrise: print("Setting template for Bitrise") fileManager.removeItems(in: "codemagic.yaml") From 4ec71c2162a708b262ce2b12a88612bd6584f4ee Mon Sep 17 00:00:00 2001 From: David Date: Mon, 4 Dec 2023 09:41:22 +0700 Subject: [PATCH 21/29] [#498] Create Enum for gifhub runner type --- .../iOSTemplateMaker/SetUpCICDService.swift | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift index 3f830069..182d6909 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift @@ -21,6 +21,24 @@ struct SetUpCICDService { } } } + + enum GithubRunnerType { + + case macOSLatest, selfHosted, later + + init?(_ name: String) { + switch name.lowercased() { + case "m", "macOS": + self = .macOSLatest + case "s", "self-hosted": + self = .selfHosted + case "l", "later": + self = .later + default: + return nil + } + } + } private let fileManager = FileManager.default @@ -33,28 +51,27 @@ struct SetUpCICDService { switch service { case .github: - var runnerType: String? + var runnerType: GithubRunnerType? while runnerType == nil { - print("Which workflow runner do you want to use? [(m)acos-latest/(s)elf-hosted]: ") - runnerType = readLine()?.lowercased() - if runnerType != "m" && runnerType != "s" { - print("Invalid input. Please enter 'm' for macOS-latest or 's' for self-hosted.") - runnerType = nil - } + print("Which workflow runner do you want to use? [(m)acos-latest/(s)elf-hosted/(l)ater]: ") + runnerType = GithubRunnerType(readLine().string) } print("Setting template for Github Actions") fileManager.removeItems(in: "bitrise.yml") fileManager.removeItems(in: "codemagic.yaml") fileManager.removeItems(in: ".github/workflows") fileManager.createDirectory(path: ".github/workflows") - if runnerType == "s" { - fileManager.moveFiles(in: ".github/self_hosted_project_workflows", to: ".github/workflows") + switch runnerType { + case .macOSLatest: + fileManager.moveFiles(in: ".github/project_workflows", to: ".github/workflows") fileManager.removeItems(in: ".github/project_workflows") fileManager.removeItems(in: ".github/self_hosted_project_workflows") - } else { - fileManager.moveFiles(in: ".github/project_workflows", to: ".github/workflows") + case .selfHosted: + fileManager.moveFiles(in: ".github/self_hosted_project_workflows", to: ".github/workflows") fileManager.removeItems(in: ".github/project_workflows") fileManager.removeItems(in: ".github/self_hosted_project_workflows") + case .later, .none: + print("You can manually setup the runner later.") } case .bitrise: print("Setting template for Bitrise") From cae4b35741e51ec6a04c9ed6ff361c60499006b3 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 14 Dec 2023 09:05:41 +0700 Subject: [PATCH 22/29] [#498] Remove project workflows and self hosted project workflows in case CICD using bitrise or codemagic --- .../Sources/iOSTemplateMaker/SetUpCICDService.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift index 182d6909..e76ff47e 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift @@ -70,17 +70,21 @@ struct SetUpCICDService { fileManager.moveFiles(in: ".github/self_hosted_project_workflows", to: ".github/workflows") fileManager.removeItems(in: ".github/project_workflows") fileManager.removeItems(in: ".github/self_hosted_project_workflows") - case .later, .none: + case .later: print("You can manually setup the runner later.") } case .bitrise: print("Setting template for Bitrise") fileManager.removeItems(in: "codemagic.yaml") fileManager.removeItems(in: ".github/workflows") + fileManager.removeItems(in: ".github/project_workflows") + fileManager.removeItems(in: ".github/self_hosted_project_workflows") case .codemagic: print("Setting template for CodeMagic") fileManager.removeItems(in: "bitrise.yml") fileManager.removeItems(in: ".github/workflows") + fileManager.removeItems(in: ".github/project_workflows") + fileManager.removeItems(in: ".github/self_hosted_project_workflows") case .later, .none: print("You can manually setup the template later.") } From 70932c070a6993596f26659124fe8f850c5e2a08 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 14 Dec 2023 09:06:21 +0700 Subject: [PATCH 23/29] [#498] Update case .none --- .../Sources/iOSTemplateMaker/SetUpCICDService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift index e76ff47e..972e9250 100644 --- a/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift +++ b/Scripts/Swift/iOSTemplateMaker/Sources/iOSTemplateMaker/SetUpCICDService.swift @@ -70,7 +70,7 @@ struct SetUpCICDService { fileManager.moveFiles(in: ".github/self_hosted_project_workflows", to: ".github/workflows") fileManager.removeItems(in: ".github/project_workflows") fileManager.removeItems(in: ".github/self_hosted_project_workflows") - case .later: + case .later, .none: print("You can manually setup the runner later.") } case .bitrise: From 38fd30b4df92d8c05eb87c475787b9690f5f17ca Mon Sep 17 00:00:00 2001 From: phongvhd93 Date: Wed, 18 Oct 2023 21:32:37 +0700 Subject: [PATCH 24/29] [#349] Create target --- .../NetworkAPI/Core/NetworkAPIError.swift | 11 +++ .../NetworkAPI/Core/NetworkAPIProtocol.swift | 33 +++++++ .../Core/RequestConfiguration.swift | 39 ++++++++ .../Sources/NetworkAPI/Interceptors/.gitkeep | 0 .../Data/Sources/NetworkAPI/Models/.gitkeep | 0 .../Data/Sources/NetworkAPI/NetworkAPI.swift | 25 ++++++ .../NetworkAPI/RequestConfigurations/.gitkeep | 0 Modules/Data/Sources/Repositories/.gitkeep | 0 Modules/Domain/Sources/Entities/.gitkeep | 0 Modules/Domain/Sources/Interfaces/.gitkeep | 0 .../UseCases/UseCaseFactoryProtocol.swift | 5 ++ Project.swift | 10 +++ Tuist/ProjectDescriptionHelpers/Module.swift | 33 +++++++ .../Target+Initializing.swift | 88 +++++++++++++++++-- 14 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 Modules/Data/Sources/NetworkAPI/Core/NetworkAPIError.swift create mode 100644 Modules/Data/Sources/NetworkAPI/Core/NetworkAPIProtocol.swift create mode 100644 Modules/Data/Sources/NetworkAPI/Core/RequestConfiguration.swift create mode 100644 Modules/Data/Sources/NetworkAPI/Interceptors/.gitkeep create mode 100644 Modules/Data/Sources/NetworkAPI/Models/.gitkeep create mode 100644 Modules/Data/Sources/NetworkAPI/NetworkAPI.swift create mode 100644 Modules/Data/Sources/NetworkAPI/RequestConfigurations/.gitkeep create mode 100644 Modules/Data/Sources/Repositories/.gitkeep create mode 100644 Modules/Domain/Sources/Entities/.gitkeep create mode 100644 Modules/Domain/Sources/Interfaces/.gitkeep create mode 100644 Modules/Domain/Sources/UseCases/UseCaseFactoryProtocol.swift create mode 100644 Tuist/ProjectDescriptionHelpers/Module.swift diff --git a/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIError.swift b/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIError.swift new file mode 100644 index 00000000..78c72199 --- /dev/null +++ b/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIError.swift @@ -0,0 +1,11 @@ +// +// NetworkAPIError.swift +// + +import Foundation + +enum NetworkAPIError: Error { + + case generic + case dataNotFound +} diff --git a/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIProtocol.swift b/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIProtocol.swift new file mode 100644 index 00000000..c2ee0030 --- /dev/null +++ b/Modules/Data/Sources/NetworkAPI/Core/NetworkAPIProtocol.swift @@ -0,0 +1,33 @@ +// +// NetworkAPIProtocol.swift +// + +import Alamofire + +protocol NetworkAPIProtocol { + + func performRequest( + _ configuration: RequestConfiguration, + for type: T.Type + ) async throws -> T +} + +extension NetworkAPIProtocol { + + func request( + session: Session, + configuration: RequestConfiguration, + decoder: JSONDecoder + ) async throws -> T { + try await session.request( + configuration.url, + method: configuration.method, + parameters: configuration.parameters, + encoding: configuration.encoding, + headers: configuration.headers, + interceptor: configuration.interceptor + ) + .serializingDecodable(T.self) + .value + } +} diff --git a/Modules/Data/Sources/NetworkAPI/Core/RequestConfiguration.swift b/Modules/Data/Sources/NetworkAPI/Core/RequestConfiguration.swift new file mode 100644 index 00000000..32ddb9d3 --- /dev/null +++ b/Modules/Data/Sources/NetworkAPI/Core/RequestConfiguration.swift @@ -0,0 +1,39 @@ +// +// RequestConfiguration.swift +// + +import Alamofire +import Foundation + +protocol RequestConfiguration { + + var baseURL: String { get } + + var endpoint: String { get } + + var method: HTTPMethod { get } + + var url: URLConvertible { get } + + var parameters: Parameters? { get } + + var encoding: ParameterEncoding { get } + + var headers: HTTPHeaders? { get } + + var interceptor: RequestInterceptor? { get } +} + +extension RequestConfiguration { + + var url: URLConvertible { + let url = URL(string: baseURL)?.appendingPathComponent(endpoint) + return url?.absoluteString ?? "\(baseURL)\(endpoint)" + } + + var parameters: Parameters? { nil } + + var headers: HTTPHeaders? { nil } + + var interceptor: RequestInterceptor? { nil } +} diff --git a/Modules/Data/Sources/NetworkAPI/Interceptors/.gitkeep b/Modules/Data/Sources/NetworkAPI/Interceptors/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Data/Sources/NetworkAPI/Models/.gitkeep b/Modules/Data/Sources/NetworkAPI/Models/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Data/Sources/NetworkAPI/NetworkAPI.swift b/Modules/Data/Sources/NetworkAPI/NetworkAPI.swift new file mode 100644 index 00000000..d52c2081 --- /dev/null +++ b/Modules/Data/Sources/NetworkAPI/NetworkAPI.swift @@ -0,0 +1,25 @@ +// +// NetworkAPI.swift +// + +import Alamofire + +final class NetworkAPI: NetworkAPIProtocol { + + private let decoder: JSONDecoder + + init(decoder: JSONDecoder = JSONDecoder()) { + self.decoder = decoder + } + + func performRequest( + _ configuration: RequestConfiguration, + for type: T.Type + ) async throws -> T { + try await request( + session: Session(), + configuration: configuration, + decoder: decoder + ) + } +} diff --git a/Modules/Data/Sources/NetworkAPI/RequestConfigurations/.gitkeep b/Modules/Data/Sources/NetworkAPI/RequestConfigurations/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Data/Sources/Repositories/.gitkeep b/Modules/Data/Sources/Repositories/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Domain/Sources/Entities/.gitkeep b/Modules/Domain/Sources/Entities/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Domain/Sources/Interfaces/.gitkeep b/Modules/Domain/Sources/Interfaces/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Modules/Domain/Sources/UseCases/UseCaseFactoryProtocol.swift b/Modules/Domain/Sources/UseCases/UseCaseFactoryProtocol.swift new file mode 100644 index 00000000..9948f5ba --- /dev/null +++ b/Modules/Domain/Sources/UseCases/UseCaseFactoryProtocol.swift @@ -0,0 +1,5 @@ +// +// UseCaseFactoryProtocol.swift +// + +protocol UseCaseFactoryProtocol: AnyObject {} diff --git a/Project.swift b/Project.swift index 92d63a5e..da528d53 100644 --- a/Project.swift +++ b/Project.swift @@ -6,6 +6,16 @@ let project = Project.project(name: "{PROJECT_NAME}", bundleId: "${PRODUCT_BUNDL extension Project { static func project(name: String, bundleId: String) -> Project { + var targets: [Target] = [ + .mainTarget(name: name, bundleId: bundleId), + .testsTarget(name: name, bundleId: bundleId), + .kifUITestsTarget(name: name, bundleId: bundleId), + ] + + Module.allCases.forEach { + targets.append(Target.makeFramework(module: $0, bundleId: bundleId)) + } + return Project( name: name, organizationName: "Nimble", diff --git a/Tuist/ProjectDescriptionHelpers/Module.swift b/Tuist/ProjectDescriptionHelpers/Module.swift new file mode 100644 index 00000000..06f95023 --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Module.swift @@ -0,0 +1,33 @@ +// +// 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)] + } + } +} diff --git a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift index 5e2158db..ccc1cc9e 100644 --- a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift @@ -1,9 +1,16 @@ import ProjectDescription extension Target { - + private static let plistsPath: String = "Configurations/Plists" - + + enum Constant { + + static let modulesRootPath: String = "Modules" + static let sourcesPath: String = "Sources" + static let resourcesPath: String = "Resources" + } + public static func mainTarget(name: String, bundleId: String) -> Target { return Target( name: name, @@ -11,7 +18,7 @@ extension Target { product: .app, bundleId: bundleId, deploymentTarget: .iOS( - targetVersion: "{TARGET_VERSION}", + targetVersion: "{TARGET_VERSION}", devices: [.iphone] ), infoPlist: "\(name)/\(plistsPath)/Info.plist", @@ -26,10 +33,14 @@ extension Target { .swiftLintScript(), .swiftFormatLintScript(), .firebaseScript() + ], + dependencies: [ + .target(name: Module.data.name), + .target(name: Module.data.name) ] ) } - + public static func testsTarget(name: String, bundleId: String) -> Target { let targetName = "\(name)Tests" return Target( @@ -47,7 +58,7 @@ extension Target { dependencies: [.target(name: name)] ) } - + public static func kifUITestsTarget(name: String, bundleId: String) -> Target { let targetName = "\(name)KIFUITests" return Target( @@ -59,8 +70,73 @@ extension Target { sources: ["\(targetName)/**"], resources: [ "\(targetName)/**/.gitkeep", // To include empty folders - ], + ], dependencies: [.target(name: name)] ) } + + public static func makeFramework(module: Module, bundleId: String) -> Target { + let frameworkPath = "\(Constant.modulesRootPath)/\(module.name)" + let resourcesElement = ResourceFileElement.glob(pattern: "\(frameworkPath)/\(Constant.resourcesPath)/**") + + return Target( + name: module.name, + platform: .iOS, + product: .framework, + bundleId: bundleId, + sources: ["\(frameworkPath)/\(Constant.sourcesPath)/**"], + resources: ResourceFileElements(resources: [resourcesElement]), + dependencies: module.dependencies + ) + } } + +//// MARK: - Domain +// +//extension Target { +// +// public static func domainTarget(bundleId: String) -> Target { +// return Target( +// name: "Domain", +// platform: .iOS, +// product: .staticLibrary, +// bundleId: bundleId +// ) +// } +// +// public static func domainTestsTarget(bundleId: String) -> Target { +// return Target( +// name: "DomainTests", +// platform: .iOS, +// product: .unitTests, +// bundleId: bundleId +// ) +// } +//} +// +// +//// MARK: - Data +// +//extension Target { +// +// public static func domainTarget(bundleId: String) -> Target { +// let name = "Data" +// return Target( +// name: name, +// platform: .iOS, +// product: .staticLibrary, +// bundleId: bundleId, +// sources: ["\(modulesPath)/\(name)/**"] +// ) +// } +// +// public static func domainTestsTarget(bundleId: String) -> Target { +// return Target( +// name: "DataTests", +// platform: .iOS, +// product: .unitTests, +// bundleId: bundleId, +// sources: [""] +// ) +// } +//} From 547acd500d71ad4b1d0e398cb91a2c619ab3bf78 Mon Sep 17 00:00:00 2001 From: phongvhd93 Date: Mon, 23 Oct 2023 14:55:52 +0700 Subject: [PATCH 25/29] [#349] Create modules for Domain and Data layer --- .../Data/Tests/Resources}/.gitkeep | 0 .../Sources/Dummies}/DummyNetworkModel.swift | 0 .../Dummies}/DummyRequestConfiguration.swift | 4 +- .../Specs}/NetworkAPI/NetworkAPISpec.swift | 2 +- .../Sources/Utilities/NetworkStubber.swift | 0 .../Domain/Tests/Resources}/.gitkeep | 0 .../Tests/Sources/Specs/DummySpec.swift | 23 +++ Project.swift | 16 +-- Tuist/Interfaces/SwiftUI/Project/Podfile | 21 +++ Tuist/Interfaces/UIKit/Project/Podfile | 25 +++- .../ProjectDescriptionHelpers/Constant.swift | 15 ++ Tuist/ProjectDescriptionHelpers/Module.swift | 36 ++++- .../Scheme+Initializing.swift | 20 +-- .../Target+Initializing.swift | 136 ++++++++---------- fastlane/Fastfile.swift | 4 - fastlane/Helpers/Test.swift | 4 +- .../NetworkAPI/Core/NetworkAPIError.swift | 11 -- .../NetworkAPI/Core/NetworkAPIProtocol.swift | 33 ----- .../Core/RequestConfiguration.swift | 39 ----- .../Sources/Data/NetworkAPI/NetworkAPI.swift | 25 ---- .../NetworkAPI/RequestConfigurations/.gitkeep | 0 .../Sources/Data/Repositories/.gitkeep | 0 .../Sources/Domain/Entities/.gitkeep | 0 .../Sources/Domain/Interfaces/.gitkeep | 0 .../UseCases/UseCaseFactoryProtocol.swift | 5 - 25 files changed, 191 insertions(+), 228 deletions(-) rename {{PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors => Modules/Data/Tests/Resources}/.gitkeep (100%) rename {{PROJECT_NAME}Tests/Sources/Dummy/Data => Modules/Data/Tests/Sources/Dummies}/DummyNetworkModel.swift (100%) rename {{PROJECT_NAME}Tests/Sources/Dummy/Data => Modules/Data/Tests/Sources/Dummies}/DummyRequestConfiguration.swift (88%) rename {{PROJECT_NAME}Tests/Sources/Specs/Data => Modules/Data/Tests/Sources/Specs}/NetworkAPI/NetworkAPISpec.swift (98%) rename {{PROJECT_NAME}Tests => Modules/Data/Tests}/Sources/Utilities/NetworkStubber.swift (100%) rename {{PROJECT_NAME}/Sources/Data/NetworkAPI/Models => Modules/Domain/Tests/Resources}/.gitkeep (100%) create mode 100644 Modules/Domain/Tests/Sources/Specs/DummySpec.swift create mode 100644 Tuist/ProjectDescriptionHelpers/Constant.swift delete mode 100644 {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift delete mode 100644 {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift delete mode 100644 {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift delete mode 100644 {PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift delete mode 100644 {PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep delete mode 100644 {PROJECT_NAME}/Sources/Data/Repositories/.gitkeep delete mode 100644 {PROJECT_NAME}/Sources/Domain/Entities/.gitkeep delete mode 100644 {PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep delete mode 100644 {PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors/.gitkeep b/Modules/Data/Tests/Resources/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors/.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/Data/NetworkAPI/Models/.gitkeep b/Modules/Domain/Tests/Resources/.gitkeep similarity index 100% rename from {PROJECT_NAME}/Sources/Data/NetworkAPI/Models/.gitkeep rename to Modules/Domain/Tests/Resources/.gitkeep 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 da528d53..c58b5cd2 100644 --- a/Project.swift +++ b/Project.swift @@ -6,15 +6,7 @@ let project = Project.project(name: "{PROJECT_NAME}", bundleId: "${PRODUCT_BUNDL extension Project { static func project(name: String, bundleId: String) -> Project { - var targets: [Target] = [ - .mainTarget(name: name, bundleId: bundleId), - .testsTarget(name: name, bundleId: bundleId), - .kifUITestsTarget(name: name, bundleId: bundleId), - ] - - Module.allCases.forEach { - targets.append(Target.makeFramework(module: $0, bundleId: bundleId)) - } + let targets = Target.makeTargets(name: name, bundleId: bundleId) return Project( name: name, @@ -26,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..a2fc0e78 100644 --- a/Tuist/Interfaces/SwiftUI/Project/Podfile +++ b/Tuist/Interfaces/SwiftUI/Project/Podfile @@ -46,6 +46,27 @@ 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 + # Pods for Domain + 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..d81009c3 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,27 @@ 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 + # Pods for Domain + 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 index 06f95023..4c13cb4c 100644 --- a/Tuist/ProjectDescriptionHelpers/Module.swift +++ b/Tuist/ProjectDescriptionHelpers/Module.swift @@ -7,7 +7,6 @@ import ProjectDescription - public enum Module: CaseIterable { case domain @@ -30,4 +29,39 @@ public enum Module: CaseIterable { return [.target(name: Module.domain.name)] } } + + public var frameworkPath: String { + "\(Constant.modulesRootPath)/\(name)" + } + + public var sources: ProjectDescription.SourceFilesList { + return ["\(frameworkPath)/\(Constant.sourcesPath)/**"] + } + + public var resources: ProjectDescription.ResourceFileElements { + switch self { + case .data, .domain: + return [] + } + } + + 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..33440e42 100644 --- a/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift @@ -5,14 +5,15 @@ extension Scheme { public static func productionScheme(name: String) -> Scheme { let debugConfigName = BuildConfiguration.debugProduction.name let releaseConfigName = BuildConfiguration.releaseProduction.name + + var testModules = Module.allCases.map { TestableTarget("\($0.name)\(Constant.testsPath)") } + testModules.append(contentsOf: ["\(name)Tests", "\(name)KIFUITests"]) + 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 +24,15 @@ extension Scheme { public static func stagingScheme(name: String) -> Scheme { let debugConfigName = BuildConfiguration.debugStaging.name let releaseConfigName = BuildConfiguration.releaseStaging.name + + var testModules = Module.allCases.map { TestableTarget("\($0.name)\(Constant.testsPath)") } + testModules.append(contentsOf: ["\(name)Tests", "\(name)KIFUITests"]) + 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,8 +41,6 @@ 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, diff --git a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift index ccc1cc9e..58f508bc 100644 --- a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift @@ -1,17 +1,32 @@ import ProjectDescription extension Target { - - private static let plistsPath: String = "Configurations/Plists" - - enum Constant { - - static let modulesRootPath: String = "Modules" - static let sourcesPath: String = "Sources" - static let resourcesPath: String = "Resources" + + public static func makeTargets(name: String, bundleId: String) -> [Target] { + var targets: [Target] = [] + + let frameworks = Module.allCases + .flatMap { Target.makeFramework(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 } - - public static func mainTarget(name: String, bundleId: String) -> Target { +} + +// MARK: - Main Targets + +extension Target { + + fileprivate static func mainTarget(name: String, bundleId: String) -> Target { return Target( name: name, platform: .iOS, @@ -21,7 +36,7 @@ extension Target { targetVersion: "{TARGET_VERSION}", devices: [.iphone] ), - infoPlist: "\(name)/\(plistsPath)/Info.plist", + infoPlist: "\(name)/\(Constant.plistsPath)/Info.plist", sources: ["\(name)/Sources/**"], resources: [ "\(name)/Resources/**", @@ -36,19 +51,19 @@ extension Target { ], dependencies: [ .target(name: Module.data.name), - .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 @@ -58,15 +73,15 @@ extension Target { dependencies: [.target(name: name)] ) } - - 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 @@ -74,69 +89,36 @@ extension Target { dependencies: [.target(name: name)] ) } - - public static func makeFramework(module: Module, bundleId: String) -> Target { - let frameworkPath = "\(Constant.modulesRootPath)/\(module.name)" - let resourcesElement = ResourceFileElement.glob(pattern: "\(frameworkPath)/\(Constant.resourcesPath)/**") - - return Target( +} + +// MARK: - Dependencies + +extension Target { + + fileprivate static func makeFramework(module: Module, bundleId: String) -> [Target] { + let framework = Target( name: module.name, platform: .iOS, product: .framework, - bundleId: bundleId, - sources: ["\(frameworkPath)/\(Constant.sourcesPath)/**"], - resources: ResourceFileElements(resources: [resourcesElement]), + 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] } } - -//// MARK: - Domain -// -//extension Target { -// -// public static func domainTarget(bundleId: String) -> Target { -// return Target( -// name: "Domain", -// platform: .iOS, -// product: .staticLibrary, -// bundleId: bundleId -// ) -// } -// -// public static func domainTestsTarget(bundleId: String) -> Target { -// return Target( -// name: "DomainTests", -// platform: .iOS, -// product: .unitTests, -// bundleId: bundleId -// ) -// } -//} -// -// -//// MARK: - Data -// -//extension Target { -// -// public static func domainTarget(bundleId: String) -> Target { -// let name = "Data" -// return Target( -// name: name, -// platform: .iOS, -// product: .staticLibrary, -// bundleId: bundleId, -// sources: ["\(modulesPath)/\(name)/**"] -// ) -// } -// -// public static func domainTestsTarget(bundleId: String) -> Target { -// return Target( -// name: "DataTests", -// platform: .iOS, -// product: .unitTests, -// bundleId: bundleId, -// sources: [""] -// ) -// } -//} 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", diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift deleted file mode 100644 index 78c72199..00000000 --- a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// NetworkAPIError.swift -// - -import Foundation - -enum NetworkAPIError: Error { - - case generic - case dataNotFound -} diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift deleted file mode 100644 index c2ee0030..00000000 --- a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// NetworkAPIProtocol.swift -// - -import Alamofire - -protocol NetworkAPIProtocol { - - func performRequest( - _ configuration: RequestConfiguration, - for type: T.Type - ) async throws -> T -} - -extension NetworkAPIProtocol { - - func request( - session: Session, - configuration: RequestConfiguration, - decoder: JSONDecoder - ) async throws -> T { - try await session.request( - configuration.url, - method: configuration.method, - parameters: configuration.parameters, - encoding: configuration.encoding, - headers: configuration.headers, - interceptor: configuration.interceptor - ) - .serializingDecodable(T.self) - .value - } -} diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift deleted file mode 100644 index 32ddb9d3..00000000 --- a/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// RequestConfiguration.swift -// - -import Alamofire -import Foundation - -protocol RequestConfiguration { - - var baseURL: String { get } - - var endpoint: String { get } - - var method: HTTPMethod { get } - - var url: URLConvertible { get } - - var parameters: Parameters? { get } - - var encoding: ParameterEncoding { get } - - var headers: HTTPHeaders? { get } - - var interceptor: RequestInterceptor? { get } -} - -extension RequestConfiguration { - - var url: URLConvertible { - let url = URL(string: baseURL)?.appendingPathComponent(endpoint) - return url?.absoluteString ?? "\(baseURL)\(endpoint)" - } - - var parameters: Parameters? { nil } - - var headers: HTTPHeaders? { nil } - - var interceptor: RequestInterceptor? { nil } -} diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift deleted file mode 100644 index d52c2081..00000000 --- a/{PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// NetworkAPI.swift -// - -import Alamofire - -final class NetworkAPI: NetworkAPIProtocol { - - private let decoder: JSONDecoder - - init(decoder: JSONDecoder = JSONDecoder()) { - self.decoder = decoder - } - - func performRequest( - _ configuration: RequestConfiguration, - for type: T.Type - ) async throws -> T { - try await request( - session: Session(), - configuration: configuration, - decoder: decoder - ) - } -} diff --git a/{PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep b/{PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/{PROJECT_NAME}/Sources/Data/Repositories/.gitkeep b/{PROJECT_NAME}/Sources/Data/Repositories/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/{PROJECT_NAME}/Sources/Domain/Entities/.gitkeep b/{PROJECT_NAME}/Sources/Domain/Entities/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/{PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep b/{PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/{PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift b/{PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift deleted file mode 100644 index 9948f5ba..00000000 --- a/{PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift +++ /dev/null @@ -1,5 +0,0 @@ -// -// UseCaseFactoryProtocol.swift -// - -protocol UseCaseFactoryProtocol: AnyObject {} From 517900eb64632bed20bcad4efac6c81fc941c579 Mon Sep 17 00:00:00 2001 From: phongvhd93 Date: Mon, 6 Nov 2023 20:29:01 +0700 Subject: [PATCH 26/29] [#349] remove unncessary constant --- fastlane/Constants/Constant.swift | 5 ----- 1 file changed, 5 deletions(-) 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 { From 4fccd4161edecd54d548478a611df4fc0207cfd7 Mon Sep 17 00:00:00 2001 From: phongvhd93 Date: Wed, 22 Nov 2023 16:27:05 +0700 Subject: [PATCH 27/29] [#349] Rename makeFramework to frameworkTargets --- Tuist/ProjectDescriptionHelpers/Module.swift | 5 +---- Tuist/ProjectDescriptionHelpers/Target+Initializing.swift | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Tuist/ProjectDescriptionHelpers/Module.swift b/Tuist/ProjectDescriptionHelpers/Module.swift index 4c13cb4c..1d9d6e71 100644 --- a/Tuist/ProjectDescriptionHelpers/Module.swift +++ b/Tuist/ProjectDescriptionHelpers/Module.swift @@ -39,10 +39,7 @@ public enum Module: CaseIterable { } public var resources: ProjectDescription.ResourceFileElements { - switch self { - case .data, .domain: - return [] - } + [] } public var testsSources: ProjectDescription.SourceFilesList { diff --git a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift index 58f508bc..5d974c07 100644 --- a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift @@ -6,7 +6,7 @@ extension Target { var targets: [Target] = [] let frameworks = Module.allCases - .flatMap { Target.makeFramework(module: $0, bundleId: bundleId) } + .flatMap { Target.frameworkTargets(module: $0, bundleId: bundleId) } targets.append(contentsOf: frameworks) @@ -95,7 +95,7 @@ extension Target { extension Target { - fileprivate static func makeFramework(module: Module, bundleId: String) -> [Target] { + fileprivate static func frameworkTargets(module: Module, bundleId: String) -> [Target] { let framework = Target( name: module.name, platform: .iOS, From c86637e05151d6ffb3c03df078f57df130df43a6 Mon Sep 17 00:00:00 2001 From: phongvhd93 Date: Mon, 4 Dec 2023 22:44:09 +0700 Subject: [PATCH 28/29] [#349] Improve the duplicated codes --- Tuist/Interfaces/SwiftUI/Project/Podfile | 1 - Tuist/Interfaces/UIKit/Project/Podfile | 2 -- Tuist/ProjectDescriptionHelpers/Module.swift | 2 +- .../Scheme+Initializing.swift | 14 ++++++++------ 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Tuist/Interfaces/SwiftUI/Project/Podfile b/Tuist/Interfaces/SwiftUI/Project/Podfile index a2fc0e78..e39e56c6 100644 --- a/Tuist/Interfaces/SwiftUI/Project/Podfile +++ b/Tuist/Interfaces/SwiftUI/Project/Podfile @@ -61,7 +61,6 @@ target 'Data' do end target 'Domain' do - # Pods for Domain target 'DomainTests' do testing_pods end diff --git a/Tuist/Interfaces/UIKit/Project/Podfile b/Tuist/Interfaces/UIKit/Project/Podfile index d81009c3..bb31ac75 100644 --- a/Tuist/Interfaces/UIKit/Project/Podfile +++ b/Tuist/Interfaces/UIKit/Project/Podfile @@ -51,7 +51,6 @@ end target 'Data' do data_dependencies - target 'DataTests' do data_dependencies testing_pods @@ -59,7 +58,6 @@ target 'Data' do end target 'Domain' do - # Pods for Domain target 'DomainTests' do testing_pods end diff --git a/Tuist/ProjectDescriptionHelpers/Module.swift b/Tuist/ProjectDescriptionHelpers/Module.swift index 1d9d6e71..28a6a00d 100644 --- a/Tuist/ProjectDescriptionHelpers/Module.swift +++ b/Tuist/ProjectDescriptionHelpers/Module.swift @@ -35,7 +35,7 @@ public enum Module: CaseIterable { } public var sources: ProjectDescription.SourceFilesList { - return ["\(frameworkPath)/\(Constant.sourcesPath)/**"] + ["\(frameworkPath)/\(Constant.sourcesPath)/**"] } public var resources: ProjectDescription.ResourceFileElements { diff --git a/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift index 33440e42..cda644fd 100644 --- a/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Scheme+Initializing.swift @@ -5,9 +5,7 @@ extension Scheme { public static func productionScheme(name: String) -> Scheme { let debugConfigName = BuildConfiguration.debugProduction.name let releaseConfigName = BuildConfiguration.releaseProduction.name - - var testModules = Module.allCases.map { TestableTarget("\($0.name)\(Constant.testsPath)") } - testModules.append(contentsOf: ["\(name)Tests", "\(name)KIFUITests"]) + let testModules = testSchemes(name) return Scheme( name: name, @@ -24,9 +22,7 @@ extension Scheme { public static func stagingScheme(name: String) -> Scheme { let debugConfigName = BuildConfiguration.debugStaging.name let releaseConfigName = BuildConfiguration.releaseStaging.name - - var testModules = Module.allCases.map { TestableTarget("\($0.name)\(Constant.testsPath)") } - testModules.append(contentsOf: ["\(name)Tests", "\(name)KIFUITests"]) + let testModules = testSchemes(name) return Scheme( name: "\(name) Staging", @@ -47,4 +43,10 @@ extension Scheme { 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 + } } From c9675af78222ffad259efe3d943a56954ee12e3f Mon Sep 17 00:00:00 2001 From: phongvhd93 Date: Fri, 17 Nov 2023 17:31:08 +0700 Subject: [PATCH 29/29] [#541] Update Standard File Organization --- .github/wiki/Standard-File-Organization.md | 216 +++++++++++---------- 1 file changed, 114 insertions(+), 102 deletions(-) diff --git a/.github/wiki/Standard-File-Organization.md b/.github/wiki/Standard-File-Organization.md index 6008ef78..07134b13 100644 --- a/.github/wiki/Standard-File-Organization.md +++ b/.github/wiki/Standard-File-Organization.md @@ -7,108 +7,109 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro ``` . ├── README.md -├── {ProjectName} -│   ├── Configurations -│   │   ├── Plists -│   │   └── XCConfigs -│   ├── Resources -│   │   ├── Assets -│   │   ├── Languages -│   │   └── LaunchScreen -│   └── Sources -│   ├── Application -│   │   └── Varies by UI Interface -│   ├── Constants -│   │   ├── Constants+API.swift -│   │   └── Constants.swift -│   ├── Data -│   │   ├── Keychain -│   │   │   ├── Keychain.swift -│   │   │   ├── KeychainKey.swift -│   │   │   └── Models -│   │   ├── Models -│   │   ├── NetworkAPI -│   │   │   ├── AuthenticatedNetworkAPI.swift -│   │   │   ├── NetworkAPI.swift -│   │   │   ├── Core -│   │   │   ├── Interceptors -│   │   │   ├── Models -│   │   │   └── RequestConfigurations -│   │   └── Repositories -│   │   ├── RepositoryProvider.swift -│   │   ├── Authentication -│   │   └── User -│   ├── Domain -│   │   ├── Entities -│   │   │   ├── User.swift -│   │   │   └── Token.swift -│   │   ├── Interfaces -│   │   │   └── Repositories -│   │   └── UseCases -│   │   ├── UseCaseProvider.swift -│   │   ├── Authentication -│   │   └── User -│   ├── Presentation -│   │   └── Varies by UI Interface -│   └── Supports -│   ├── Builder -│   │   └── Builder.swift -│   ├── Extensions -│   │   ├── Foundation -│   │   └── UIKit -│   └── Helpers -│   ├── Typealias -│   └── UIKit -├── {ProjectName}Tests -│   ├── Configurations -│   │   └── Plists -│   ├── Resources -│   └── Sources -│   ├── Dummy -│   │   ├── Data -│   │   │   └── Models -│   │   ├── Domain -│   │   │   └── Entities -│   │   └── Modules -│   │   └── Home -│   ├── Mocks -│   │   ├── NetworkAPIMock.swift -│   │   └── Sourcery -│   │   ├── AutoMockable.generated.swift -│   │   └── HomeViewModelProtocolMock+Equatable.swift -│   ├── Specs -│   │   ├── Data -│   │   │   └── Datasources -│   │   │   └── Repositories -│   │   ├── Domain -│   │   │   └── UseCases -│   │   ├── Presentation -│   │   │   ├── Modules -│   │   │   └── Navigator -│   │   └── Supports -│   │   └── Extensions -│   └── Utilities -│   ├── Data+Decode.swift -│   ├── String+Data.swift -│   └── TestError.swift -└── {ProjectName}KIFUITests -    ├── Configurations -    │   └── Plists -    └── Sources -    ├── AccessibilityIdentifiers -    │   ├── Login -    │   └── Home -    ├── Flows -    │   ├── Login -    │   └── Home -    ├── Screens -    │   ├── Login -    │   └── Home -    ├── Specs -    │   ├── Login -    │   └── Home -    └── Utilities -    └── KIF+Swift.swift +├── Modules/ +│ ├── Data/ +│ │ ├── Sources/ +│ │ │ ├── NetworkAPI/ +│ │ │ │ ├── Interceptors +│ │ │ │ ├── Models +│ │ │ │ ├── RequestConfigurations +│ │ │ │ ├── Core/ +│ │ │ │ │ ├── NetworkAPIError.swift +│ │ │ │ │ ├── NetworkAPIProtocol.swift +│ │ │ │ │ └── RequestConfiguration.swift +│ │ │ │ └── NetworkAPI.swift +│ │ │ └── Repositories +│ │ └── Tests/ +│ │ ├── Resources +│ │ └── Sources/ +│ │ ├── Dummies/ +│ │ │ ├── DummyNetworkModel.swift +│ │ │ └── DummyRequestConfiguration.swift +│ │ ├── Specs/ +│ │ │ └── NetworkAPISpec.swift +│ │ └── Utilities/ +│ │ └── NetworkStubber.swift +│ └── Domain/ +│ ├── Sources/ +│ │ ├── Entities +│ │ ├── Interfaces +│ │ └── UseCases/ +│ │ └── UseCaseFactoryProtocol.swift +│ └── Tests/ +│ ├── Resources +│ └── Sources/ +│ └── DummySpec.swift +├── {ProjectName}/ +│ ├── Configurations/ +│ │ ├── Plists +│ │ └── XCConfigs +│ ├── Resources/ +│ │ ├── Assets +│ │ ├── Languages +│ │ └── LaunchScreen +│ └── Sources/ +│ ├── Application/ +│ │ └── Varies by UI Interface +│ ├── Constants/ +│ │ ├── Constants+API.swift +│ │ └── Constants.swift +│ ├── Presentations/ +│ │ └── Varies by UI Interaface +│ └── Supports/ +│ ├── Builder/ +│ │ └── Builder.swift +│ ├── Extensions/ +│ │ ├── Foundation +│ │ └── UIKit +│ └── Helpers/ +│ ├── Typealias +│ └── UIKit +├── {ProjectName}Tests/ +│ ├── Configurations/ +│ │ └── Plists +│ ├── Resources +│ └── Sources/ +│ ├── Dummy/ +│ │ ├── Data/ +│ │ │ └── Models +│ │ ├── Domain/ +│ │ │ └── Entities +│ │ └── Modules/ +│ │ └── Home +│ ├── Mocks/ +│ │ ├── NetworkAPIMock.swift +│ │ └── Sourcery/ +│ │ ├── AutoMockable.generated.swift +│ │ └── HomeViewModelProtocolMock+Equatable.swift +│ ├── Specs/ +│ │ ├── Presentation/ +│ │ │ ├── Modules +│ │ │ └── Navigator +│ │ └── Supports/ +│ │ └── Extensions +│ └── Utilities/ +│ ├── Data+Decode.swift +│ ├── String+Data.swift +│ └── TestError.swift +└── {ProjectName}KIFUITests/ + ├── Configurations/ + │ └── Plists + └── Sources/ + ├── AccessibilityIdentifiers/ + │ ├── Login + │ └── Home + ├── Flows/ + │ ├── Login + │ └── Home + ├── Screens/ + │ ├── Login + │ └── Home + ├── Specs/ + │ ├── Login + │ └── Home + └── Utilities/ + └── KIF+Swift.swift ``` ### SwiftUI @@ -173,6 +174,17 @@ To keep all current and upcoming iOS projects aligned, we standardize an iOS pro - How to set up the project? - What are project configurations? +## Modules + +This folder contains modules which represent targets in the project. Currently, it contains `Data` and `Domain` folder. + +- Data: This folder contains two subfolders + - Sources: This folder contains only `.swift` files - the main source code of the module. + - Tests: This folder contains the unit testing. +- Domain: This folder contains source files and Unit Test for the `Domain` target. + - Sources: This folder contains only `.swift` files - the main source code of the module. + - Tests: This folder contains the unit testing. + ## {ProjectName} This folder contains the main sources of the project. There are three sub-folders: