When I moved to Xcode 26, I added UIDesignRequiresCompatibility to Info.plist and postponed the visual change. My wallpaper apps are built around a full-screen image viewer and a custom bottom bar, and I could not predict what would happen if Liquid Glass landed on them all at once.
Last week I set that flag back to false. The reasoning was simple: this flag is expected to be ignored in Xcode 27. Choosing the day it comes off hurts less than waiting for the day it is taken away.
Three things broke. None of them were the kind you can leave for later, and every one of them would have cost me hours if I had found it on a device the night before a release.
The escape hatch was never meant to last until it disappears
At the WWDC25 UI Frameworks Group Lab, Apple stated plainly that the compatibility option is a temporary measure and is scheduled to be removed in the next major release. The way out that shipped with iOS 26 and Xcode 26 had an expiry date printed on it from day one.
Laying the dates side by side makes the remaining runway easier to feel.
| When | What | Confidence |
|---|---|---|
| April 28, 2026 | All App Store submissions must be built with the iOS 26 SDK (Xcode 26) | Official |
| September 2026 (expected) | iOS 27 / Xcode 27 ship. The compatibility flag is ignored | Apple's own statement plus the usual annual cycle |
| Mid-April 2027 (predicted) | App Store submissions expected to require iOS 27 SDK builds | Extrapolated from past cycles, not announced |
That April 2027 date is a prediction, not something Apple has published as of this writing. But the moment the release version lands in September, rebuilding against the new SDK will change how your app looks. The practical deadline is this autumn, not next spring.
Turning it off means deleting one line, or setting the value to false.
<!-- Info.plist -->
<!-- Temporary flag that keeps the pre-iOS 26 appearance. Expected to be ignored in Xcode 27. -->
<key>UIDesignRequiresCompatibility</key>
<false/>Build with Xcode 26 after that and the standard controls render with the new design. You get to rehearse next year's forced state on the SDK you are already shipping with — which is, in the end, the most useful thing this flag can do for you. You do not need an iOS 27 beta device to start. If you are weighing which device to put a beta on, I wrote up that decision separately in choosing a device for the iOS 27 public beta.
The first thing to break was the bottom of the full-screen viewer
My wallpaper apps show an image full-screen with save, share, and favourite buttons along the bottom edge. For a long time that row was positioned using a hardcoded offset based on the tab bar height.
// Before: lifting the row by a hardcoded bar height
VStack {
WallpaperCanvas(item: item)
ActionBar(item: item)
.padding(.bottom, 49) // tab bar height
}The Liquid Glass tab bar floats above content and minimises as you scroll. That changes the value of safeAreaInsets itself, so 49 stops being correct. On device, the button row sank halfway behind the bar.
The fix looked like this:
// After: let the system tell you how much room the bar needs
VStack {
WallpaperCanvas(item: item)
}
.safeAreaInset(edge: .bottom) {
ActionBar(item: item) // no hardcoded height
}It is a tiny diff, but the lesson is not specific to Liquid Glass. Anywhere a bar height lives as a constant, that code will break again the next time something floats or shrinks. This migration turned out to be a good excuse to find those constants.
Custom background colours do not stop working — they go muddy
The second break was the navigation bar background. I had set an opaque background in the brand colour, and it fought with the glass material, producing a murky result nobody designed.
// Before: an opaque background supplied by hand
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .brandBackground
UINavigationBar.appearance().standardAppearance = appearanceThe awkward part is that nothing disappears. There is no error, no warning — the finish is simply worse. The build succeeds, so you will not notice until you put screenshots side by side.
The fix was to drop the custom background and let the system draw the bar, keeping the brand colour as an accent in the content instead. There are also reports of the system automatically inserting a UIDropShadowView, which throws off layout code that counts positions in the view hierarchy. If you index into subviews anywhere, that is worth checking at the same time.
For my apps, everything affected fell into three buckets.
| Pattern | Symptom | Fix |
|---|---|---|
| Hardcoded bar heights | Button row sinks behind the tab bar | Replace with safeAreaInset |
| Opaque bar backgrounds | Muddy finish where glass should be | Remove the custom background; move colour into content |
| Indexed view hierarchy access | Offsets shift when the system inserts a view | Look views up by type or identifier, not index |
I asked the agent for an inventory, not for fixes
What I actually asked Antigravity to do was list every suspicious call site — and change nothing.
Concretely: layout code containing numbers that look like bar heights (44, 49, 83), assignments to UINavigationBarAppearance and UITabBarAppearance, indexed access into subviews, and references to UIScreen.main, all with file names and line numbers. This kind of work — mechanically findable, easy for a human to skim past — is where an agent earns its keep.
The decision about what to change stayed with me. Some of those indexed lookups look like mistakes but are deliberate, and a bulk automated fix erases the intent along with the pattern. For migrations like this one I prefer a clean split: discovery automated, judgement manual.
The request itself only needed a target and an output format.
List every line in the Swift files of this repository that matches the following,
with file name and line number. Do not modify anything.
1. padding / offset / frame values using numeric constants that look like bar heights (44 / 49 / 83 / 96)
2. Assignments to UINavigationBarAppearance or UITabBarAppearance
3. Indexed access into subviews
4. References to UIScreen.main
For each entry, add one line explaining why it might be a problem under Liquid Glass.I ended up touching about half of what came back. The rest I marked as read and understood. In a migration, the expensive part is rarely the fixing — it is reaching the point where you are confident the untouched code is fine.
Before Liquid Glass, look at the UIScene life cycle
I put the visual work first in this article, but it is second in priority.
Apple's technical note TN3187 states that in the next major release the UIScene life cycle becomes mandatory when building against the latest SDK, and that apps which have not adopted it will not launch. A broken layout and an app that refuses to start are not the same order of problem.
The work centres on creating a SceneDelegate, adding UIApplicationSceneManifest to Info.plist, and moving to UIWindow(windowScene:). The AppDelegate life cycle methods were deprecated in iOS 26 and already have designated replacements.
| Deprecated (AppDelegate) | Replacement (SceneDelegate) |
|---|---|
applicationDidBecomeActive(_:) | sceneDidBecomeActive(_:) |
applicationDidEnterBackground(_:) | sceneDidEnterBackground(_:) |
application(_:open:options:) | scene(_:openURLContexts:) |
At indie developer scale, the pragmatic route is to finish the migration with UIApplicationSupportsMultipleScenes left false and a single-window assumption. Multi-window support can wait. Moving to the Scene life cycle cannot.
The more launch paths your app has — push notifications, custom URL schemes, widgets — the more combinations you need to walk through. Writing that list down before you submit to the App Store is time well spent.
The order I would do it in before September
This is the sequence I actually followed.
- Set UIDesignRequiresCompatibility to false and build with Xcode 26 — this is where reality shows up
- Capture screenshots of every broken screen before touching anything
- Replace hardcoded bar heights with
safeAreaInset - Remove opaque bar backgrounds and move brand colour into the content
- Swap
UIScreen.mainfor access viawindowScene.screen - Begin the UIScene life cycle migration, single-window assumption is fine
Steps 1 through 4 took roughly half a day per app across my two apps. Put differently: done after Xcode 27 ships, that same half day becomes half a day with a release held hostage.
The only thing you have to do today is change one line in Info.plist and build. That alone tells you where next year's version of you is going to get stuck. If you are running the same migration on the Android side, the Android 16 edge-to-edge enforcement notes follow a similar shape.
References: Meet Liquid Glass (WWDC25 Session 219) / TN3187: Migrating to the UIKit scene-based life cycle