diff --git a/.github/ISSUE_TEMPLATE/bug_template.md b/.github/ISSUE_TEMPLATE/bug_template.md new file mode 100644 index 00000000..5eb84c37 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_template.md @@ -0,0 +1,20 @@ +--- +name: "Bug Report" +about: "You found something that is not working. Report it so that it can be fixed. 👷‍" +title: "Fix: " +labels: "type : bug" +--- + +## Issue + +Describe the issue you are facing. Show us the implementation: screenshots, gif, etc. + +## Expected + +Describe what should be the correct behaviour. + +## Steps to reproduce + +1. +2. +3. diff --git a/.github/ISSUE_TEMPLATE/chore_template.md b/.github/ISSUE_TEMPLATE/chore_template.md new file mode 100644 index 00000000..bb36ec26 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/chore_template.md @@ -0,0 +1,14 @@ +--- +name: "Chore" +about: "Open a Chore for minor update." +title: "Update " +labels: "type : chore" +--- + +## Why + +Describe the update details and why it's needed. + +## Who Benefits? + +Describe who will be the beneficiaries e.g. everyone, specific chapters, clients... diff --git a/.github/ISSUE_TEMPLATE/feature_template.md b/.github/ISSUE_TEMPLATE/feature_template.md new file mode 100644 index 00000000..5a26eb96 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_template.md @@ -0,0 +1,14 @@ +--- +name: "Feature" +about: "Open a feature issue to add new functionalities." +title: "Add " +labels: "type : feature" +--- + +## Why + +Describe the big picture of the feature and why it's needed. + +## Who Benefits? + +Describe who will be the beneficiaries e.g. everyone, specific chapters, clients... diff --git a/.github/ISSUE_TEMPLATE/story_template.md b/.github/ISSUE_TEMPLATE/story_template.md new file mode 100644 index 00000000..9d5c1108 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/story_template.md @@ -0,0 +1,22 @@ +--- +name: "Story" +about: "Open a feature story" +title: "[Type] As a user, I can " +labels: "type : feature" +--- + +## Why + +Describe the idea of the user story as in what the motive of the user story is. + +## Acceptance Criteria + +List down how the user story will be tested and what criteria are necessary for the user story to be accepted. + +## Design + +(Optional) Add design screenshots or Figma links for UI/UX-related stories. + +## Resources + +(Optional) Add useful resources such as links to documentation, implementation ideas, or best practices. diff --git a/.github/workflows/test.yml b/.github/workflows/automatic_pull_request_review.yml similarity index 51% rename from .github/workflows/test.yml rename to .github/workflows/automatic_pull_request_review.yml index f49493bc..d0aaf2ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/automatic_pull_request_review.yml @@ -1,26 +1,12 @@ -name: test +name: Automatic pull request review on: pull_request: - types: [assigned, opened, synchronize, reopened] + types: [opened, reopened, edited, synchronize] jobs: - Lint: - name: Lint - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Run SwiftLint - uses: norio-nomura/action-swiftlint@3.1.0 - with: - args: --strict - - Test: - name: Test + review_pull_request: + name: Pull request review by Danger runs-on: macOS-latest steps: - name: Cancel Previous Runs @@ -29,12 +15,19 @@ jobs: access_token: ${{ github.token }} - uses: actions/checkout@v2 - # Set fetch-depth (default: 1) to get whole tree with: fetch-depth: 0 - + + - uses: actions/cache@v2 + id: bunlderCache + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + - name: Bundle install - run: bundle install + run: bundle install --path vendor/bundle - name: Cache Pods uses: actions/cache@v2 @@ -47,7 +40,16 @@ jobs: - name: Install Pods Dependencies run: bundle exec pod install - shell: bash - name: Build and Test run: bundle exec fastlane build_and_test + env: + CI: true + + - name: Export code coverage report + run: bundle exec fastlane run_xcov + + - name: Review pull request by Danger + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bundle exec danger diff --git a/.github/workflows/test_install_script.yml b/.github/workflows/test_install_script.yml new file mode 100644 index 00000000..de2bfeaa --- /dev/null +++ b/.github/workflows/test_install_script.yml @@ -0,0 +1,30 @@ +name: Test Install Script + +on: + push: + branches: [ feature/**, bug/**, chore/** ] + +jobs: + Test: + name: Test + runs-on: macOS-latest + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.5.0 + with: + access_token: ${{ github.token }} + + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Bundle install + run: bundle install + + - name: Start Install Script for Template App + run: sh make.sh --bundle-id co.nimblehq.template --bundle-id-staging co.nimblehq.template.staging --project-name TemplateApp + + - name: Build and Test + run: bundle exec fastlane build_and_test + env: + CI: true diff --git a/.gitignore b/.gitignore index 4f1016df..f9be475e 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,13 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output +fastlane/xcov_output # Sourcery -*.generated.swift \ No newline at end of file +*.generated.swift + +# Derived Data folder +DerivedData + +# Tuist +.tuist-generated diff --git a/.sourcery.yml b/.sourcery.yml index 8d479cc3..29469316 100644 --- a/.sourcery.yml +++ b/.sourcery.yml @@ -1,12 +1,12 @@ sources: - - ProjectName/Sources + - {PROJECT_NAME}/Sources templates: - tools/sourcery/templates output: - ProjectNameTests/Sources/Mocks/Sourcery + {PROJECT_NAME}Tests/Sources/Mocks/Sourcery args: autoMockableTestableImports: - - ProjectName + - {PROJECT_NAME} autoMockableImports: - RxSwift - RxCocoa diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 00000000..1bf106ec --- /dev/null +++ b/.swiftformat @@ -0,0 +1,11 @@ +# file options +--exclude Pods, Generated, **/*.generated.swift + +# rules +--disable fileHeader +--disable initCoderUnavailable +--disable trailingCommas +--disable wrapEnumCases +--disable wrapMultilineStatementBraces +--disable wrapSwitchCases +--disable blankLinesAtStartOfScope diff --git a/.swiftlint.yml b/.swiftlint.yml index cb70588c..ec1acf82 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,6 +1,11 @@ +included: + - {PROJECT_NAME} + - {PROJECT_NAME}Tests + - {PROJECT_NAME}UITests + excluded: - - ProjectNameTests/Sources/Mocks/Sourcery/AutoMockable.generated.swift - - ProjectName/Sources/Supports/Helpers/R.swift/R.generated.swift + - {PROJECT_NAME}Tests/Sources/Mocks/Sourcery/AutoMockable.generated.swift + - {PROJECT_NAME}/Sources/Supports/Helpers/R.swift/R.generated.swift - Pods - Derived - DerivedData diff --git a/Dangerfile b/Dangerfile new file mode 100644 index 00000000..255b7841 --- /dev/null +++ b/Dangerfile @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require './fastlane/Constants/Constants' + +# Warn when there is a big PR +warn("This pull request is quite big (#{git.lines_of_code} lines changed), please consider splitting it into multiple pull requests.") if git.lines_of_code > 500 + +# Warn to encourage that labels should have been used on the PR +warn("This pull request doesn't have any labels, please consider to add labels to this pull request.") if github.pr_labels.empty? + +# SwiftFormat +swiftformat.binary_path = './Pods/SwiftFormat/CommandLineTool/swiftformat' +swiftformat.exclude = %w(Pods/** **/*generated.swift) +swiftformat.check_format + +# Swiftlint +swiftlint.binary_path = './Pods/SwiftLint/swiftlint' +swiftlint.config_file = '.swiftlint.yml' +swiftlint.max_num_violations = 20 +swiftlint.lint_files( + inline_mode: true, + fail_on_error: true, + additional_swiftlint_args: '--strict' +) + +xcresultPath = "#{Constants.TEST_OUTPUT_DIRECTORY_PATH}/#{Constants.TESTS_SCHEME}.xcresult" + +# Xcode summary +xcode_summary.ignored_files = 'Pods/**' +xcode_summary.inline_mode = true +xcode_summary.report xcresultPath + +# Upload the report of the code coverage of the files changed in a pull request +markdown File.read("#{Constants.XCOV_OUTPUT_DIRECTORY_PATH}/report.md") \ No newline at end of file diff --git a/Gemfile b/Gemfile index 82d1e304..b3816a38 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,12 @@ source "https://rubygems.org" -gem "fastlane" gem "cocoapods" +gem "fastlane" +gem "xcov" +gem "danger" +gem "danger-swiftlint" +gem "danger-xcode_summary" +gem 'danger-swiftformat' + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Podfile b/Podfile index 2579908a..7c21e364 100644 --- a/Podfile +++ b/Podfile @@ -1,4 +1,4 @@ -platform :ios, '10.0' +platform :ios, '11.0' use_frameworks! def testing_pods @@ -7,9 +7,10 @@ def testing_pods pod 'RxNimble', subspecs: ['RxBlocking', 'RxTest'] pod 'RxSwift' pod 'Sourcery' + pod 'SwiftFormat/CLI' end -target 'ProjectName' do +target '{PROJECT_NAME}' do # UI pod 'Kingfisher' pod 'SnapKit' @@ -28,17 +29,18 @@ target 'ProjectName' do pod 'IQKeyboardManagerSwift' pod 'NimbleExtension', :git => 'https://github.com/nimblehq/NimbleExtension', :branch => 'master' pod 'R.swift' + pod 'Resolver' # Needs Cocoapods on iOS 11 to support Resolver # Development pod 'SwiftLint' pod 'Wormholy', :configurations => ['Debug Staging', 'Debug Production'] - target 'ProjectNameTests' do + target '{PROJECT_NAME}Tests' do inherit! :search_paths testing_pods end - target 'ProjectNameUITests' do + target '{PROJECT_NAME}UITests' do testing_pods end end diff --git a/Project.swift b/Project.swift index 9118bb2f..6d38bb70 100644 --- a/Project.swift +++ b/Project.swift @@ -1,11 +1,11 @@ import ProjectDescription import ProjectDescriptionHelpers -let project = Project.project(name: "ProjectName") +let project = Project.project(name: "{PROJECT_NAME}", bundleId: "{BUNDLE_ID_PRODUCTION}") extension Project { - static func project(name: String) -> Project { + static func project(name: String, bundleId: String) -> Project { return Project( name: name, organizationName: "Nimble", @@ -14,9 +14,9 @@ extension Project { .map { $0.createConfiguration(projectName: name) } ), targets: [ - Target.mainTarget(name: name), - Target.testsTarget(name: name), - Target.uiTestsTarget(name: name) + Target.mainTarget(name: name, bundleId: bundleId), + Target.testsTarget(name: name, bundleId: bundleId), + Target.uiTestsTarget(name: name, bundleId: bundleId) ], schemes: [ Scheme.productionScheme(name: name), diff --git a/ProjectNameUITests/Resources/.gitkeep b/ProjectNameUITests/Resources/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/ProjectNameUITests/Sources/.gitkeep b/ProjectNameUITests/Sources/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/README.md b/README.md index 0a745dd3..e7a04757 100644 --- a/README.md +++ b/README.md @@ -30,20 +30,9 @@ Documentation : [Tuist Official Documents](https://docs.tuist.io/tutorial/get-st ## How to use +### Install Script -1. Change the project name at line 3 in `Project.swift` - - ```swift - let project = Project.project(name: "ios-template") - ``` - to: - ```swift - let project = Project.project(name: "") - ``` - -2. Execute the following command: - - ```bash - tuist generate - ``` - The command `tuist generate` will generate your project files, folders and two important files `*.xcodeproj` and `*.xcworkspace`. +Execute the following command +``` +sh make.sh --bundle-id [BUNDLE_ID_PRODUCTION] --bundle-id-staging [BUNDLE_ID_STAGING] --project-name [PROJECT_NAME] +``` diff --git a/Tuist/Config.swift b/Tuist/Config.swift index f1ee8d9f..e39707e0 100644 --- a/Tuist/Config.swift +++ b/Tuist/Config.swift @@ -2,6 +2,8 @@ import ProjectDescription let config = Config( generationOptions: [ - .disableAutogeneratedSchemes + .disableAutogeneratedSchemes, + .disableSynthesizedResourceAccessors, + .disableBundleAccessors ] ) diff --git a/Tuist/ProjectDescriptionHelpers/ProjectBuildConfiguration.swift b/Tuist/ProjectDescriptionHelpers/ProjectBuildConfiguration.swift index 7f7bd8ea..bc309144 100644 --- a/Tuist/ProjectDescriptionHelpers/ProjectBuildConfiguration.swift +++ b/Tuist/ProjectDescriptionHelpers/ProjectBuildConfiguration.swift @@ -31,12 +31,12 @@ public enum ProjectBuildConfiguration: CaseIterable { } public func createConfiguration(projectName: String) -> CustomConfiguration { - let xcconfigPath = Path("\(projectName)/\(xcconfigPath)") + let resultXcconfigPath = Path("\(projectName)/\(xcconfigPath)") switch self { case .debugStaging, .debugProduction: - return .debug(name: name, xcconfig: xcconfigPath) + return .debug(name: name, xcconfig: resultXcconfigPath) case .releaseStaging, .releaseProduction: - return .release(name: name, xcconfig: xcconfigPath) + return .release(name: name, xcconfig: resultXcconfigPath) } } } diff --git a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift index 2951afe4..a5b25dc7 100644 --- a/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/Target+Initializing.swift @@ -4,12 +4,12 @@ extension Target { private static let plistsPath: String = "Configurations/Plists" - public static func mainTarget(name: String, bundleId: String = "co.nimblehq") -> Target { + public static func mainTarget(name: String, bundleId: String) -> Target { return Target( name: name, platform: .iOS, product: .app, - bundleId: "\(bundleId).\(name)", + bundleId: bundleId, infoPlist: "\(name)/\(plistsPath)/Info.plist", sources: ["\(name)/Sources/**"], resources: [ @@ -19,37 +19,42 @@ extension Target { actions: [ TargetAction.sourceryAction(), TargetAction.rswiftAction(), - TargetAction.swiftLintAction() + TargetAction.swiftLintAction(), + TargetAction.swiftFormatLintAction(), + TargetAction.firebaseAction() ] ) } - - public static func testsTarget(name: String, bundleId: String = "co.nimblehq") -> Target { + + public static func testsTarget(name: String, bundleId: String) -> Target { let targetName = "\(name)Tests" return Target( name: targetName, platform: .iOS, product: .unitTests, - bundleId: "\(bundleId).\(targetName)", + bundleId: bundleId, infoPlist: "\(targetName)/\(plistsPath)/Info.plist", sources: ["\(targetName)/**"], resources: [ "\(targetName)/**/.gitkeep", // To include empty folders "\(targetName)/Resources/**/*" - ], + ], + actions: [ + TargetAction.swiftFormatAction() + ], dependencies: [ .target(name: name) ] ) } - public static func uiTestsTarget(name: String, bundleId: String = "co.nimblehq") -> Target { + public static func uiTestsTarget(name: String, bundleId: String) -> Target { let targetName = "\(name)UITests" return Target( name: targetName, platform: .iOS, product: .uiTests, - bundleId: "\(bundleId).\(targetName)", + bundleId: bundleId, infoPlist: "\(targetName)/\(plistsPath)/Info.plist", sources: ["\(targetName)/**"], resources: [ diff --git a/Tuist/ProjectDescriptionHelpers/TargetAction+Initializing.swift b/Tuist/ProjectDescriptionHelpers/TargetAction+Initializing.swift index 2564d95a..f1ebf2d2 100644 --- a/Tuist/ProjectDescriptionHelpers/TargetAction+Initializing.swift +++ b/Tuist/ProjectDescriptionHelpers/TargetAction+Initializing.swift @@ -25,11 +25,64 @@ extension TargetAction { } public static func swiftLintAction() -> TargetAction { - let swiftLintPath = "${PODS_ROOT}/SwiftLint/swiftlint" + let swiftLintPath = """ + if [ -z "$CI" ]; then + ${PODS_ROOT}/SwiftLint/swiftlint + fi + """ return .pre( - script: "\"\(swiftLintPath)\"", + script: swiftLintPath, name: "SwiftLint", basedOnDependencyAnalysis: true ) } + + public static func swiftFormatAction() -> TargetAction { + let runSwiftFormat = """ + if [ -z "$CI" ]; then + "${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat" "$SRCROOT" + fi + """ + return .pre( + script: runSwiftFormat, + name: "SwiftFormat", + basedOnDependencyAnalysis: true + ) + } + + public static func swiftFormatLintAction() -> TargetAction { + let runSwiftFormat = """ + if [ -z "$CI" ]; then + "${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat" "$SRCROOT" --lint --lenient + fi + """ + return .pre( + script: runSwiftFormat, + name: "SwiftFormat Lint", + basedOnDependencyAnalysis: true + ) + } + + public static func firebaseAction() -> TargetAction { + let script = """ + PATH_TO_GOOGLE_PLISTS="$SRCROOT/$PROJECT_NAME/Configurations/Plists/GoogleService" + + case "${CONFIGURATION}" in + "\(ProjectBuildConfiguration.debugStaging.name)" | "\(ProjectBuildConfiguration.releaseStaging.name)" ) + cp -r "$PATH_TO_GOOGLE_PLISTS/Staging/GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" + ;; + "\(ProjectBuildConfiguration.debugProduction.name)" | "\(ProjectBuildConfiguration.releaseProduction.name)" ) + cp -r "$PATH_TO_GOOGLE_PLISTS/Production/GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" + ;; + *) + ;; + esac + """ + + return .post( + script: script, + name: "Copy GoogleService-Info.plist", + basedOnDependencyAnalysis: true + ) + } } diff --git a/bitrise.yml b/bitrise.yml index 5f6d9cda..3efe61bc 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -6,11 +6,13 @@ trigger_map: - push_branch: develop workflow: deploy_staging - push_branch: master - workflow: deploy_appstore + workflow: deploy_app_store +- push_branch: release + workflow: deploy_release_firebase - push_branch: "*" workflow: test workflows: - deploy_appstore: + deploy_app_store: steps: - activate-ssh-key@4: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' @@ -54,11 +56,60 @@ workflows: envs: - opts: is_expand: false - BUNDLE_ID: com.nimblehq.example + BUNDLE_ID: {BUNDLE_ID_PRODUCTION} - opts: is_expand: false - BITRISE_SCHEME: ExampleApp - deploy_staging: + BITRISE_SCHEME: {PROJECT_NAME} + deploy_release_firebase: + steps: + - activate-ssh-key@4: + run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' + - script@1: + inputs: + - content: |- + #!/bin/bash + set -ex + RBENV_DIR="`cd $(rbenv which ruby)/../..;pwd`" + echo "Gem cache directory: $RBENV_DIR" + envman add --key GEM_CACHE_PATH --value $RBENV_DIR + title: Set GEM_CACHE_PATH env var + - git-clone@6.1: {} + - cache-pull@2: {} + - cocoapods-install@2: {} + - xcode-test@3.0: {} + - fastlane-match@0: + inputs: + - type: appstore + - team_id: "$TEAM_ID" + - app_id: "$BUNDLE_ID" + - git_url: "$MATCH_REPO_URL" + - decrypt_password: "$MATCH_PASSWORD" + - certificate-and-profile-installer@1: {} + - set-xcode-build-number@1: + inputs: + - plist_path: "$INFO_PLIST_PATH" + - xcode-archive@3: {} + - firebase-app-distribution@0: + inputs: + - firebase_token: "$FIREBASE_CI_TOKEN" + - app: "$FIREBASE_APP_ID_PRD" + - firebase-dsym-upload@2: + inputs: + - fdu_google_services_location: "$GOOGLE_SERVICE_INFO_PLIST_PATH" + - cache-push@2: + inputs: + - cache_paths: |- + ./Pods -> ./Podfile.lock + $BITRISE_CACHE_DIR + $GEM_CACHE_PATH + envs: + - opts: + is_expand: false + BUNDLE_ID: {BUNDLE_ID_PRODUCTION} + - opts: + is_expand: false + BITRISE_SCHEME: {PROJECT_NAME} +deploy_staging: steps: - activate-ssh-key@4: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' @@ -103,10 +154,10 @@ workflows: envs: - opts: is_expand: false - BUNDLE_ID: com.nimblehq.example.staging + BUNDLE_ID: {BUNDLE_ID_STAGING} - opts: is_expand: false - BITRISE_SCHEME: ExampleApp Staging + BITRISE_SCHEME: {PROJECT_NAME} Staging test: steps: - activate-ssh-key@4: @@ -123,7 +174,15 @@ workflows: - git-clone@6.1: {} - cache-pull@2: {} - cocoapods-install@2: {} - - xcode-test@3.0: {} + - fastlane@3: + inputs: + - lane: build_and_test + - fastlane@3: + inputs: + - lane: run_xcov + - danger@2: + inputs: + - github_api_token: $DANGER_GITHUB_API_TOKEN - cache-push@2: inputs: - cache_paths: |- @@ -133,15 +192,15 @@ workflows: envs: - opts: is_expand: false - BUNDLE_ID: com.nimblehq.example.staging + BUNDLE_ID: {BUNDLE_ID_STAGING} - opts: is_expand: false - BITRISE_SCHEME: ExampleApp Staging + BITRISE_SCHEME: {PROJECT_NAME} Staging app: envs: - opts: is_expand: false - BITRISE_PROJECT_PATH: ExampleApp.xcworkspace + BITRISE_PROJECT_PATH: {PROJECT_NAME}.xcworkspace - opts: is_expand: false TEAM_ID: TEAMID @@ -150,7 +209,7 @@ app: MATCH_REPO_URL: https://github.com/nimblehq/fastlane-match.git - opts: is_expand: false - INFO_PLIST_PATH: "./ExampleApp/Info.plist" + INFO_PLIST_PATH: "./{PROJECT_NAME}/Info.plist" - opts: is_expand: false - GOOGLE_SERVICE_INFO_PLIST_PATH: "./ExampleApp/GoogleService-Info.plist" + GOOGLE_SERVICE_INFO_PLIST_PATH: "./{PROJECT_NAME}/GoogleService-Info.plist" diff --git a/fastlane/.env.example b/fastlane/.env.example new file mode 100644 index 00000000..7be6bf7b --- /dev/null +++ b/fastlane/.env.example @@ -0,0 +1,4 @@ +FIREBASE_CLI_TOKEN = +KEYCHAIN_PASSWORD = +APPSTORE_CONNECT_API_KEY = +MANUAL_VERSION = diff --git a/fastlane/Constants/Constants.rb b/fastlane/Constants/Constants.rb index 4711eb7e..ff6113f5 100644 --- a/fastlane/Constants/Constants.rb +++ b/fastlane/Constants/Constants.rb @@ -4,7 +4,12 @@ class Constants ################# #### PROJECT #### ################# - + + # Workspace path + def self.WORKSPACE_PATH + './{PROJECT_NAME}.xcworkspace' + end + # Project path def self.PROJECT_PATH './{PROJECT_NAME}.xcodeproj' @@ -33,7 +38,7 @@ def self.DERIVED_DATA_PATH def self.BUILD_PATH './Build' end - + ################# #### TESTING #### ################# @@ -58,6 +63,16 @@ def self.UI_TESTS_TARGET '{PROJECT_NAME}UITests' end + # xcov output directory path + def self.XCOV_OUTPUT_DIRECTORY_PATH + './fastlane/xcov_output' + end + + # test output directory path + def self.TEST_OUTPUT_DIRECTORY_PATH + './fastlane/test_output' + end + ################## #### FIREBASE #### ################## @@ -69,12 +84,12 @@ def self.GSP_DIRECTORY # a gsp file name for staging def self.GSP_STAGING - './GoogleService-Info.plist' + './{PROJECT_NAME}/Configurations/Plists/GoogleService/Staging/GoogleService-Info.plist' end # a gsp file name for production def self.GSP_PRODUCTION - './GoogleService-Info.plist' + './{PROJECT_NAME}/Configurations/Plists/GoogleService/Production/GoogleService-Info.plist' end # The path to the upload-symbols file of the Fabric app @@ -96,7 +111,7 @@ def self.FIREBASE_APP_ID_PRODUCTION def self.FIREBASE_TESTER_GROUPS "nimble-dev" end - + ################# #### KEYCHAIN #### ################# diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 66c832e8..5439674b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -8,9 +8,17 @@ require './Managers/SymbolManager' require './Managers/DistributionManager' require './Managers/MatchManager' require './Managers/TestManager' +require './Managers/ReportManager' test_manager = TestManager.new( - fastlane: self + fastlane: self, + device: Constants.DEVICE, + output_directory: Constants.TEST_OUTPUT_DIRECTORY_PATH +) + +report_manager = ReportManager.new( + fastlane: self, + output_directory: Constants.XCOV_OUTPUT_DIRECTORY_PATH ) versioning_manager = VersioningManager.new( @@ -50,19 +58,26 @@ platform :ios do desc 'Run tests' lane :tests do - test_manager.test(scheme: Constants.TESTS_SCHEME, target: Constants.TESTS_TARGET) + test_manager.test( + scheme: Constants.TESTS_SCHEME, + targets: [Constants.TESTS_TARGET, Constants.UI_TESTS_TARGET] + ) end - desc 'Run UI tests' - lane :ui_tests do - test_manager.test(scheme: Constants.TESTS_SCHEME, target: Constants.UI_TESTS_TARGET) + desc 'Run xcov' + lane :run_xcov do + clear_derived_data(derived_data_path: Constants.XCOV_OUTPUT_DIRECTORY_PATH) + report_manager.produce_report( + scheme: Constants.TESTS_SCHEME, + workspace: Constants.WORKSPACE_PATH, + test_output_directory: Constants.TEST_OUTPUT_DIRECTORY_PATH + ) end desc 'Build and Test' lane :build_and_test do build_for_testing tests - ui_tests end # Code Sign diff --git a/fastlane/Managers/ReportManager.rb b/fastlane/Managers/ReportManager.rb new file mode 100644 index 00000000..00b06be6 --- /dev/null +++ b/fastlane/Managers/ReportManager.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class ReportManager + def initialize(fastlane:, output_directory:) + @fastlane = fastlane + @output_directory = output_directory + end + + def produce_report(scheme:, workspace:, test_output_directory:) + xccov_file_direct_path = "#{test_output_directory}/#{scheme}.xcresult" + @fastlane.xcov( + scheme: scheme, + workspace: workspace, + output_directory: @output_directory, + xccov_file_direct_path: xccov_file_direct_path, + only_project_targets: true, + markdown_report: true, + html_report: false + ) + end +end diff --git a/fastlane/Managers/TestManager.rb b/fastlane/Managers/TestManager.rb index 9b7354d9..248ad3f8 100644 --- a/fastlane/Managers/TestManager.rb +++ b/fastlane/Managers/TestManager.rb @@ -1,15 +1,36 @@ # frozen_string_literal: true class TestManager - def initialize(fastlane:) + def initialize(fastlane:, device:, output_directory:) @fastlane = fastlane + @device = device + @output_directory = output_directory end def build(scheme:) - @fastlane.scan(scheme: scheme, build_for_testing: true, should_zip_build_products: true, clean: true) + @fastlane.scan( + scheme: scheme, + device: @device, + output_directory: @output_directory, + code_coverage: true, + result_bundle: true, + build_for_testing: true, + should_zip_build_products: true, + clean: true, + fail_build: false + ) end - def test(scheme:, target:) - @fastlane.scan(scheme: scheme, test_without_building: true, only_testing: [target]) + def test(scheme:, targets:) + @fastlane.scan( + scheme: scheme, + device: @device, + output_directory: @output_directory, + code_coverage: true, + result_bundle: true, + test_without_building: true, + only_testing: targets, + fail_build: false + ) end end diff --git a/make.sh b/make.sh new file mode 100644 index 00000000..fb915733 --- /dev/null +++ b/make.sh @@ -0,0 +1,166 @@ +#!/bin/sh +set -e + +# Script inspired by https://gist.github.com/szeidner/613fe4652fc86f083cefa21879d5522b + +readonly PROGNAME=$(basename $0) +readonly WORKING_DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P) + +die() { + echo "$PROGNAME: $*" >&2 + exit 1 +} + +usage() { + if [ "$*" != "" ] ; then + echo "Error: $*" + fi + + cat << EOF +Usage: $PROGNAME --bundle-id [BUNDLE_ID_PRODUCTION] --bundle-id-staging [BUNDLE_ID_STAGING] --project-name [PROJECT_NAME] +Set up an iOS app from tuist template. +Options: +-h, --help display this usage message and exit +-b, --bundle-id [BUNDLE_ID_PRODUCTION] the production id (i.e. com.example.package) +-s, --bundle-id-staging [BUNDLE_ID_STAGING] the staging id (i.e. com.example.package.staging) +-n, --project-name [PROJECT_NAME] the project name (i.e. MyApp) +EOF + exit 1 +} + +bundle_id_production="" +bundle_id_staging="" +project_name="" + +readonly CONSTANT_PROJECT_NAME="{PROJECT_NAME}" +readonly CONSTANT_BUNDLE_PRODUCTION="{BUNDLE_ID_PRODUCTION}" +readonly CONSTANT_BUNDLE_STAGING="{BUNDLE_ID_STAGING}" + +while [ $# -gt 0 ] ; do + case "$1" in + -h|--help) + usage + ;; + -b|--bundle-id) + bundle_id_production="$2" + shift + ;; + -s|--bundle-id-staging) + bundle_id_staging="$2" + shift + ;; + -n|--project-name) + project_name="$2" + shift + ;; + -*) + usage "Unknown option '$1'" + ;; + *) + usage "Too many arguments" + ;; + esac + shift +done + +if [ -z "$bundle_id_production" ] ; then + read -p "BUNDLE ID PRODUCTION (i.e. com.example.project):" bundle_id_production +fi + +if [ -z "$bundle_id_staging" ] ; then + read -p "BUNDLE ID STAGING (i.e. com.example.project.staging):" bundle_id_staging +fi + +if [ -z "$project_name" ] ; then + read -p "PROJECT NAME (i.e. NewProject):" project_name +fi + +if [ -z "$bundle_id_production" ] || [ -z "$bundle_id_staging" ] || [ -z "$project_name" ] ; then + usage "Input cannot be blank." +fi + +# Enforce package name +regex='^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$' +if ! [[ $bundle_id_production =~ $regex ]]; then + die "Invalid Package Name: $bundle_id_production (needs to follow standard pattern {com.example.package})" +fi + +echo "=> 🐢 Starting init $project_name ..." + +# Trim spaces in APP_NAME +readonly PROJECT_NAME_NO_SPACES=$(echo "$project_name" | sed "s/ //g") + +# Rename files structure +echo "=> 🔎 Replacing files structure..." + + +## user define function +rename_folder(){ + local DIR=$1 + local NEW_DIR=$2 + if [ -d "$DIR" ] + then + mv ${DIR} ${NEW_DIR} + fi +} + +# Rename test folder structure +rename_folder "${CONSTANT_PROJECT_NAME}Tests" "${PROJECT_NAME_NO_SPACES}Tests" + +# Rename UI Test folder structure +rename_folder "${CONSTANT_PROJECT_NAME}UITests" "${PROJECT_NAME_NO_SPACES}UITests" + +# Rename app folder structure +rename_folder "${CONSTANT_PROJECT_NAME}" "${PROJECT_NAME_NO_SPACES}" + +# Add AutoMockable.generated.swift file +mkdir -p "${PROJECT_NAME_NO_SPACES}Tests/Sources/Mocks/Sourcery" +touch "${PROJECT_NAME_NO_SPACES}Tests/Sources/Mocks/Sourcery/AutoMockable.generated.swift" + +echo "✅ Completed" + +# Search and replace in files +echo "=> 🔎 Replacing package and package name within files..." +BUNDLE_ID_PRODUCTION_ESCAPED="${bundle_id_production//./\.}" +BUNDLE_ID_STAGING_ESCAPED="${bundle_id_staging//./\.}" +LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_BUNDLE_STAGING/$BUNDLE_ID_STAGING_ESCAPED/g" {} + +LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_BUNDLE_PRODUCTION/$BUNDLE_ID_PRODUCTION_ESCAPED/g" {} + +LC_ALL=C find $WORKING_DIR -type f -exec sed -i "" "s/$CONSTANT_PROJECT_NAME/$PROJECT_NAME_NO_SPACES/g" {} + +echo "✅ Completed" + +# check for tuist and install +if ! command -v tuist &> /dev/null +then + echo "Tuist could not be found" + echo "Installing tuist" + curl -Ls https://install.tuist.io | bash + tuist install 1.48.1 +fi + +# Generate with tuist +echo "Tuist found" +tuist generate +echo "✅ Completed" + +# Install dependencies +echo "Installing gems" +bundle install +echo "Installing pod dependencies" +bundle exec pod install --repo-update +echo "✅ Completed" + +# remove gitkeep files +echo "Remove gitkeep files from project" +sed -i "" "s/.*\(gitkeep\).*,//" $PROJECT_NAME_NO_SPACES.xcodeproj/project.pbxproj +echo "✅ Completed" + +# remove Tuist files +echo "Remove tuist files" +rm -rf tuist +rm -rf Project.swift +rm -rf make.sh +rm -rf .github/workflows/test_install_script.yml +echo "✅ Completed" + +# Done! +echo "=> 🚀 Done! App is ready to be tested 🙌" \ No newline at end of file diff --git a/{PROJECT_NAME}/Configurations/Plists/GoogleService/Production/GoogleService-Info.plist b/{PROJECT_NAME}/Configurations/Plists/GoogleService/Production/GoogleService-Info.plist new file mode 100644 index 00000000..d6f0fc7a --- /dev/null +++ b/{PROJECT_NAME}/Configurations/Plists/GoogleService/Production/GoogleService-Info.plist @@ -0,0 +1,7 @@ + + + + + + + diff --git a/{PROJECT_NAME}/Configurations/Plists/GoogleService/Staging/GoogleService-Info.plist b/{PROJECT_NAME}/Configurations/Plists/GoogleService/Staging/GoogleService-Info.plist new file mode 100644 index 00000000..a71fe808 --- /dev/null +++ b/{PROJECT_NAME}/Configurations/Plists/GoogleService/Staging/GoogleService-Info.plist @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ProjectName/Configurations/Plists/Info.plist b/{PROJECT_NAME}/Configurations/Plists/Info.plist similarity index 100% rename from ProjectName/Configurations/Plists/Info.plist rename to {PROJECT_NAME}/Configurations/Plists/Info.plist diff --git a/ProjectName/Configurations/XCConfigs/DebugProduction.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig similarity index 87% rename from ProjectName/Configurations/XCConfigs/DebugProduction.xcconfig rename to {PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig index 69358a17..a2ec4f41 100644 --- a/ProjectName/Configurations/XCConfigs/DebugProduction.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/DebugProduction.xcconfig @@ -8,5 +8,5 @@ SWIFT_OPTIMIZATION_LEVEL = -Onone GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 PRODUCTION=1 PRODUCT_NAME = $(TARGET_NAME) -PRODUCT_BUNDLE_IDENTIFIER = co.nimblehq.projectname +PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_PRODUCTION} SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG PRODUCTION diff --git a/ProjectName/Configurations/XCConfigs/DebugStaging.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig similarity index 85% rename from ProjectName/Configurations/XCConfigs/DebugStaging.xcconfig rename to {PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig index 569cb8bc..1e627b05 100644 --- a/ProjectName/Configurations/XCConfigs/DebugStaging.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/DebugStaging.xcconfig @@ -8,5 +8,5 @@ SWIFT_OPTIMIZATION_LEVEL = -Onone GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 STAGING=1 PRODUCT_NAME = $(TARGET_NAME) Staging -PRODUCT_BUNDLE_IDENTIFIER = co.nimblehq.projectname.staging +PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_STAGING} SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG STAGING diff --git a/ProjectName/Configurations/XCConfigs/ReleaseProduction.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig similarity index 86% rename from ProjectName/Configurations/XCConfigs/ReleaseProduction.xcconfig rename to {PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig index aba06d10..76b1b92c 100644 --- a/ProjectName/Configurations/XCConfigs/ReleaseProduction.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseProduction.xcconfig @@ -7,5 +7,5 @@ ENABLE_BITCODE = YES GCC_PREPROCESSOR_DEFINITIONS = $(inherited) PRODUCTION=1 PRODUCT_NAME = $(TARGET_NAME) -PRODUCT_BUNDLE_IDENTIFIER = co.nimblehq.projectname +PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_PRODUCTION} SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODUCTION RELEASE diff --git a/ProjectName/Configurations/XCConfigs/ReleaseStaging.xcconfig b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig similarity index 85% rename from ProjectName/Configurations/XCConfigs/ReleaseStaging.xcconfig rename to {PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig index 46ab0abe..68ffed80 100644 --- a/ProjectName/Configurations/XCConfigs/ReleaseStaging.xcconfig +++ b/{PROJECT_NAME}/Configurations/XCConfigs/ReleaseStaging.xcconfig @@ -8,5 +8,5 @@ ENABLE_BITCODE = YES ENABLE_BITCODE = NO GCC_PREPROCESSOR_DEFINITIONS = $(inherited) STAGING=1 PRODUCT_NAME = $(TARGET_NAME) Staging -PRODUCT_BUNDLE_IDENTIFIER = co.nimblehq.projectname.staging +PRODUCT_BUNDLE_IDENTIFIER = {BUNDLE_ID_STAGING} SWIFT_ACTIVE_COMPILATION_CONDITIONS = STAGING RELEASE diff --git a/ProjectName/Resources/Assets/Assets.xcassets/AccentColor.colorset/Contents.json b/{PROJECT_NAME}/Resources/Assets/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from ProjectName/Resources/Assets/Assets.xcassets/AccentColor.colorset/Contents.json rename to {PROJECT_NAME}/Resources/Assets/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/ProjectName/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/{PROJECT_NAME}/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ProjectName/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json rename to {PROJECT_NAME}/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ProjectName/Resources/Assets/Assets.xcassets/Contents.json b/{PROJECT_NAME}/Resources/Assets/Assets.xcassets/Contents.json similarity index 100% rename from ProjectName/Resources/Assets/Assets.xcassets/Contents.json rename to {PROJECT_NAME}/Resources/Assets/Assets.xcassets/Contents.json diff --git a/ProjectName/Resources/LaunchScreen/LaunchScreen.storyboard b/{PROJECT_NAME}/Resources/LaunchScreen/LaunchScreen.storyboard similarity index 100% rename from ProjectName/Resources/LaunchScreen/LaunchScreen.storyboard rename to {PROJECT_NAME}/Resources/LaunchScreen/LaunchScreen.storyboard diff --git a/ProjectName/Sources/Application/AppDelegate.swift b/{PROJECT_NAME}/Sources/Application/AppDelegate.swift similarity index 100% rename from ProjectName/Sources/Application/AppDelegate.swift rename to {PROJECT_NAME}/Sources/Application/AppDelegate.swift diff --git a/ProjectName/Sources/Constants/Constants+API.swift b/{PROJECT_NAME}/Sources/Constants/Constants+API.swift similarity index 100% rename from ProjectName/Sources/Constants/Constants+API.swift rename to {PROJECT_NAME}/Sources/Constants/Constants+API.swift diff --git a/ProjectName/Sources/Constants/Constants.swift b/{PROJECT_NAME}/Sources/Constants/Constants.swift similarity index 100% rename from ProjectName/Sources/Constants/Constants.swift rename to {PROJECT_NAME}/Sources/Constants/Constants.swift diff --git a/ProjectName/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIError.swift diff --git a/ProjectName/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/NetworkAPIProtocol.swift diff --git a/ProjectName/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/Core/RequestConfiguration.swift diff --git a/ProjectName/Sources/Data/NetworkAPI/Interceptors/.gitkeep b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors/.gitkeep similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/Interceptors/.gitkeep rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/Interceptors/.gitkeep diff --git a/ProjectName/Sources/Data/NetworkAPI/Models/.gitkeep b/{PROJECT_NAME}/Sources/Data/NetworkAPI/Models/.gitkeep similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/Models/.gitkeep rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/Models/.gitkeep diff --git a/ProjectName/Sources/Data/NetworkAPI/NetworkAPI.swift b/{PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/NetworkAPI.swift rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/NetworkAPI.swift diff --git a/ProjectName/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep b/{PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep similarity index 100% rename from ProjectName/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep rename to {PROJECT_NAME}/Sources/Data/NetworkAPI/RequestConfigurations/.gitkeep diff --git a/ProjectName/Sources/Data/Repositories/.gitkeep b/{PROJECT_NAME}/Sources/Data/Repositories/.gitkeep similarity index 100% rename from ProjectName/Sources/Data/Repositories/.gitkeep rename to {PROJECT_NAME}/Sources/Data/Repositories/.gitkeep diff --git a/ProjectName/Sources/Domain/Entities/.gitkeep b/{PROJECT_NAME}/Sources/Domain/Entities/.gitkeep similarity index 100% rename from ProjectName/Sources/Domain/Entities/.gitkeep rename to {PROJECT_NAME}/Sources/Domain/Entities/.gitkeep diff --git a/ProjectName/Sources/Domain/Interfaces/.gitkeep b/{PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep similarity index 100% rename from ProjectName/Sources/Domain/Interfaces/.gitkeep rename to {PROJECT_NAME}/Sources/Domain/Interfaces/.gitkeep diff --git a/ProjectName/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift b/{PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift similarity index 100% rename from ProjectName/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift rename to {PROJECT_NAME}/Sources/Domain/UseCases/UseCaseFactoryProtocol.swift diff --git a/ProjectName/Sources/Presentation/Modules/HomeViewController.swift b/{PROJECT_NAME}/Sources/Presentation/Modules/HomeViewController.swift similarity index 100% rename from ProjectName/Sources/Presentation/Modules/HomeViewController.swift rename to {PROJECT_NAME}/Sources/Presentation/Modules/HomeViewController.swift diff --git a/ProjectName/Sources/Presentation/Navigator/Navigator+Scene.swift b/{PROJECT_NAME}/Sources/Presentation/Navigator/Navigator+Scene.swift similarity index 100% rename from ProjectName/Sources/Presentation/Navigator/Navigator+Scene.swift rename to {PROJECT_NAME}/Sources/Presentation/Navigator/Navigator+Scene.swift diff --git a/ProjectName/Sources/Presentation/Navigator/Navigator+Transition.swift b/{PROJECT_NAME}/Sources/Presentation/Navigator/Navigator+Transition.swift similarity index 100% rename from ProjectName/Sources/Presentation/Navigator/Navigator+Transition.swift rename to {PROJECT_NAME}/Sources/Presentation/Navigator/Navigator+Transition.swift diff --git a/ProjectName/Sources/Presentation/Navigator/Navigator.swift b/{PROJECT_NAME}/Sources/Presentation/Navigator/Navigator.swift similarity index 100% rename from ProjectName/Sources/Presentation/Navigator/Navigator.swift rename to {PROJECT_NAME}/Sources/Presentation/Navigator/Navigator.swift diff --git a/ProjectName/Sources/Presentation/Views/.gitkeep b/{PROJECT_NAME}/Sources/Presentation/Views/.gitkeep similarity index 100% rename from ProjectName/Sources/Presentation/Views/.gitkeep rename to {PROJECT_NAME}/Sources/Presentation/Views/.gitkeep diff --git a/ProjectName/Sources/Supports/Extensions/Foundation/Optional+Unwrap.swift b/{PROJECT_NAME}/Sources/Supports/Extensions/Foundation/Optional+Unwrap.swift similarity index 100% rename from ProjectName/Sources/Supports/Extensions/Foundation/Optional+Unwrap.swift rename to {PROJECT_NAME}/Sources/Supports/Extensions/Foundation/Optional+Unwrap.swift diff --git a/ProjectName/Sources/Supports/Extensions/UIKit/Color+Application.swift b/{PROJECT_NAME}/Sources/Supports/Extensions/UIKit/Color+Application.swift similarity index 100% rename from ProjectName/Sources/Supports/Extensions/UIKit/Color+Application.swift rename to {PROJECT_NAME}/Sources/Supports/Extensions/UIKit/Color+Application.swift diff --git a/ProjectName/Sources/Supports/Extensions/UIKit/UIView+Subviews.swift b/{PROJECT_NAME}/Sources/Supports/Extensions/UIKit/UIView+Subviews.swift similarity index 100% rename from ProjectName/Sources/Supports/Extensions/UIKit/UIView+Subviews.swift rename to {PROJECT_NAME}/Sources/Supports/Extensions/UIKit/UIView+Subviews.swift diff --git a/ProjectName/Sources/Supports/Helpers/Typealiases/Typealiases.swift b/{PROJECT_NAME}/Sources/Supports/Helpers/Typealiases/Typealiases.swift similarity index 100% rename from ProjectName/Sources/Supports/Helpers/Typealiases/Typealiases.swift rename to {PROJECT_NAME}/Sources/Supports/Helpers/Typealiases/Typealiases.swift diff --git a/ProjectNameTests/Configurations/Plists/Info.plist b/{PROJECT_NAME}Tests/Configurations/Plists/Info.plist similarity index 100% rename from ProjectNameTests/Configurations/Plists/Info.plist rename to {PROJECT_NAME}Tests/Configurations/Plists/Info.plist diff --git a/ProjectNameTests/Resources/.gitkeep b/{PROJECT_NAME}Tests/Resources/.gitkeep similarity index 100% rename from ProjectNameTests/Resources/.gitkeep rename to {PROJECT_NAME}Tests/Resources/.gitkeep diff --git a/ProjectNameTests/Sources/Dummy/.gitkeep b/{PROJECT_NAME}Tests/Sources/Dummy/.gitkeep similarity index 100% rename from ProjectNameTests/Sources/Dummy/.gitkeep rename to {PROJECT_NAME}Tests/Sources/Dummy/.gitkeep diff --git a/{PROJECT_NAME}Tests/Sources/Specs/Supports/Extensions/Foundation/OptionalUnwrapSpec.swift b/{PROJECT_NAME}Tests/Sources/Specs/Supports/Extensions/Foundation/OptionalUnwrapSpec.swift new file mode 100644 index 00000000..58221e9c --- /dev/null +++ b/{PROJECT_NAME}Tests/Sources/Specs/Supports/Extensions/Foundation/OptionalUnwrapSpec.swift @@ -0,0 +1,34 @@ +import Nimble +import Quick + +@testable import {PROJECT_NAME} + +final class OptionalUnwrapSpec: QuickSpec { + + override func spec() { + + describe("an string optional") { + var value: String? + + context("when value is not nil") { + beforeEach { + value = "hello world" + } + + it("returns string with unwrap value") { + expect(value.string) == "hello world" + } + } + + context("when value is nil") { + beforeEach { + value = nil + } + + it("returns empty string") { + expect(value.string) == "" + } + } + } + } +} diff --git a/ProjectNameTests/Sources/Specs/.gitkeep b/{PROJECT_NAME}Tests/Sources/Utilities/.gitkeep similarity index 100% rename from ProjectNameTests/Sources/Specs/.gitkeep rename to {PROJECT_NAME}Tests/Sources/Utilities/.gitkeep diff --git a/ProjectNameUITests/Configurations/Plists/Info.plist b/{PROJECT_NAME}UITests/Configurations/Plists/Info.plist similarity index 100% rename from ProjectNameUITests/Configurations/Plists/Info.plist rename to {PROJECT_NAME}UITests/Configurations/Plists/Info.plist diff --git a/ProjectNameTests/Sources/Utilities/.gitkeep b/{PROJECT_NAME}UITests/Resources/.gitkeep similarity index 100% rename from ProjectNameTests/Sources/Utilities/.gitkeep rename to {PROJECT_NAME}UITests/Resources/.gitkeep diff --git a/{PROJECT_NAME}UITests/Sources/Specs/Application/ApplicationSpec.swift b/{PROJECT_NAME}UITests/Sources/Specs/Application/ApplicationSpec.swift new file mode 100644 index 00000000..8cac465b --- /dev/null +++ b/{PROJECT_NAME}UITests/Sources/Specs/Application/ApplicationSpec.swift @@ -0,0 +1,30 @@ +import Foundation +import Nimble +import Quick + +final class ApplicationSpec: QuickSpec { + + override func spec() { + + var app: XCUIApplication! + + describe("a {PROJECT_NAME} app") { + + beforeEach { + app = XCUIApplication() + app.launch() + } + + afterEach { + app.terminate() + } + + context("when opens") { + + it("empty tests") { + expect(true) == true + } + } + } + } +}