diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 300ab8f3..d4f8f522 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: xcode: - - 26.2 + - 26.4 platform: - iOS steps: @@ -18,7 +18,7 @@ jobs: with: submodules: recursive - name: Download Libdigidocpp iOS artifact - uses: dawidd6/action-download-artifact@v15 + uses: dawidd6/action-download-artifact@v20 with: workflow: build.yml branch: master @@ -26,7 +26,7 @@ jobs: path: libdigidocpp-ios repo: open-eid/libdigidocpp - name: Download Libdigidocpp iOS Simulator artifact - uses: dawidd6/action-download-artifact@v15 + uses: dawidd6/action-download-artifact@v20 with: workflow: build.yml branch: master @@ -44,14 +44,9 @@ jobs: rm -rf $LIBDIGIDOCPP_PATH/ios-arm64_x86_64-simulator && mkdir $LIBDIGIDOCPP_PATH/ios-arm64_x86_64-simulator && cp -r ${{ github.workspace }}/libdigidocpp.iphonesimulator/libdigidocpp.iphonesimulator/lib/digidocpp.framework $LIBDIGIDOCPP_PATH/ios-arm64_x86_64-simulator/ - name: Set up Homebrew id: set-up-homebrew - uses: Homebrew/actions/setup-homebrew@master - - name: Install Swift-sh - run: brew install swift-sh - - name: Install Mockolo - run: | - curl -L https://github.com/uber/mockolo/releases/download/2.5.0/mockolo.artifactbundle.zip -o mockolo.zip - unzip mockolo.zip - sudo cp mockolo.artifactbundle/mockolo/macos/mockolo /usr/local/bin/mockolo + uses: Homebrew/actions/setup-homebrew@main + - name: Install dependencies + run: brew install swift-sh mockolo - name: Setup config and TSL files run: | export RESOURCES_DIRECTORY="Modules/ConfigLib/Sources/ConfigLib/Resources" @@ -95,6 +90,8 @@ jobs: GOOGLE_SERVICES_PLIST: ${{ secrets.GOOGLE_SERVICES_PLIST }} run: | export LANG=en_US.UTF-8 + + sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app # Setup Google Services echo $GOOGLE_SERVICES_PLIST | base64 --decode > "${{ github.workspace }}/RIADigiDoc/Supporting files/GoogleService-Info.plist" diff --git a/Modules/CommonsLib/Package.swift b/Modules/CommonsLib/Package.swift index 0f830238..28672fd5 100644 --- a/Modules/CommonsLib/Package.swift +++ b/Modules/CommonsLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Modules/ConfigLib/Package.swift b/Modules/ConfigLib/Package.swift index 91eccb70..18ce9f2e 100644 --- a/Modules/ConfigLib/Package.swift +++ b/Modules/ConfigLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -16,7 +16,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/hmlongco/Factory", exact: .init(2, 5, 3)), - .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 10, 2)), + .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 11, 2)), .package(url: "https://github.com/TakeScoop/SwiftyRSA", exact: .init(1, 8, 0)), .package(path: "../CommonsLib"), .package(path: "../UtilsLib"), diff --git a/Modules/CryptoLib/Package.swift b/Modules/CryptoLib/Package.swift index 5567f38a..9eb2adc8 100644 --- a/Modules/CryptoLib/Package.swift +++ b/Modules/CryptoLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 import PackageDescription let packageRoot = #filePath diff --git a/Modules/IdCardLib/Package.swift b/Modules/IdCardLib/Package.swift index 01605b04..9b0a5ca7 100644 --- a/Modules/IdCardLib/Package.swift +++ b/Modules/IdCardLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 import PackageDescription let package = Package( @@ -13,7 +13,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/hmlongco/Factory", exact: .init(2, 5, 3)), - .package(url: "https://github.com/leif-ibsen/BigInt.git", exact: .init(1, 21, 0)), + .package(url: "https://github.com/leif-ibsen/BigInt.git", exact: .init(1, 23, 0)), .package(url: "https://github.com/leif-ibsen/Digest.git", exact: .init(1, 13, 0)), .package(url: "https://github.com/filom/ASN1Decoder", exact: .init(1, 10, 0)), .package(url: "https://github.com/leif-ibsen/SwiftECC.git", exact: .init(5, 5, 0)), @@ -60,7 +60,10 @@ let package = Package( .target( name: "IdCardLibMocks", dependencies: ["IdCardLib"], - path: "Tests/Mocks/Generated" + path: "Tests/Mocks/Generated", + swiftSettings: [ + .swiftLanguageMode(.v5) + ] ), .testTarget( name: "IdCardLibTests", diff --git a/Modules/LibdigidocLib/Package.swift b/Modules/LibdigidocLib/Package.swift index ebf502d4..71ece81c 100644 --- a/Modules/LibdigidocLib/Package.swift +++ b/Modules/LibdigidocLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 import PackageDescription let package = Package( diff --git a/Modules/MobileIdLib/Package.swift b/Modules/MobileIdLib/Package.swift index 42ed8182..6923dc74 100644 --- a/Modules/MobileIdLib/Package.swift +++ b/Modules/MobileIdLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -15,7 +15,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/hmlongco/Factory", exact: .init(2, 5, 3)), - .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 10, 2)), + .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 11, 2)), .package(path: "../CommonsLib"), .package(path: "../UtilsLib") ], diff --git a/Modules/SmartIdLib/Package.swift b/Modules/SmartIdLib/Package.swift index 02396703..ea5228f1 100644 --- a/Modules/SmartIdLib/Package.swift +++ b/Modules/SmartIdLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -15,7 +15,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/hmlongco/Factory", exact: .init(2, 5, 3)), - .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 10, 2)), + .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 11, 2)), .package(path: "../UtilsLib"), .package(path: "../CommonsLib") ], diff --git a/Modules/Test/CommonsTestShared/Package.swift b/Modules/Test/CommonsTestShared/Package.swift index 8690f90c..6dfc771b 100644 --- a/Modules/Test/CommonsTestShared/Package.swift +++ b/Modules/Test/CommonsTestShared/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -14,8 +14,8 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/weichsel/ZIPFoundation", exact: .init(0, 9, 19)), - .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 10, 2)), + .package(url: "https://github.com/weichsel/ZIPFoundation", exact: .init(0, 9, 20)), + .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 11, 2)), .package(path: "../CommonsLib") ], targets: [ diff --git a/Modules/UtilsLib/Package.swift b/Modules/UtilsLib/Package.swift index 68d0bc2e..559a3692 100644 --- a/Modules/UtilsLib/Package.swift +++ b/Modules/UtilsLib/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.2 +// swift-tools-version: 6.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -16,8 +16,8 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/hmlongco/Factory", exact: .init(2, 5, 3)), - .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 10, 2)), - .package(url: "https://github.com/weichsel/ZIPFoundation", exact: .init(0, 9, 19)), + .package(url: "https://github.com/Alamofire/Alamofire.git", exact: .init(5, 11, 2)), + .package(url: "https://github.com/weichsel/ZIPFoundation", exact: .init(0, 9, 20)), .package(path: "../CommonsLib"), .package(path: "../ConfigLib"), .package(path: "../Test/CommonsTestShared") diff --git a/RIADigiDoc.xcodeproj/project.pbxproj b/RIADigiDoc.xcodeproj/project.pbxproj index 2fabbdb6..67cceb31 100644 --- a/RIADigiDoc.xcodeproj/project.pbxproj +++ b/RIADigiDoc.xcodeproj/project.pbxproj @@ -1693,24 +1693,24 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/leif-ibsen/BigInt"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.21.0; + kind = exactVersion; + version = 1.23.0; }; }; 1A2D04BD2E8588F100A7269B /* XCRemoteSwiftPackageReference "SwiftECC" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/leif-ibsen/SwiftECC"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.5.0; + kind = exactVersion; + version = 5.5.0; }; }; 1A7CC1C52E71CAAB0002CEA3 /* XCRemoteSwiftPackageReference "ASN1Decoder" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/filom/ASN1Decoder"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.10.0; + kind = exactVersion; + version = 1.10.0; }; }; DF1D59582F3BF88000855E2E /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { @@ -1718,15 +1718,15 @@ repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; requirement = { kind = exactVersion; - version = 12.9.0; + version = 12.12.1; }; }; DF54F82D2D431BD50021D05A /* XCRemoteSwiftPackageReference "swift-certificates" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-certificates.git"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.7.0; + kind = exactVersion; + version = 1.19.0; }; }; DF9AFE902E00D30A0062C64D /* XCRemoteSwiftPackageReference "Factory" */ = { @@ -1742,7 +1742,7 @@ repositoryURL = "https://github.com/Alamofire/Alamofire.git"; requirement = { kind = exactVersion; - version = 5.10.2; + version = 5.11.2; }; }; DFED7A5F2CC9A3B200D8BCA9 /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = { @@ -1750,7 +1750,7 @@ repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins"; requirement = { kind = exactVersion; - version = 0.57.0; + version = 0.63.2; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 267674e2..987b34cd 100644 --- a/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "81e76711f6279eca5b9f401e1e845c4440027fd242576922d1b378fa621d3946", + "originHash" : "a82b406c3d85517dd4b2e58cb9c226399e1542da2d025b4d90acd2079cd678d8", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Alamofire/Alamofire.git", "state" : { - "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", - "version" : "5.10.2" + "revision" : "e938f8c66708e7352fc7e3512647fa54255b267a", + "version" : "5.11.2" } }, { @@ -51,8 +51,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/leif-ibsen/BigInt", "state" : { - "revision" : "afb70a0038bfbba845271b60fa9a58d5840f8017", - "version" : "1.21.0" + "revision" : "c2f49bee454760e433bafff17bfdc5b96f75d7c8", + "version" : "1.23.0" } }, { @@ -78,8 +78,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/firebase/firebase-ios-sdk.git", "state" : { - "revision" : "9b3aed4fa6226125305b82d4d86c715bef250785", - "version" : "12.9.0" + "revision" : "46579c364d39b86ec9fb8f613e19f023700a929c", + "version" : "12.12.1" } }, { @@ -87,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk", "state" : { - "revision" : "35b601a60fbbea2de3ea461f604deaaa4d8bbd0c", - "version" : "3.2.0" + "revision" : "19dffda9a9caf8d86570ff846535902d8509d7bf", + "version" : "3.5.0" } }, { @@ -96,8 +96,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleAppMeasurement.git", "state" : { - "revision" : "2ffd220823f3716904733162e9ae685545c276d1", - "version" : "12.8.0" + "revision" : "5100f946bd32778662047a823bf145c6da32d85d", + "version" : "12.12.1" } }, { @@ -186,8 +186,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-certificates.git", "state" : { - "revision" : "274f8668d3ec5d2892904d8465635c5ea659f767", - "version" : "1.7.0" + "revision" : "5aa1c0d1bc204908df47c2075bdbb39573d05e8d", + "version" : "1.19.0" } }, { @@ -195,8 +195,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-crypto.git", "state" : { - "revision" : "ff0f781cf7c6a22d52957e50b104f5768b50c779", - "version" : "3.10.0" + "revision" : "95ba0316a9b733e92bb6b071255ff46263bbe7dc", + "version" : "3.15.1" } }, { @@ -213,8 +213,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SimplyDanny/SwiftLintPlugins", "state" : { - "revision" : "7c80ce6f142164b0201871e580b021d1b2c69804", - "version" : "0.57.0" + "revision" : "8a4640d14777685ba8f14e832373160498fbab92", + "version" : "0.63.2" } }, { @@ -231,8 +231,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/weichsel/ZIPFoundation", "state" : { - "revision" : "02b6abe5f6eef7e3cbd5f247c5cc24e246efcfe0", - "version" : "0.9.19" + "revision" : "22787ffb59de99e5dc1fbfe80b19c97a904ad48d", + "version" : "0.9.20" } } ], diff --git a/RIADigiDoc/Supporting files/Localizable.xcstrings b/RIADigiDoc/Supporting files/Localizable.xcstrings index 350c0bce..9f18fb05 100644 --- a/RIADigiDoc/Supporting files/Localizable.xcstrings +++ b/RIADigiDoc/Supporting files/Localizable.xcstrings @@ -164,6 +164,7 @@ } }, "Back" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -325,6 +326,7 @@ }, "Certificate details" : { "comment" : "Title of Certificate Details view", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -647,6 +649,7 @@ } }, "Close" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -808,6 +811,7 @@ }, "Container files" : { "comment" : "Signing view container files title", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -987,6 +991,7 @@ }, "Continue" : { "comment" : "My eID PIN change or unblock view step button", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -1994,6 +1999,7 @@ }, "General error" : { "comment" : "General error", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -2593,6 +2599,7 @@ }, "Libdigidocpp is already initialized" : { "comment" : "Meant to be shown in logging only", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -2629,6 +2636,7 @@ }, "Loading" : { "comment" : "Loading label", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -4769,6 +4777,7 @@ } }, "Menu" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -4894,6 +4903,7 @@ }, "More options" : { "comment" : "More options button name", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -6617,6 +6627,7 @@ } }, "Save" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -6759,6 +6770,7 @@ } }, "Settings" : { + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { @@ -7082,6 +7094,7 @@ }, "Signature details" : { "comment" : "Title for Signature Details view", + "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { diff --git a/RIADigiDoc/ViewModel/EncryptViewModel.swift b/RIADigiDoc/ViewModel/EncryptViewModel.swift index 20f5a3d1..9b5b54b0 100644 --- a/RIADigiDoc/ViewModel/EncryptViewModel.swift +++ b/RIADigiDoc/ViewModel/EncryptViewModel.swift @@ -176,11 +176,6 @@ class EncryptViewModel: EncryptViewModelProtocol, Loggable { "Unable to add data files to container: \(String(reflecting: error))" ) - guard let containerUrl = containerURL else { - errorMessage = ToastMessage(key: "General error") - return - } - await handleAddFilesError(error) } diff --git a/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift b/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift index a5ee8593..b152c285 100644 --- a/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift +++ b/RIADigiDoc/ViewModel/Signing/SmartId/SmartIdViewModel.swift @@ -25,7 +25,7 @@ import UtilsLib import ConfigLib import CommonsLib import CryptoKit -import ActivityKit +@preconcurrency import ActivityKit @Observable @MainActor diff --git a/codemagic.yaml b/codemagic.yaml index 69adfdfe..52ff7350 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -28,7 +28,7 @@ workflows: APP_STORE_APP_ID: 1216104448 APP_STORE_APPLE_ID: ET847QJV9F RESOURCES_DIRECTORY: "Modules/ConfigLib/Sources/ConfigLib/Resources" - xcode: latest + xcode: 26.4 inputs: defaultCentralConfigurationUrl_input: description: Default Central Configuration URL @@ -69,11 +69,7 @@ workflows: echo Run tests?: ${{ inputs.enableTests_input }} - name: Setup dependencies script: | - brew install swift-sh - - curl -L https://github.com/uber/mockolo/releases/download/2.5.0/mockolo.artifactbundle.zip -o mockolo.zip - unzip mockolo.zip - sudo cp mockolo.artifactbundle/mockolo/macos/mockolo /usr/local/bin/mockolo + brew install swift-sh mockolo - *get_google_services_plist - name: "Setup config and TSL files" script: | @@ -230,15 +226,11 @@ workflows: APP_STORE_APP_ID: 1216104448 APP_STORE_APPLE_ID: ET847QJV9F RESOURCES_DIRECTORY: "Modules/ConfigLib/Sources/ConfigLib/Resources" - xcode: latest + xcode: 26.4 scripts: - name: Setup dependencies script: | - brew install swift-sh - - curl -L https://github.com/uber/mockolo/releases/download/2.5.0/mockolo.artifactbundle.zip -o mockolo.zip - unzip mockolo.zip - sudo cp mockolo.artifactbundle/mockolo/macos/mockolo /usr/local/bin/mockolo + brew install swift-sh mockolo - *get_google_services_plist - name: "Setup config and TSL files" script: | diff --git a/scripts/run-ci-tests.sh b/scripts/run-ci-tests.sh index 184e666a..5fe7a8b1 100644 --- a/scripts/run-ci-tests.sh +++ b/scripts/run-ci-tests.sh @@ -30,13 +30,42 @@ MODULE_GROUPS=( # ----------------------------- # Reset simulators # ----------------------------- -echo "Shutting down and erasing all simulators..." -xcrun simctl shutdown all || true -xcrun simctl erase all || true +reset_simulator() { + echo "Resetting simulator..." + xcrun simctl shutdown all 2>/dev/null || true + sleep 5 + xcrun simctl erase all 2>/dev/null || true + sleep 5 + + # Boot the simulator and wait for it to be ready + UDID=$(xcrun simctl list devices available | grep "$DEVICE" | head -1 | grep -E -o '[0-9A-F-]{36}') + if [ -z "$UDID" ]; then + echo "ERROR: Could not find simulator UDID for '$DEVICE'" + exit 1 + fi + + echo -n "Booting $DEVICE ($UDID)..." + xcrun simctl boot "$UDID" 2>/dev/null || true + until xcrun simctl list devices | grep "$UDID" | grep -q "Booted"; do + printf '.' + sleep 5 + done + echo " ready!" + + # Shutting down any stale simulator processes + pkill -9 -f "Simulator" 2>/dev/null || true + sleep 5 + open -a Simulator --args -CurrentDeviceUDID "$UDID" + sleep 5 +} + +reset_simulator # ----------------------------- # Run tests group by group # ----------------------------- +FAILED_GROUPS=() + for GROUP in "${MODULE_GROUPS[@]}"; do echo "----------------------------------------" echo "Running test group: $GROUP" @@ -47,10 +76,38 @@ for GROUP in "${MODULE_GROUPS[@]}"; do TEST_XCARGS+=" -only-testing:${MODULE}" done - xcode-project run-tests \ - "${COMMON_ARGS[@]}" \ - --test-xcargs "$TEST_XCARGS" + # Retry once on failure before giving up + SUCCESS=false + for ATTEMPT in 1 2; do + if xcode-project run-tests \ + "${COMMON_ARGS[@]}" \ + --test-xcargs "$TEST_XCARGS"; then + SUCCESS=true + break + else + echo "Attempt $ATTEMPT failed for $GROUP — resetting simulator and retrying..." + reset_simulator + fi + done + + if [ "$SUCCESS" = false ]; then + echo "ERROR: $GROUP failed after 2 attempts" + FAILED_GROUPS+=("$GROUP") + fi + + echo "Shutting down emulator..." + xcrun simctl shutdown all 2>/dev/null || true + sleep 5 done +# ----------------------------- +# Report +# ----------------------------- +if [ ${#FAILED_GROUPS[@]} -ne 0 ]; then + echo "The following test groups failed:" + for G in "${FAILED_GROUPS[@]}"; do echo " - $G"; done + exit 1 +fi + echo "All test groups completed successfully!"