Resolve Bitrise iOS build failures caused by an Xcode or stack version mismatch — Swift and toolchain errors, deprecated stacks, SDK incompatibilities — with a clear method for aligning your stack to your project.
When a Bitrise build fails because of an Xcode or stack version problem, the symptoms are distinct from signing or dependency errors. You will often see Swift compiler messages such as "module compiled with Swift X.Y cannot be imported by the Swift Z.W compiler", errors about an unavailable SDK or deployment target, complaints that a symbol or API is unavailable, or a flat statement that the selected Xcode version is not installed on the machine. Sometimes the build fails immediately because the stack you referenced no longer exists after Bitrise deprecated it. The unifying cause is that the macOS stack running your build carries a specific Xcode version and toolchain, and something in your project — its Swift language version, a dependency compiled with a different Swift, an SDK feature, or a minimum-Xcode requirement — does not agree with what that stack provides. The first move is always to identify which Xcode version the failing build actually used, which appears near the top of the build log and in the stack selection shown in the Workflow Editor, and then compare it deliberately against what your project and dependencies need.
A frequent cause is a stack whose Xcode is older than your project requires. This happens when your team upgrades Xcode locally and adopts a newer Swift feature, a newer SDK API, or a project setting that demands a minimum Xcode, but the Bitrise workflow still points at an older stack. The build then fails because the older compiler does not recognize the syntax or the SDK lacks the API. It also happens with dependencies: a Swift package or CocoaPod may declare a minimum Swift tools version, and if the stack's Swift is older, resolution or compilation fails. The fix is to select a newer stack whose Xcode matches or exceeds what your project and dependencies need. In the Workflow Editor, open the stack settings and choose the appropriate macOS stack with the required Xcode version, either at the app level or overridden per workflow. After changing it, run a clean build and read the log to confirm the new Xcode is in use. Aligning to the version your team standardizes on locally is the most reliable way to avoid this class of failure.
The opposite mismatch is just as real: a stack whose Xcode is newer than your project is ready for. When you jump to the latest stack — sometimes involuntarily, if a workflow floats to a default — a newer Xcode can enforce a newer Swift language mode, change default build settings, tighten warnings into errors, or drop support for an old deployment target. Code that compiled cleanly before can suddenly break on concurrency checking, deprecated API removal, or stricter type inference. Dependencies that have not been updated for the new Xcode may fail to compile at all. The fix depends on your goal. If you are not ready to adopt the new Xcode, pin your workflow to the specific older stack that your project builds on, giving your team time to update code and dependencies. If you do want to move forward, treat the upgrade as a deliberate task: update dependencies to versions compatible with the new Xcode, address the new warnings and language-mode changes locally in Xcode first, then update the Bitrise stack once the project builds cleanly on that Xcode on a developer machine.
Bitrise periodically deprecates and removes older stacks as Apple releases new Xcode versions and as older macOS and Xcode combinations fall out of support. If your workflow references a stack that has since been removed, the build can fail outright or be silently migrated to a fallback that has a different Xcode than you expected, which then triggers one of the version-mismatch failures above. Bitrise communicates these deprecations through announcements and the stacks documentation, but teams that do not track them are caught off guard when a long-stable pipeline suddenly breaks. The fix is to consult Bitrise's current list of available stacks, identify the supported stack closest to what your project needs, and update your workflow to reference it explicitly. Make this a periodic maintenance habit rather than a fire drill: whenever Apple ships a major Xcode release, check whether your stack is still supported and plan a migration before the old one is removed. Pinning to an explicit, currently-supported stack — rather than relying on a default that can shift underneath you — keeps your builds predictable across Bitrise's stack lifecycle.
Even with the right Xcode, Swift version conflicts can break a build, especially with precompiled or binary dependencies. The classic message is that a module was compiled with one Swift version and cannot be imported by another. This arises when a dependency ships a binary framework or an .xcframework built against a specific Swift version that does not match the stack's compiler, or when a package's declared Swift tools version disagrees with the toolchain. The remedy is to align versions: update the dependency to a build compatible with your stack's Swift, or choose a stack whose Swift matches the dependency. For Swift Package Manager, ensure the package's swift-tools-version and its released binaries support your Xcode. For CocoaPods, confirm the pod version supports the Swift version in your stack, and that any SWIFT_VERSION build setting in your project matches. If you maintain the dependency yourself, rebuild its binary against the target Swift. When binary compatibility cannot be reconciled, switching to a source-distributed version of the dependency, which compiles against whatever toolchain the stack provides, often resolves the conflict cleanly.
Sometimes a version-related failure is really an environment problem: a cache captured under one Xcode is restored under another, leaving incompatible artifacts. If your workflow caches derived data, the SPM build cache, or CocoaPods, and you then change stacks, the restored cache can contain modules built with the previous Xcode's Swift, producing import errors that look like a version mismatch even though your configuration is now correct. The fix is to invalidate or clear the cache after any stack or Xcode change, so the first build on the new stack rebuilds everything from source. Configure your cache keys to include the Xcode or stack version where possible, so a stack change naturally produces a new cache rather than reusing an incompatible one. Also confirm no hardcoded toolchain path or environment variable in your Steps or scripts points at an Xcode that is not present on the new stack, since a stale DEVELOPER_DIR or an explicit xcode-select path can override the stack's default and cause confusing failures. Clearing drift ensures the version you selected is actually the version doing the compiling.
To fix and prevent these failures systematically, treat Xcode as a versioned dependency of your project, not an incidental detail. First, identify the Xcode version your project builds on locally and, ideally, record it explicitly — many teams keep a note or a .xcode-version style marker in the repo. Second, select the Bitrise stack whose Xcode matches that version, pinning it explicitly in your workflow rather than relying on a shifting default. Third, keep dependencies current and compatible with that Xcode's Swift, updating them deliberately as part of any Xcode upgrade rather than discovering incompatibilities in CI. Fourth, when you upgrade Xcode, do it locally first, resolve all warnings and language-mode changes there, and only then bump the Bitrise stack. Fifth, watch Bitrise's stack deprecation announcements and migrate ahead of removals. This mirrors an honest truth about iOS development generally: Bitrise runs Apple's Xcode and toolchain, so your CI is only ever as compatible as the Xcode you select, and staying aligned with the Apple toolchain your project targets is unavoidable work that no CI tool eliminates — it simply makes the alignment explicit and reproducible.
The Xcode version appears near the top of the build log and is determined by the macOS stack selected in your Workflow Editor. Open the stack settings for your app or workflow to see the current stack and its bundled Xcode, and check the log's environment output. Knowing the exact version is the first step to diagnosing whether the stack is too old, too new, or deprecated.
Bitrise deprecates and removes older stacks over time. Consult the current stacks list, choose the supported stack whose Xcode matches your project's needs, and update your workflow to reference it explicitly. Make checking stack support a habit whenever Apple ships a major Xcode release, and pin an explicit supported stack rather than relying on a default that can shift underneath you.
This means a dependency's precompiled binary was built with a different Swift version than the compiler on your stack. Align the versions by updating the dependency to one compatible with your stack's Swift, choosing a stack whose Swift matches the dependency, or switching to a source-distributed version of the dependency that compiles against whatever toolchain the stack provides. Also clear caches captured under a different Xcode.
No. A newer Xcode can enforce a newer Swift language mode, change build defaults, and break code or dependencies that are not ready for it. Upgrade deliberately: adopt the new Xcode locally first, resolve warnings and incompatibilities, update dependencies, and only then bump the Bitrise stack. Pin an explicit stack so your builds do not drift onto a new Xcode before you are ready.
If your workflow caches derived data, the SPM cache, or CocoaPods and you change stacks, the restored cache can contain modules built with the previous Xcode's Swift, causing import errors that mimic a version mismatch. Clearing the cache forces a clean rebuild from source on the new stack. Including the Xcode or stack version in your cache key prevents reusing incompatible caches.