How Do You Know Whether or Not SwiftUI Previews and Preview Content Are Excluded From App Store Builds?
I’ve found what I believe to be a bug, or at least deeply disappointing behavior, in Xcode’s treatment of SwiftUI previews. I’ll put an explanation together in the paragraphs that follow, but the TL;DR is: I think you’ll probably want to start wrapping all your SwiftUI Previews and Preview Content Swift source code in #if DEBUG
active compilation condition checks.
Screenshot of an Archive action build failure.
SwiftUI previews are, if you consider it from a broader perspective, comprised of two kinds of source code:
-
Preview proper: - The actual
#Preview
orPreviewProvider
source code, typically at the bottom of the participating SwiftUI file. -
Development Assets: - Ancillary source (and assets) under a particular directory whose contents should only be used to populate SwiftUI previews. The default name for this directory (in
File/New...
) projects isPreview Content
.
The SwiftUI preview dream is supposed to be this:
-
Dream Part 1: I write
#Preview { ... }
or aPreviewProvider
at the bottom of my file. -
Dream Part 2: I stash any images, assets, or source code (representative data) in the Preview Content directory of Development Assets.
-
Dream Part 3: Xcode ensures that both my Preview proper, and all my Development Assets, are omitted from App Store builds.
The reality is less than that ideal:
-
#Preview
andPreviewProvider
source code is stripped out of any build where Swift Optimizations are anything above-Onone
. It (hilariously) does not matter whether or not you’ve got theENABLE_PREVIEWS
build setting set toNo
, or whether your build configuration is namedRelease
or not. They are included in any unoptimized build. -
Once more, with feeling: Preview source code is included in any unoptimized build.
-
Note I said, two bullets ago, “stripped out”. Previews are actually built in all builds, but they are later stripped out of the compiled binary as part of code optimization. They aren’t skipped altogether, not even for optimized builds. This will have bearing on the bullet points that follow. . . .
-
Development Assets are not always built. They are omitted at the front end of the build process during Archiving only (i.e. running “Archive” from the Xcode GUI menu, or the
archive
xcodebuild command), and even then only if you’ve defined theDEVELOPMENT_ASSET_PATHS
build setting. Any images, asset catalogs, or source files under those paths are excluded from build input. If you aren’t using “Archive”/archive
, you won’t see a build error! -
This means that if you’ve got a SwiftUI Preview that depends upon
.swift
source code, any “Archive”/archive
attempt will fail at build time because the Preview depends upon source code that no longer exists among the build inputs.
There are several workarounds, none of which are spectacular:
-
Stop using SwiftUI previews. - Boo, a non-starter.
-
Leave Development Assets hanging around in optimized builds. - This is done by removing the definition of the
DEVELOPMENT_ASSET_PATHS
build setting. Also “Boo” because the whole point of Development Assets is that you don’t want them showing up in App Store builds. -
Wrap all SwiftUI previews in
#if DEBUG
active compilation condition checks. - Unless you’ve overridden new project defaults, Release build configurations do not defineDEBUG
as a Swift active compilation condition. Wrapping all your SwiftUI Previews in#if DEBUG
is a semi-bulletproof solution that will ensure your Previews don’t break optimized builds. But it doesn’t, on its own, prevent development assets from creeping into App Store builds because you might have forgotten to define theDEVELOPMENT_ASSET_PATHS
build setting. If the build setting is missing, your development assets are potentially delivered in App Store builds. I say “potentially” because such a build is, after all, optimized, and therefore if no production code is actually accessing the development asset source code, it’s possible it may be optimized out of the final binary. -
Wrap all SwiftUI previews AND Development Assets Swift source in
#if DEBUG
active compilation condition checks. - This is the only solution, short of improvements to Xcode itself, that ensures that Previews don’t break optimized builds and that Development Assets source code doesn’t leak into App Store builds.
That’s the long-winded explanation.
Until and unless Apple makes ergonomic improvements to align SwiftUI Preview and Development Asset conditional compilation techniques, I recommend wrapping both kinds of source code in #if DEBUG
to prevent accidental slippage of test data and source into production code, as well as to prevent unexpected build failures on multitenant hardware (for teams that build for App Store in CI, these build failures often don’t appear except on unattended machines around the time a release is being cut, making them perniciously difficult to spot during day-to-day code review).