chore: raise minimum supported platform, Swift, and Xcode versions - #4268
Closed
harsh62 wants to merge 7 commits into
Closed
chore: raise minimum supported platform, Swift, and Xcode versions#4268harsh62 wants to merge 7 commits into
harsh62 wants to merge 7 commits into
Conversation
Apple has required Xcode 26 and the 26 SDKs for App Store Connect uploads since April 28, 2026. Per the support policy in README.md, Amplify Swift tracks the Swift version shipped with the minimum Xcode allowed for App Store Connect uploads. Platform floors now match Apple's published Xcode 26 support matrix, and the Swift toolchain floor matches AWS SDK for Swift: - iOS 13 -> 15 - tvOS 13 -> 15 - macOS 12 (unchanged) - watchOS 9 (unchanged, already above Apple's floor of 8) - visionOS: declared in Package.swift for the first time, promoted to GA - swift-tools-version 5.9 -> 6.0 - Minimum Xcode 16.0 -> 26.0 Adopting the Swift 6 language mode is deliberately out of scope. Raising swift-tools-version to 6.0 would otherwise flip the default language mode to Swift 6, so `swiftLanguageModes: [.v5]` pins the existing behavior and keeps this change behavior-neutral. The strict-concurrency migration is tracked separately. CI: - Runners moved from macos-15 to macos-26 - Latest Xcode 26.3 -> 26.5; minimum 16.0.0 -> 26.0.1 - Removed the Xcode 16 legs and the duplicate hardcoded 16.4.0 pins - Simulator devices updated to those present on macos-26 (iPhone 17 Pro Max, Apple Watch Series 11); minimum-Xcode legs use the 26.2 runtime because the image ships 26.0 SDKs but installs no 26.0 runtime - visionOS added to the minimum-supported-version build matrix, which previously never exercised the platform the docs call supported - Added an advisory, non-blocking nightly build against the Xcode 27 preview image for early warning on the next Xcode release Docs: corrected the stale Xcode 13.4 requirement in CONTRIBUTING.md. Three test files were reformatted because raising swiftformat's --swiftversion to 6.0 activates the preferCountWhere rule.
harsh62
had a problem deploying
to
IntegrationTest
August 7, 2026 14:23 — with
GitHub Actions
Failure
The tvOS build failed with "'SFSpeechRecognitionResult' is unavailable in tvOS". SFSpeechRecognitionResult is marked API_UNAVAILABLE(tvos), and the CoreML predictions plugin was relying on `#if canImport(Speech)` to compile itself out on tvOS. As of the Xcode 26 SDKs, the tvOS Speech framework ships a module (module.modulemap and Speech.swiftmodule), so canImport(Speech) now succeeds on tvOS while the type itself remains unavailable. The guard silently stopped excluding the platform. Verified this is an SDK change and not a consequence of raising the tvOS deployment floor: the same file fails to compile against the Xcode 26 tvOS SDK at both tvos13.0 and tvos15.0. Adding `&& !os(tvOS)` restores the pre-Xcode-26 behavior. Applied to every file in the CoreML plugin rather than only the Speech types, because the plugin's configure/reset/client-behavior files reference those types and were previously compiled out as a unit. Verified by building CoreMLPredictionsPlugin and AWSPredictionsPlugin for arm64-apple-tvos15.0-simulator, and confirming macOS still compiles CoreMLPredictionService so the plugin stays active where Speech is genuinely available.
thisisabhash
previously approved these changes
Aug 7, 2026
Jobs on this PR were being killed at exactly 30 minutes, scattered randomly
across schemes and platforms rather than clustered on any one target. The
job logs show no build output at all before the kill, and cleanup
terminating orphaned git and git-remote-http processes: the jobs were still
cloning dependencies, not compiling.
Root cause is the dependencies cache key. It embedded the Xcode version:
amplify-packages-${{ steps.platform.outputs.xcode-version }}-<Package.resolved hash>
Raising the latest Xcode from 26.3 to 26.5 made every existing entry
unreachable, including via restore-keys. With no entry to restore, the
build cache restore is skipped too (it is gated on a dependencies cache
hit), so xcodebuild ran against an empty clonedSourcePackagesDirPath and
had to clone the full dependency graph (~34 packages: aws-sdk-swift,
smithy-swift, aws-crt-swift, swift-nio, ...). Dozens of jobs did that
concurrently and starved each other until they timed out.
These checkouts are just source and are not toolchain specific --
Package.resolved already pins them exactly -- so the Xcode version is
dropped from the key. Otherwise this breaks again on every Xcode bump.
Applied to all five workflows that share the key so they keep sharing it.
Timeouts raised from 30 to 45 minutes, including the nine callers that
hardcoded 30 and so overrode the reusable workflow default. A cold run
legitimately needs more than 30 minutes, and the cache is only seeded from
main by design, so PR runs must tolerate a cold start.
Note the dependencies cache is intentionally only written on main
(build_scheme.yml), so PRs stay consumers and cannot poison it.
Runs the Xcode 27 preview build on PRs and pushes to main instead of on a nightly schedule, so breakages from the next Xcode surface on the PR that introduces them rather than hours later on a run nobody is watching. Every job keeps continue-on-error, so this reports without gating a merge. The preview image runs a beta toolchain and can have constrained capacity, which makes it unsuitable as a required check. Renamed from nightly_xcode_preview.yml to build_xcode_preview.yml to match the trigger, and the concurrency group now keys on the PR number like the other PR workflows so superseded runs are cancelled.
Raising the job timeout from 30 to 45 minutes did not fix the killed jobs; it only moved the wall. Jobs still die with no build output, and passing jobs on the same run cluster at 28-43 minutes, right up against the new ceiling. That is contention, not a slow step, and no timeout value fixes it. The dependencies cache never populates. Every unit test, integration test and build job only *restores* `amplify-packages-*`. The single step that writes it lives in build_scheme.yml gated on `github.ref_name == 'main'`, but the only workflow that reaches it with save_build_cache enabled is build_amplify_swift_platforms.yml, which declares `branches-ignore: [main]` and is otherwise only reachable from the deploy workflows that run on release. The write was unreachable, so the cache has always been empty (the API reports total_count 0). The result is that all ~190 jobs on a run each clone the full dependency graph (~34 packages) from github.com concurrently and starve each other. A clean resolve takes about 5 minutes locally, so the 45 minutes of silence is contention between jobs, not the resolve itself. This adds a workflow that resolves dependencies on main and saves the cache, so PR jobs restore it instead of cloning. It runs only when Package.resolved or Package.swift change, and its concurrency group prevents two seeding runs from racing the same key. The bare git mirrors under repositories/ are pruned before saving: only checkouts/ is needed once working copies exist, and consumers build with -disableAutomaticPackageResolution on a cache hit. That takes the entry from ~4.3GB to ~830MB, which matters against the repository-wide 10GB budget. Verified locally that resolving into the cache directory produces 31 checkouts, that resolution still succeeds after pruning repositories/, and that a real consumer build (AWSPredictionsPlugin for macOS) succeeds against the pruned directory with -disableAutomaticPackageResolution.
Two unit tests failed on the Xcode 27 preview while passing on Xcode 26 across all five platforms. Both were timing assumptions rather than real regressions, so they are fixed at the source instead of being retried. GraphQL subscribe/query/mutate and REST Combine tests waited on expectations with a 0.05 second timeout. That is not enough budget on a slower toolchain: testMixedSuccessAndErrorValues failed with "Exceeded timeout of 0.05 seconds, with unfulfilled expectations: receivedStateValueConnected". Raised to 0.5 seconds. 0.5 rather than something larger because 72 of these expectations are isInverted, and an inverted expectation waits out its full timeout on the happy path. A multi-second value would have added minutes to the suite for no benefit. AWSCloudWatchLoggingSessionControllerTests.testConsumeFailureSendsHubEvent registered a Hub listener and immediately flushed logs. Hub registration is asynchronous, so flushLogFailure could be published before the listener attached, leaving the expectation unfulfilled until the 10 second timeout. It now waits for registration using the existing HubListenerTestUtilities.waitForListener helper, which is the pattern used elsewhere in the repo, and asserts that registration succeeded. Verified on Xcode 26: GraphQLSubscribeCombineTests 6/6 pass (including the test that failed on 27), AWSCloudWatchLoggingPluginTests 60 pass, InternalAWSPinpointUnitTests 59 pass, and the full AWSAPIPluginTests target has no failures.
3 tasks
Contributor
Author
|
Superseded by #4271, which contains the same version bump rebuilt cleanly off main without the unrelated CI changes that were tangled in here. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue
Apple has required Xcode 26 and the 26 SDKs for App Store Connect uploads since April 28, 2026. The support policy in
README.mdstates that Amplify Swift updates its minimum Swift version within 60 days of Apple's change, so this is overdue.This also lands ahead of AWS SDK for Swift's own proposed bump (~September 2026), so we move by plan rather than being forced by an upstream dependency.
Description
Platform floors now match Apple's published Xcode 26 support matrix, and the Swift toolchain floor matches AWS SDK for Swift.
Notes on the choices:
Package.swift. It was documented as "Preview" and built in CI, but resolved implicitly via iOS compatibility. It is now explicitly declared and marked GA.Swift 6 language mode is intentionally NOT in this PR
Raising
swift-tools-versionto 6.0 silently flips the default language mode to Swift 6. To keep this change behavior-neutral,swiftLanguageModes: [.v5]pins the existing behavior.Measured separately: the Swift 6 migration is ~350 genuine strict-concurrency sites (concentrated in Storage, DataStore, and Auth). That lands in a follow-up so it can be reviewed on its own.
CI
macos-15→macos-2626.3→26.5; minimum16.0.0→26.0.116.4.0pin that drifted independently of the central mappingmacos-26(iPhone 17 Pro Max,Apple Watch Series 11) — the previousiPhone 16 Pro Max/Series 10devices do not exist on that imagexcode-27preview image for early warning on the next Xcode. Non-blocking (continue-on-error), so it never gates a PR.Docs
Corrected the stale Xcode 13.4 requirement in
CONTRIBUTING.md(12 major versions out of date).Semver
Treated as a minor release, not a breaking change, consistent with how AWS SDK for Swift is handling their equivalent bump.
How did you test these changes?
Locally on Xcode 26.5 (Swift 6.3.2):
swift build— clean, 0 errorsswift test— 0 failuresxcodebuild build -scheme Amplify-Packageverified end-to-end for iOS, macOS, and visionOSios15.0,tvos15.0,watchos9.0,xros1.0swiftformat --lint . --swiftversion 6.0— clean. Three test files needed reformatting because the 6.0 bump activates thepreferCountWhererule; verified they were clean at 5.9 beforehand, so CI would have failed without this.swiftlint— 0 new violations in changed files (366 pre-existing warnings elsewhere are untouched)tvOS and watchOS full-package builds are left to CI, which has those simulator runtimes; the local machine does not. Compiler-level verification at those floors is included above.
Documentation
README.md— platform table, Swift version, Xcode requirementCONTRIBUTING.md— stale Xcode versionAGENTS.md/CLAUDE.mdREADME-combine-support.mdChecklist
yarn lintpassed (swiftformat + swiftlint equivalents run)By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.