Up until Xcode 16, it was possible to check in build scripts of your Xcode project via the ENABLE_PREVIEWS
environment variable whether a build for a SwiftUI preview is being executed.
This made it possible, for example, to skip build scripts for SwiftUI preview builds or to execute other commands in the scripts. One use case was to skip Linter and code formatter steps so that the preview builds were not infinitely slow.
In addition, with this variable and via macros, code could be conditionally compiled for SwiftUI preview builds or excluded from compilation.
None of this works anymore with Xcode 16 (including the current beta v5) and the new SwiftUI Preview compilation system that Apple has introduced.
Has anyone already found an alternative?
You can easily observe the new behavior in Xcode 16 by switching on the option to display the environment variables for build scripts.
With the new build system, the value of ENABLE_PREVIEWS
will always be NO
.
I know that you can currently still switch to the old SwiftUI preview system with the "Use Legacy Previews Execution" option, but Apple describes in the Xcode 16 beta release notes that this method will be removed in the near future.
2
Answers
Since I received an “official” response from Apple Engineers on the subject, I wanted to share the findings on this and the answer to my questions:
First of all, there is no actual replacement for
ENABLE_PREVIEWS
with Xcode 16 and the new build system.The change has been made deliberately, and the following quote explains why:
One can also observe that the build behavior of SwiftUI previews has changed with Xcode 16. For example, a build for a SwiftUI preview may no longer be triggered if the artifacts of a previous debug build are already available and no code has changed for the preview.
With regard to the new SwiftUI build system, this means:
1. Conditionally compiled code for SwiftUI previews is no longer possible
As already described in the question, special code could previously exist in a project (e.g. mocks / extensions etc.), which was compiled exclusively for SwiftUI Previews and was not available in other builds.
This code could therefore not be used “by mistake” in productive parts of the project.
The only thing that is still possible in this respect is to rely on the dead code stripping feature, which removes unused code from builds. Of course, this feature does not prevent a developer from using code that may only be used for previews and should not end up in the production app.
2. Prevention of code execution in SwiftUI previews at runtime
Since SwiftUI Previews are known to start the app in the background with every view / refresh, code is also executed that you may not want to execute. This can include, for example, network calls in the AppDelegate. Information about the developer systems that should not be disclosed may be leaked this way.
ENABLE_PREVIEWS
made it possible to use a compilation conditional in a simple way to prevent such executions in previews.Fortunately, at this point there is a possibility at runtime to identify the execution for previews via an environment variable:
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"]
Therefore, without
ENABLE_PREVIEWS
there is a way that certain code for previews is not executed.3. Build scripts and SwiftUI preview builds
In many large iOS projects, various build scripts are often used that should only be executed for certain build variants for various reasons. In general, such scripts may slow down the build process considerably. (e.g. formatter and linter scripts)
ENABLE_PREVIEWS
was a very useful tool in the past to significantly speed up and optimize the build process for SwiftUI previews, as these could sometimes become extremely slow in large projects.There is no direct replacement here either. It might be possible to use the default "Debug" build configuration (which Apple says is used by SwiftUI previews) exclusively for SwiftUI previews and use or create a separate debug build configuration with a different name for regular debug builds.
However, I have not yet tried this in practice.
I compared all the build settings used for normal build and previews build. the only difference I found is target device env
TARGET_DEVICE_IDENTIFIER
Here is my temporary solution instead of using ENABLE_PREVIEWS, you have to find your destination id used by Previews (I got this value from "Show environment variables in build log")