Skip to content

fix(ios): let pre-Xcode-26 toolchains compile the Liquid Glass surfaces - #364

Draft
simonhamp wants to merge 1 commit into
mainfrom
ios18-compat-backdeploy
Draft

fix(ios): let pre-Xcode-26 toolchains compile the Liquid Glass surfaces#364
simonhamp wants to merge 1 commit into
mainfrom
ios18-compat-backdeploy

Conversation

@simonhamp

Copy link
Copy Markdown
Member

Fixes the compile failure reported in NativePHP/nativephp.com#450, where a fresh v4 app built with Xcode 16.4 (iOS 18.5 SDK) fails in the native renderer:

value of type 'TabBarAccessoryModifier.Content' has no member 'tabViewBottomAccessory'

Why the existing guards weren't enough

#available(iOS 26.0, *) is a runtime gate — it stops the call on old devices, but the symbol still has to exist in the SDK being compiled against. So the linked PR was right that #available alone can't save it, but wrong that Xcode 26 is therefore mandatory: each iOS 26 call site just needs a second, compile-time gate wrapped around the runtime one.

Swift has no direct "which SDK am I building against?" condition, but the toolchain and the SDK ship together, so the compiler version stands in for it:

Xcode Swift SDK
16.0–16.4 6.0–6.1.2 iOS 18
26.x 6.2+ iOS 26

#if compiler(>=6.2) therefore reads as "the iOS 26 SDK is present".

What changed

tabViewBottomAccessory was just the first error hit — there were six iOS 26 call sites, all now gated:

  • ContentView.GlassPillBackground.glassEffect(in:)
  • NodeStyleModifier.GlassModifier.glassEffect(_:in:)
  • NodeStyleModifier.WithGlassContainerGlassEffectContainer
  • NativeRootStackRenderer.safeAreaBar(edge:)
  • NativeRootStackRenderer.navigationSubtitle(_:)
  • NativeRootTabsRenderer.tabViewBottomAccessory

Each keeps its #available check inside the #if, with the pre-26 branch factored into a fallback helper so the two #if arms can't drift apart. New LiquidGlassAvailability.swift documents the rationale in one place and emits a #warning on pre-Xcode-26 toolchains, so it's obvious in the build log that Liquid Glass was compiled out.

Device support is unchanged

The iOS 18 side already worked — IPHONEOS_DEPLOYMENT_TARGET is already 18.2 and every iOS 26 API was already runtime-guarded, so an iPad stuck on iOS 18 was never the blocked case. The compile was the only failure. Real floor is Xcode 16.2, not 16.0 — 16.0/16.1 ship the iOS 18.0/18.1 SDK, below the 18.2 deployment target.

Two things to flag

  1. A binary built with Xcode 16 shows the fallbacks even on iOS 26 hardware. The gate is per-build, not per-device: no Liquid Glass, no bottom accessory, no .navigationSubtitle.
  2. Xcode 26 is still required to ship. App Store Connect has rejected uploads built against anything older than the iOS 26 SDK since 28 April 2026. This buys back local dev and CI on older Xcode; full-fidelity rendering still needs 26. docs(mobile/4): iOS build requires Xcode 26 (iOS 26 SDK) nativephp.com#450 should probably say "Xcode 26 to submit and for Liquid Glass; 16.2+ compiles and runs" rather than hard-requiring 26.

Verification

Built both paths with Xcode 26.3 against a clean derived-data dir. Error set is identical to the pre-change baseline (10 pre-existing php_embed errors — the PHP headers aren't vendored into a bare clone) with no new warnings. Then forced the #else arm via a temporary compiler(>=9.9) flip and rebuilt: still no new errors, and the #warning fires as expected. That proves the fallbacks type-check with zero iOS 26 symbols in scope at a 18.2 deployment target.

Only Xcode 26.3 was available here, so the flip is a simulation of Xcode 16 rather than the real thing — worth one confirming build on 16.4 before merge.

PHP suite: 854 passed.

🤖 Generated with Claude Code

`#available(iOS 26.0, *)` is a runtime gate — the symbol still has to
exist in the SDK being compiled against. Building the renderer with
Xcode 16 (iOS 18 SDK) therefore failed outright:

    value of type 'TabBarAccessoryModifier.Content'
    has no member 'tabViewBottomAccessory'

Wrap each of the six iOS 26 call sites in a compile-time `#if
compiler(>=6.2)` gate around the existing runtime one. Swift has no
direct SDK-version condition, but the toolchain and SDK ship together
(Xcode 16.x tops out at Swift 6.1.2; Xcode 26 ships 6.2), so the
compiler version stands in for it. The pre-26 branch is factored into a
`fallback` helper at each site so the two `#if` arms can't drift apart.

Nothing about device support changes: the deployment target stays at
iOS 18.2 and the runtime guards are untouched. A binary built with
Xcode 16 simply renders the pre-26 fallbacks everywhere, including on
iOS 26 hardware — `LiquidGlassAvailability.swift` documents the trade-off
and emits a `#warning` so that's visible in the build log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant