From 3d86317f32ad7cca6621d1bb7fbf060b83577354 Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Fri, 10 May 2024 15:09:25 +0200 Subject: [PATCH 1/4] Update template project The template now has: * An AppDelegate * Localization for English and Dutch * Default tint color and icon * Unit tests, UI tests with Salad and a test plan * GitHub Actions configuration * Default files for: Info.plist, launch screen storyboard, entitlements * Rename script * Linting and formatting configuration --- .github/actions/code-signing-setup/action.yml | 39 +++++ .github/workflows/build.yml | 116 +++++++++++++++ .github/workflows/test.yml | 57 ++++++++ .swiftformat | 3 + .swiftlint.yml | 4 + ExportOptions.plist | 22 +++ README.md | 45 ++++++ TemplateApp.xcodeproj/project.pbxproj | 138 +++++++++++++----- .../xcshareddata/swiftpm/Package.resolved | 15 ++ .../xcschemes/TemplateApp.xcscheme | 107 ++++++++++++++ TemplateApp/AllTests.xctestplan | 41 ++++++ .../AccentColor.colorset/Contents.json | 9 ++ .../AppIcon.appiconset/Contents.json | 1 + .../AppIcon.appiconset/q42-icon.png | Bin 0 -> 49055 bytes TemplateApp/Info.plist | 5 + TemplateApp/Launch Screen.storyboard | 48 ++++++ TemplateApp/Localizable.xcstrings | 16 ++ .../{ContentView.swift => RootView.swift} | 10 +- TemplateApp/TemplateApp.entitlements | 5 + TemplateApp/TemplateAppApp.swift | 6 +- TemplateApp/TemplateAppAppDelegate.swift | 15 ++ ...plateAppTests.swift => ExampleTests.swift} | 6 +- TemplateAppUITests/ExampleUITests.swift | 36 +++++ TemplateAppUITests/TemplateAppUITests.swift | 33 ----- TemplateAppUITests/ViewObjects/RootView.swift | 15 ++ scripts/rename-project.py | 64 ++++++++ 26 files changed, 779 insertions(+), 77 deletions(-) create mode 100644 .github/actions/code-signing-setup/action.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/test.yml create mode 100644 .swiftformat create mode 100644 .swiftlint.yml create mode 100644 ExportOptions.plist create mode 100644 README.md create mode 100644 TemplateApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 TemplateApp.xcodeproj/xcshareddata/xcschemes/TemplateApp.xcscheme create mode 100644 TemplateApp/AllTests.xctestplan create mode 100644 TemplateApp/Assets.xcassets/AppIcon.appiconset/q42-icon.png create mode 100644 TemplateApp/Info.plist create mode 100644 TemplateApp/Launch Screen.storyboard create mode 100644 TemplateApp/Localizable.xcstrings rename TemplateApp/{ContentView.swift => RootView.swift} (58%) create mode 100644 TemplateApp/TemplateApp.entitlements create mode 100644 TemplateApp/TemplateAppAppDelegate.swift rename TemplateAppTests/{TemplateAppTests.swift => ExampleTests.swift} (92%) create mode 100644 TemplateAppUITests/ExampleUITests.swift delete mode 100644 TemplateAppUITests/TemplateAppUITests.swift create mode 100644 TemplateAppUITests/ViewObjects/RootView.swift create mode 100644 scripts/rename-project.py diff --git a/.github/actions/code-signing-setup/action.yml b/.github/actions/code-signing-setup/action.yml new file mode 100644 index 0000000..c5cfd1f --- /dev/null +++ b/.github/actions/code-signing-setup/action.yml @@ -0,0 +1,39 @@ +name: Set up Apple code signing +description: Installs code signing certificate(s) to a temporary keychain +inputs: + build-certificate-base64: + required: true + description: The base64-encoded p12 build certificate + p12-password: + required: true + description: The password for the p12 build certificate + keychain-password: + required: true + description: The password for the temporary keychain +runs: + using: "composite" + steps: + - name: Install code signing certificate + env: + BUILD_CERTIFICATE_BASE64: ${{ inputs.build-certificate-base64 }} + P12_PASSWORD: ${{ inputs.p12-password }} + KEYCHAIN_PASSWORD: ${{ inputs.keychain-password }} + shell: bash + # Source: + # https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development + run: | + # Create variables + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + # Import certificate from secrets + echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH + + # Create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # Import certificate to keychain + security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..b8fa444 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,116 @@ +name: Run tests and deploy to TestFlight + +on: + workflow_dispatch: + # Uncomment this to run the workflow on every push to the main branch: + # push: + # branches: ["main"] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + XCODE_PROJECT: TemplateApp.xcodeproj + SCHEME: TemplateApp + TEST_PLAN: AllTests + TEST_RESULT_BUNDLE: TestResults.xcresult + ARCHIVE_PATH: TemplateApp.xcarchive + EXPORT_OPTIONS_PLIST: ExportOptions.plist + +jobs: + build: + name: Run tests and deploy to TestFlight + runs-on: [macOS] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache Swift Package Manager dependencies + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/SourcePackages + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + restore-keys: | + ${{ runner.os }}-spm- + + - name: Install Swift packages + run: | + xcodebuild -project "${{ env.XCODE_PROJECT }}" \ + -scheme "${{ env.SCHEME }}" \ + -onlyUsePackageVersionsFromResolvedFile \ + -resolvePackageDependencies \ + -clonedSourcePackagesDirPath "${{ runner.temp }}/SourcePackages" + + - name: Install code signing certificate + uses: "./.github/actions/code-signing-setup" + with: + build-certificate-base64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} + p12-password: ${{ secrets.P12_PASSWORD }} + keychain-password: ${{ secrets.KEYCHAIN_PASSWORD }} + + - name: Install App Store Connect API key + run: | + echo -n "${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}" | base64 --decode -o "${{ runner.temp }}/AuthKey.p8" + + - name: Run tests + env: + PLATFORM: ${{ 'iOS Simulator' }} + run: | + # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) + DEVICE=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` + xcodebuild test -project "${{ env.XCODE_PROJECT }}" \ + -scheme "${{ env.SCHEME }}" \ + -testPlan "${{ env.TEST_PLAN }}" \ + -destination "platform=$PLATFORM,name=$DEVICE" \ + -resultBundlePath "${{ env.TEST_RESULT_BUNDLE }}" \ + -clonedSourcePackagesDirPath "${{ runner.temp }}/SourcePackages" \ + -disableAutomaticPackageResolution + + - name: Upload test result bundle + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: ${{ env.SCHEME }}-${{ github.ref_name }}-${{ github.sha }}.xcresult + path: ${{ env.TEST_RESULT_BUNDLE }} + + - name: Archive build + run: | + xcodebuild clean archive -project "${{ env.XCODE_PROJECT }}" \ + -scheme "${{ env.SCHEME }}" \ + -destination generic/platform=iOS \ + -archivePath "${{ runner.temp }}/${{ env.ARCHIVE_PATH }}" \ + -allowProvisioningUpdates \ + -authenticationKeyPath "${{ runner.temp }}/AuthKey.p8" \ + -authenticationKeyID ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} \ + -authenticationKeyIssuerID ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} \ + -clonedSourcePackagesDirPath "${{ runner.temp }}/SourcePackages" \ + -disableAutomaticPackageResolution + + - name: Upload to TestFlight + run: | + xcodebuild -exportArchive \ + -allowProvisioningUpdates \ + -authenticationKeyPath "${{ runner.temp }}/AuthKey.p8" \ + -authenticationKeyID ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} \ + -authenticationKeyIssuerID ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} \ + -archivePath "${{ runner.temp }}/${{ env.ARCHIVE_PATH }}" \ + -exportPath ${{ env.ARCHIVE_PATH }} \ + -exportOptionsPlist ${{ env.EXPORT_OPTIONS_PLIST }} + + # To add Crashlytics dSYM upload, grap the `upload-symbols` script from the Firebase SDK from here: + # https://github.com/firebase/firebase-ios-sdk/blob/main/Crashlytics/upload-symbols + # and add it to the repository in a folder called `scripts`. Then uncomment the following lines: + + # - name: Upload dSYM files to Firebase Crashlytics + # run: | + # ./scripts/upload-symbols -p ios \ + # -gsp "TemplateApp/GoogleService-Info.plist" \ + # "${{ runner.temp }}/${{ env.ARCHIVE_PATH }}/dSYMs" + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.SCHEME }}-${{ github.ref_name }}-${{ github.sha }}.xcarchive + path: ${{ runner.temp }}/${{ env.ARCHIVE_PATH }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..93a8d4c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,57 @@ +name: Run tests + +on: + pull_request: + branches: ["main"] + +env: + XCODE_PROJECT: TemplateApp.xcodeproj + SCHEME: TemplateApp + TEST_PLAN: AllTests + TEST_RESULT_BUNDLE: TestResults.xcresult + +jobs: + build: + name: Run tests + runs-on: [macOS] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache Swift Package Manager dependencies + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/SourcePackages + key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + restore-keys: | + ${{ runner.os }}-spm- + + - name: Install Swift packages + run: | + xcodebuild -project "${{ env.XCODE_PROJECT }}" \ + -scheme "${{ env.SCHEME }}" \ + -onlyUsePackageVersionsFromResolvedFile \ + -resolvePackageDependencies \ + -clonedSourcePackagesDirPath "${{ runner.temp }}/SourcePackages" + + - name: Run tests + env: + PLATFORM: ${{ 'iOS Simulator' }} + run: | + # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959) + DEVICE=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"` + xcodebuild test -project "${{ env.XCODE_PROJECT }}" \ + -scheme "${{ env.SCHEME }}" \ + -testPlan "${{ env.TEST_PLAN }}" \ + -destination "platform=$PLATFORM,name=$DEVICE" \ + -resultBundlePath "${{ env.TEST_RESULT_BUNDLE }}" \ + -clonedSourcePackagesDirPath "${{ runner.temp }}/SourcePackages" \ + -disableAutomaticPackageResolution + + - name: Upload test result bundle + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: ${{ env.SCHEME }}-${{ github.ref_name }}-${{ github.sha }}.xcresult + path: ${{ env.TEST_RESULT_BUNDLE }} diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000..5ffbfff --- /dev/null +++ b/.swiftformat @@ -0,0 +1,3 @@ +--disable unusedArguments +--disable redundantRawValues +--indent 4 diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..7ca66e6 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,4 @@ +disabled_rules: + - trailing_comma + - line_length + - unused_closure_parameter diff --git a/ExportOptions.plist b/ExportOptions.plist new file mode 100644 index 0000000..4238904 --- /dev/null +++ b/ExportOptions.plist @@ -0,0 +1,22 @@ + + + + + destination + upload + manageAppVersionAndBuildNumber + + method + app-store + signingStyle + automatic + stripSwiftSymbols + + teamID + XXXXXXXXXX + testFlightInternalTestingOnly + + uploadSymbols + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7b7758 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Q42 iOS template + +This is a template for creating iOS projects at Q42. It has opinionated defaults and boilerplate, based on how we do iOS projects at Q42. + +## How to use it + +1. In GitHub, press "use this template" to create a new repository. +2. Rename your project using the included Python script. + +## Features + +Only basic features that almost all projects use, were added in this template: + +* SwiftUI using the SwiftUI lifecycle with an AppDelegate +* Dependency injection using Factory +* Unit tests and UI tests using Salad +* GitHub Actions CI configuration that runs the tests and submits the app to TestFlight + +Xcode 15.3 or higher is required. + +## Code style + +The Xcode project is configured to use 4 spaces for indentation. +For linting Swift source code, we use [SwiftLint](https://github.com/realm/SwiftLint). +A configuration for [SwiftFormat](http://github.com/nicklockwood/SwiftFormat) is also included. + +## Continuous integration + +GitHub Actions is used for continuous integration (CI). The CI runs the automated tests when you make a pull request. +On a push to the `main` branch, it will also run the tests, and if they pass, a build of the app is made and uploaded to TestFlight. + +### CI configuration + +Five environment secrets are needed for the workflow to run on GitHub Actions. +You may configure these in the repository secret settings on GitHub. + +* `BUILD_CERTIFICATE_BASE64` contains a base64-encoded string of the .p12 certificate bundle, used to code sign the app. This bundle needs to contain two certificates: **development** and **distribution**. +* `P12_PASSWORD` contains the password of the certificate bundle. +* `APP_STORE_CONNECT_API_KEY_BASE64` contains a base64-encoded string of the .p8 App Store Connect API key. +* `APP_STORE_CONNECT_API_KEY_ID` contains the key ID of the App Store Connect API key. +* `APP_STORE_CONNECT_API_KEY_ISSUER_ID` contains the issuer ID of the App Store Connect API key. + +To create such a certificate bundle, open Keychain Access. Unfold the entries for the development and distribution certificate. Select the certificates and their private keys using shift, then right-click and select "Export 4 items...". + +You can encode a file to base64 on the command line like this: `base64 -i ~/Desktop/Certificates.p12 | pbcopy`. This automatically puts the result on your clipboard. diff --git a/TemplateApp.xcodeproj/project.pbxproj b/TemplateApp.xcodeproj/project.pbxproj index e3bed72..d09333d 100644 --- a/TemplateApp.xcodeproj/project.pbxproj +++ b/TemplateApp.xcodeproj/project.pbxproj @@ -3,16 +3,21 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ D5284F302B57C6B600BB32E7 /* TemplateAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F2F2B57C6B600BB32E7 /* TemplateAppApp.swift */; }; - D5284F322B57C6B600BB32E7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F312B57C6B600BB32E7 /* ContentView.swift */; }; + D5284F322B57C6B600BB32E7 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F312B57C6B600BB32E7 /* RootView.swift */; }; D5284F342B57C6B700BB32E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D5284F332B57C6B700BB32E7 /* Assets.xcassets */; }; D5284F372B57C6B700BB32E7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D5284F362B57C6B700BB32E7 /* Preview Assets.xcassets */; }; - D5284F412B57C6B700BB32E7 /* TemplateAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F402B57C6B700BB32E7 /* TemplateAppTests.swift */; }; - D5284F4B2B57C6B700BB32E7 /* TemplateAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F4A2B57C6B700BB32E7 /* TemplateAppUITests.swift */; }; + D5284F412B57C6B700BB32E7 /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F402B57C6B700BB32E7 /* ExampleTests.swift */; }; + D5284F4B2B57C6B700BB32E7 /* ExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5284F4A2B57C6B700BB32E7 /* ExampleUITests.swift */; }; + D5F745E82BEE14870064F06A /* TemplateAppAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F745E72BEE14870064F06A /* TemplateAppAppDelegate.swift */; }; + D5F745EA2BEE14DD0064F06A /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D5F745E92BEE14DD0064F06A /* Localizable.xcstrings */; }; + D5F745EF2BEE15C20064F06A /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D5F745EE2BEE15C20064F06A /* Launch Screen.storyboard */; }; + D5F745F52BEE48BC0064F06A /* Salad in Frameworks */ = {isa = PBXBuildFile; productRef = D5F745F42BEE48BC0064F06A /* Salad */; }; + D5F745F82BEE48F50064F06A /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F745F72BEE48F50064F06A /* RootView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -35,13 +40,21 @@ /* Begin PBXFileReference section */ D5284F2C2B57C6B600BB32E7 /* TemplateApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TemplateApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; D5284F2F2B57C6B600BB32E7 /* TemplateAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateAppApp.swift; sourceTree = ""; }; - D5284F312B57C6B600BB32E7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + D5284F312B57C6B600BB32E7 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; D5284F332B57C6B700BB32E7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D5284F362B57C6B700BB32E7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; D5284F3C2B57C6B700BB32E7 /* TemplateAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TemplateAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D5284F402B57C6B700BB32E7 /* TemplateAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateAppTests.swift; sourceTree = ""; }; + D5284F402B57C6B700BB32E7 /* ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTests.swift; sourceTree = ""; }; D5284F462B57C6B700BB32E7 /* TemplateAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TemplateAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D5284F4A2B57C6B700BB32E7 /* TemplateAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateAppUITests.swift; sourceTree = ""; }; + D5284F4A2B57C6B700BB32E7 /* ExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleUITests.swift; sourceTree = ""; }; + D5F745E72BEE14870064F06A /* TemplateAppAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateAppAppDelegate.swift; sourceTree = ""; }; + D5F745E92BEE14DD0064F06A /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + D5F745EB2BEE15210064F06A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + D5F745EE2BEE15C20064F06A /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; + D5F745F02BEE15D70064F06A /* AllTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AllTests.xctestplan; sourceTree = ""; }; + D5F745F12BEE16E60064F06A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + D5F745F22BEE16FB0064F06A /* TemplateApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TemplateApp.entitlements; sourceTree = ""; }; + D5F745F72BEE48F50064F06A /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -63,6 +76,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D5F745F52BEE48BC0064F06A /* Salad in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -72,6 +86,7 @@ D5284F232B57C6B600BB32E7 = { isa = PBXGroup; children = ( + D5F745EB2BEE15210064F06A /* README.md */, D5284F2E2B57C6B600BB32E7 /* TemplateApp */, D5284F3F2B57C6B700BB32E7 /* TemplateAppTests */, D5284F492B57C6B700BB32E7 /* TemplateAppUITests */, @@ -95,8 +110,11 @@ isa = PBXGroup; children = ( D5284F2F2B57C6B600BB32E7 /* TemplateAppApp.swift */, - D5284F312B57C6B600BB32E7 /* ContentView.swift */, + D5F745E72BEE14870064F06A /* TemplateAppAppDelegate.swift */, + D5284F312B57C6B600BB32E7 /* RootView.swift */, + D5F745E92BEE14DD0064F06A /* Localizable.xcstrings */, D5284F332B57C6B700BB32E7 /* Assets.xcassets */, + D5F745ED2BEE15B80064F06A /* Supporting Files */, D5284F352B57C6B700BB32E7 /* Preview Content */, ); path = TemplateApp; @@ -113,7 +131,7 @@ D5284F3F2B57C6B700BB32E7 /* TemplateAppTests */ = { isa = PBXGroup; children = ( - D5284F402B57C6B700BB32E7 /* TemplateAppTests.swift */, + D5284F402B57C6B700BB32E7 /* ExampleTests.swift */, ); path = TemplateAppTests; sourceTree = ""; @@ -121,11 +139,31 @@ D5284F492B57C6B700BB32E7 /* TemplateAppUITests */ = { isa = PBXGroup; children = ( - D5284F4A2B57C6B700BB32E7 /* TemplateAppUITests.swift */, + D5284F4A2B57C6B700BB32E7 /* ExampleUITests.swift */, + D5F745F62BEE48EF0064F06A /* ViewObjects */, ); path = TemplateAppUITests; sourceTree = ""; }; + D5F745ED2BEE15B80064F06A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D5F745F02BEE15D70064F06A /* AllTests.xctestplan */, + D5F745F12BEE16E60064F06A /* Info.plist */, + D5F745EE2BEE15C20064F06A /* Launch Screen.storyboard */, + D5F745F22BEE16FB0064F06A /* TemplateApp.entitlements */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D5F745F62BEE48EF0064F06A /* ViewObjects */ = { + isa = PBXGroup; + children = ( + D5F745F72BEE48F50064F06A /* RootView.swift */, + ); + path = ViewObjects; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -178,6 +216,9 @@ D5284F482B57C6B700BB32E7 /* PBXTargetDependency */, ); name = TemplateAppUITests; + packageProductDependencies = ( + D5F745F42BEE48BC0064F06A /* Salad */, + ); productName = TemplateAppUITests; productReference = D5284F462B57C6B700BB32E7 /* TemplateAppUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; @@ -207,14 +248,18 @@ }; }; buildConfigurationList = D5284F272B57C6B600BB32E7 /* Build configuration list for PBXProject "TemplateApp" */; - compatibilityVersion = "Xcode 14.0"; + compatibilityVersion = "Xcode 15.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, + nl, ); mainGroup = D5284F232B57C6B600BB32E7; + packageReferences = ( + D5F745F32BEE48BC0064F06A /* XCRemoteSwiftPackageReference "Salad" */, + ); productRefGroup = D5284F2D2B57C6B600BB32E7 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -233,6 +278,8 @@ files = ( D5284F372B57C6B700BB32E7 /* Preview Assets.xcassets in Resources */, D5284F342B57C6B700BB32E7 /* Assets.xcassets in Resources */, + D5F745EF2BEE15C20064F06A /* Launch Screen.storyboard in Resources */, + D5F745EA2BEE14DD0064F06A /* Localizable.xcstrings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -257,7 +304,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D5284F322B57C6B600BB32E7 /* ContentView.swift in Sources */, + D5F745E82BEE14870064F06A /* TemplateAppAppDelegate.swift in Sources */, + D5284F322B57C6B600BB32E7 /* RootView.swift in Sources */, D5284F302B57C6B600BB32E7 /* TemplateAppApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -266,7 +314,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D5284F412B57C6B700BB32E7 /* TemplateAppTests.swift in Sources */, + D5284F412B57C6B700BB32E7 /* ExampleTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -274,7 +322,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D5284F4B2B57C6B700BB32E7 /* TemplateAppUITests.swift in Sources */, + D5F745F82BEE48F50064F06A /* RootView.swift in Sources */, + D5284F4B2B57C6B700BB32E7 /* ExampleUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -346,7 +395,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -403,7 +452,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -418,12 +467,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = TemplateApp/TemplateApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 1.2.3; DEVELOPMENT_ASSET_PATHS = "\"TemplateApp/Preview Content\""; - DEVELOPMENT_TEAM = 8J6VSUTQNX; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = TemplateApp/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -447,12 +498,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = TemplateApp/TemplateApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 1.2.3; DEVELOPMENT_ASSET_PATHS = "\"TemplateApp/Preview Content\""; - DEVELOPMENT_TEAM = 8J6VSUTQNX; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = TemplateApp/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -477,12 +530,11 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8J6VSUTQNX; + CURRENT_PROJECT_VERSION = 1.2.3; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateAppTests; + PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateApp.tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -497,12 +549,11 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8J6VSUTQNX; + CURRENT_PROJECT_VERSION = 1.2.3; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateAppTests; + PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateApp.tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -516,11 +567,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8J6VSUTQNX; + CURRENT_PROJECT_VERSION = 1.2.3; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateAppUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateApp.uitests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -534,11 +585,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8J6VSUTQNX; + CURRENT_PROJECT_VERSION = 1.2.3; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateAppUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.q42.TemplateApp.uitests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -587,6 +638,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + D5F745F32BEE48BC0064F06A /* XCRemoteSwiftPackageReference "Salad" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Q42/Salad.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + D5F745F42BEE48BC0064F06A /* Salad */ = { + isa = XCSwiftPackageProductDependency; + package = D5F745F32BEE48BC0064F06A /* XCRemoteSwiftPackageReference "Salad" */; + productName = Salad; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = D5284F242B57C6B600BB32E7 /* Project object */; } diff --git a/TemplateApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TemplateApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..e3436c6 --- /dev/null +++ b/TemplateApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "929d2090e52375231c5ebc33be1479d4266be85bbf192a8f578aa5ddb5af112c", + "pins" : [ + { + "identity" : "salad", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Q42/Salad.git", + "state" : { + "revision" : "c10580e0632ddaf1542334d386ee3118e1074204", + "version" : "1.0.0" + } + } + ], + "version" : 3 +} diff --git a/TemplateApp.xcodeproj/xcshareddata/xcschemes/TemplateApp.xcscheme b/TemplateApp.xcodeproj/xcshareddata/xcschemes/TemplateApp.xcscheme new file mode 100644 index 0000000..d894ae9 --- /dev/null +++ b/TemplateApp.xcodeproj/xcshareddata/xcschemes/TemplateApp.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TemplateApp/AllTests.xctestplan b/TemplateApp/AllTests.xctestplan new file mode 100644 index 0000000..d641a47 --- /dev/null +++ b/TemplateApp/AllTests.xctestplan @@ -0,0 +1,41 @@ +{ + "configurations" : [ + { + "id" : "2E268531-76BE-4986-BE2E-F7971FD484AB", + "name" : "English", + "options" : { + "language" : "en", + "region" : "GB" + } + }, + { + "enabled" : false, + "id" : "AEB26D7A-9FE8-4E6A-B095-01F3DA26EFCA", + "name" : "Dutch", + "options" : { + "language" : "nl", + "region" : "NL" + } + } + ], + "defaultOptions" : { + "testTimeoutsEnabled" : true + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:TemplateApp.xcodeproj", + "identifier" : "D5284F3B2B57C6B700BB32E7", + "name" : "TemplateAppTests" + } + }, + { + "target" : { + "containerPath" : "container:TemplateApp.xcodeproj", + "identifier" : "D5284F452B57C6B700BB32E7", + "name" : "TemplateAppUITests" + } + } + ], + "version" : 1 +} diff --git a/TemplateApp/Assets.xcassets/AccentColor.colorset/Contents.json b/TemplateApp/Assets.xcassets/AccentColor.colorset/Contents.json index eb87897..9d08b4a 100644 --- a/TemplateApp/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/TemplateApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -1,6 +1,15 @@ { "colors" : [ { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "45", + "green" : "188", + "red" : "132" + } + }, "idiom" : "universal" } ], diff --git a/TemplateApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/TemplateApp/Assets.xcassets/AppIcon.appiconset/Contents.json index 13613e3..11f81c8 100644 --- a/TemplateApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/TemplateApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "q42-icon.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/TemplateApp/Assets.xcassets/AppIcon.appiconset/q42-icon.png b/TemplateApp/Assets.xcassets/AppIcon.appiconset/q42-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b95fce964bc3c631987778ed53cd47dd6b9d2f8c GIT binary patch literal 49055 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 + + + + diff --git a/TemplateApp/Launch Screen.storyboard b/TemplateApp/Launch Screen.storyboard new file mode 100644 index 0000000..dccc0e7 --- /dev/null +++ b/TemplateApp/Launch Screen.storyboard @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TemplateApp/Localizable.xcstrings b/TemplateApp/Localizable.xcstrings new file mode 100644 index 0000000..e023044 --- /dev/null +++ b/TemplateApp/Localizable.xcstrings @@ -0,0 +1,16 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "Hello, world!" : { + "localizations" : { + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hallo, wereld!" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/TemplateApp/ContentView.swift b/TemplateApp/RootView.swift similarity index 58% rename from TemplateApp/ContentView.swift rename to TemplateApp/RootView.swift index 9fc9857..e05e76c 100644 --- a/TemplateApp/ContentView.swift +++ b/TemplateApp/RootView.swift @@ -1,13 +1,13 @@ // -// ContentView.swift +// RootView.swift // TemplateApp // -// Created by Mathijs Bernson on 17/01/2024. +// Copyright © 2024 Q42. All rights reserved. // import SwiftUI -struct ContentView: View { +struct RootView: View { var body: some View { VStack { Image(systemName: "globe") @@ -16,9 +16,11 @@ struct ContentView: View { Text("Hello, world!") } .padding() + .accessibilityElement(children: .contain) + .accessibilityIdentifier("RootView") } } #Preview { - ContentView() + RootView() } diff --git a/TemplateApp/TemplateApp.entitlements b/TemplateApp/TemplateApp.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/TemplateApp/TemplateApp.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/TemplateApp/TemplateAppApp.swift b/TemplateApp/TemplateAppApp.swift index 79e68dc..faeac5f 100644 --- a/TemplateApp/TemplateAppApp.swift +++ b/TemplateApp/TemplateAppApp.swift @@ -2,16 +2,18 @@ // TemplateAppApp.swift // TemplateApp // -// Created by Mathijs Bernson on 17/01/2024. +// Copyright © 2024 Q42. All rights reserved. // import SwiftUI @main struct TemplateAppApp: App { + @UIApplicationDelegateAdaptor var appDelegate: TemplateAppAppDelegate + var body: some Scene { WindowGroup { - ContentView() + RootView() } } } diff --git a/TemplateApp/TemplateAppAppDelegate.swift b/TemplateApp/TemplateAppAppDelegate.swift new file mode 100644 index 0000000..67d4d6b --- /dev/null +++ b/TemplateApp/TemplateAppAppDelegate.swift @@ -0,0 +1,15 @@ +// +// TemplateAppAppDelegate.swift +// TemplateApp +// +// Copyright © 2024 Q42. All rights reserved. +// + +import UIKit + +class TemplateAppAppDelegate: NSObject, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + // Additional app setup can be performed here. + return true + } +} diff --git a/TemplateAppTests/TemplateAppTests.swift b/TemplateAppTests/ExampleTests.swift similarity index 92% rename from TemplateAppTests/TemplateAppTests.swift rename to TemplateAppTests/ExampleTests.swift index 2d734be..c14976d 100644 --- a/TemplateAppTests/TemplateAppTests.swift +++ b/TemplateAppTests/ExampleTests.swift @@ -1,15 +1,14 @@ // -// TemplateAppTests.swift +// ExampleTests.swift // TemplateAppTests // -// Created by Mathijs Bernson on 17/01/2024. +// Copyright © 2024 Q42. All rights reserved. // import XCTest @testable import TemplateApp final class TemplateAppTests: XCTestCase { - override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. } @@ -26,5 +25,4 @@ final class TemplateAppTests: XCTestCase { // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. XCTAssertEqual(21 * 2, 42) } - } diff --git a/TemplateAppUITests/ExampleUITests.swift b/TemplateAppUITests/ExampleUITests.swift new file mode 100644 index 0000000..09c1540 --- /dev/null +++ b/TemplateAppUITests/ExampleUITests.swift @@ -0,0 +1,36 @@ +// +// TemplateAppUITests.swift +// TemplateAppUITests +// +// Created by Mathijs Bernson on 17/01/2024. +// + +// +// ExampleUITests.swift +// TemplateAppTests +// +// Copyright © 2024 Q42. All rights reserved. +// + +import XCTest +import Salad + +final class ExampleUITests: XCTestCase { + var scenario: Scenario! + + override func setUp() { + continueAfterFailure = false + + let app = XCUIApplication() + app.launch() + scenario = Scenario(given: app) + } + + func testExample() { + scenario + .then { rootView in + XCTAssertTrue(rootView.identifyingElement.staticTexts["Hello, world!"].waitForExist(timeout: .asyncUI), + "Expected to see 'Hello, world!' label") + } + } +} diff --git a/TemplateAppUITests/TemplateAppUITests.swift b/TemplateAppUITests/TemplateAppUITests.swift deleted file mode 100644 index 412d550..0000000 --- a/TemplateAppUITests/TemplateAppUITests.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// TemplateAppUITests.swift -// TemplateAppUITests -// -// Created by Mathijs Bernson on 17/01/2024. -// - -import XCTest - -final class TemplateAppUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - -} diff --git a/TemplateAppUITests/ViewObjects/RootView.swift b/TemplateAppUITests/ViewObjects/RootView.swift new file mode 100644 index 0000000..8f775d0 --- /dev/null +++ b/TemplateAppUITests/ViewObjects/RootView.swift @@ -0,0 +1,15 @@ +// +// RootView.swift +// TemplateAppUITests +// +// Created by Mathijs Bernson on 10/05/2024. +// Copyright © 2024 Q42. All rights reserved. +// + +import XCTest +import Salad + +struct RootView: ViewObject { + let root: XCUIElement + let identifyingElementId: String = "RootView" +} diff --git a/scripts/rename-project.py b/scripts/rename-project.py new file mode 100644 index 0000000..011fd89 --- /dev/null +++ b/scripts/rename-project.py @@ -0,0 +1,64 @@ +""" +This script renames the template project to the desired name. +""" + +import os +from pathlib import Path + +folder = Path(os.path.abspath(os.path.dirname(__file__))).parent.as_posix() + +dryRun = False +oldProjectName = "TemplateApp" +print("Enter new project name:") +newProjectName = input() + +# ========= Rename folders: + +print( + "\nRenaming '%s' to '%s' in folder names.\n" % (oldProjectName, newProjectName) +) + +for root, dirs, files in os.walk(folder, topdown=False): + for subDir in dirs: + if oldProjectName in subDir: + oldFolderName = os.path.join(root, subDir) + newFolderName = os.path.join(root, subDir.replace(oldProjectName, newProjectName)) + if dryRun: + print("Would rename folder: %s to %s" % (oldFolderName, newFolderName)) + else: + print("Renaming folder: %s to %s" % (oldFolderName, newFolderName)) + os.rename(oldFolderName, newFolderName) + +# ========= Rename usages in source files: ========= + +print( + "\nReplacing all occurrences of %s in source files with: '%s'.\n" % (oldProjectName, newProjectName) +) + +def replace_package_name_occurences_in_file(filename): + print("Would update file: " + filename) + with open(filename, "r") as file: + filedata = file.read() + + if oldProjectName in filedata: + filedata = filedata.replace(oldProjectName, newProjectName) + + if dryRun: + print("Would update file: " + filename) + else: + print("Updating file: " + filename) + with open(filename, "w") as file: + file.write(filedata) + file.close() + +for root, dirs, files in os.walk(folder, topdown=False): + allowed_extensions = ["swift", "plist", "yml", "pbxproj", "storyboard", "xctestplan", "xcscheme"] + for name in files: + extension = name.split(".")[-1] + if extension in allowed_extensions: + file_name = os.path.join(root, name) + replace_package_name_occurences_in_file(file_name) + +print( + "\nDone renaming project to: '%s'.\n" % (newProjectName) +) From b47981b8e700bacf3a8ef4c39e477ef038b6e0a1 Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Mon, 13 May 2024 09:55:04 +0200 Subject: [PATCH 2/4] Switch to GitHub hosted runners --- .github/workflows/build.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8fa444..e7c4e51 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ env: jobs: build: name: Run tests and deploy to TestFlight - runs-on: [macOS] + runs-on: [macos-latest] steps: - name: Checkout diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 93a8d4c..34b84a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ env: jobs: build: name: Run tests - runs-on: [macOS] + runs-on: [macos-latest] steps: - name: Checkout From 827f59f7012445fc3b51f59075ec98c7a4d469ba Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Mon, 13 May 2024 10:48:42 +0200 Subject: [PATCH 3/4] Set build number to 1 --- TemplateApp.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TemplateApp.xcodeproj/project.pbxproj b/TemplateApp.xcodeproj/project.pbxproj index d09333d..8edbb94 100644 --- a/TemplateApp.xcodeproj/project.pbxproj +++ b/TemplateApp.xcodeproj/project.pbxproj @@ -469,7 +469,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = TemplateApp/TemplateApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.2.3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"TemplateApp/Preview Content\""; DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; @@ -500,7 +500,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = TemplateApp/TemplateApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1.2.3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"TemplateApp/Preview Content\""; DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; From e6b7223d5bcfc814d3981b82f36eac1e5ccf5e0c Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Mon, 13 May 2024 10:51:39 +0200 Subject: [PATCH 4/4] Select Xcode version 15.3 --- .github/workflows/build.yml | 3 +++ .github/workflows/test.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7c4e51..59c9d13 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Select Xcode version + run: sudo xcode-select --switch /Applications/Xcode_15.3.app + - name: Cache Swift Package Manager dependencies uses: actions/cache@v4 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34b84a0..fce0e58 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,6 +19,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Select Xcode version + run: sudo xcode-select --switch /Applications/Xcode_15.3.app + - name: Cache Swift Package Manager dependencies uses: actions/cache@v4 with: