62

Is there a way to check if a SwiftUI app is in preview mode? For example, you can check if your app is in development or production mode using #if DEBUG. Can you do something similar to check if you're previewing or not?

Ken Mueller
  • 3,659
  • 3
  • 21
  • 33

4 Answers4

130

You can detect this using ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"]. The value will be "1" at runtime when running in the canvas.

arsenius
  • 12,090
  • 7
  • 58
  • 76
  • 1
    I had some code that was still running when previewing a view. It was kind of annoying. This fixed it. – Mr Rogers Jun 04 '20 at 15:45
  • For me (Xcode 12.5RC1) the compilation flag isn't being set but the environment variable is. Do you have a link to any doc that mentions the compilation flag? – MikeyWard Apr 25 '21 at 20:23
  • @MikeyWard I'm not sure why that edit was made. I can't find any supporting documentation about it elsewhere. – arsenius Apr 26 '21 at 03:16
  • 4
    For what it's worth, checking this with a guard statement in my applicationDidFinishLaunching and not initializing things that break my SwiftUI previews got my previews working again. Good to wrap this guard in #if DEBUG / #endif for good measure. – Charles A. Aug 26 '21 at 21:16
  • For me, this still had the value "1" when run on my physical device... – wristbands Mar 15 '22 at 18:13
  • Is it possible to distinguish whether the Preview is running in interactive mode or not? – adamsfamily May 02 '22 at 13:10
10

If you like me were looking for an environment variable to use in build scripts which xcode sets when building for SwiftUI previews, it turned out to be ENABLE_PREVIEWS.

SwiftUI were pausing preview when my script updated Info.plist file. To fix that I exit the script at certain point if we are in preview build.

if [ "${ENABLE_PREVIEWS}" = "YES" ]; then
  exit 0;
fi
Orkhan Alikhanov
  • 9,122
  • 3
  • 39
  • 60
  • 1
    The trouble with this is that it is ALWAYS "yes" for any project that uses SwiftUI. The only time it's set to "no" is for an archive build. So if you have scripts you want to run when building the app, those won't run until you archive a release version. – Bryan Mar 22 '22 at 01:25
  • Really? At the time of writing this answer, it was set to YES when xCode was building the SwiftUI preview. But when you were building the app regularly or archiving, it was set to other than YES. – Orkhan Alikhanov Mar 22 '22 at 09:45
  • Can confirm that it works. I was looking for it because I use SwiftFormat to reformat the code on build and it was causing issues when working on a SwiftUI view. See https://github.com/nicklockwood/SwiftFormat/issues/969 – Yonic Surny Feb 23 '23 at 16:30
8

Though there is no compilation flag currently available for checking if the active build is intended for the Previews Canvas, I would still recommend using a compiler directive over a runtime check, if it can meet your needs.

For example, this check resolves to true for both the simulator and Previews:

#if targetEnvironment(simulator)
// Execute code only intended for the simulator or Previews
#endif

Negate the condition if you want your code to only execute on physical devices (such as camera-related operations that are otherwise guaranteed to fail).

The runtime check for whether your code is executing for Previews (as given in the accepted answer) probably does not add significant performance overhead, but it still feels a little gross to ship that code IMO. So it's worth at least considering first if your situation requires that level of specificity. If it does, I'd recommend wrapping that code in a compiler check to remove it from release builds.

Nathan Hosselton
  • 1,089
  • 1
  • 12
  • 16
  • 1
    This worked for my preview needs! This suggestion better suited for cases you'd like to handle this at compile-time level instead of wasting time and space in runtime. – Rock_Artist Sep 13 '22 at 04:27
  • is there a way for preview only? i want to use some specific data only for preview mode. – chitgoks Jan 12 '23 at 03:56
  • there is no compiler check (as i mention in the above answer). there is another answer on this question which provides a runtime check if that is what you absolutely require. however, it sounds like you may be referring to mock data for previews. i would argue that mock data is perfectly suitable for DEBUG/simulator builds (for which you can use a compiler check) and so is not worth the effort to segregate exclusively to previews. my apps will use mock data on simulator based on my scheme selection. – Nathan Hosselton Jan 19 '23 at 21:30
6

If you don't want to rely on the ProccessInfo value you can always design your own environment variable in SwiftUI.

import SwiftUI

private struct IsPreviewKey: EnvironmentKey {
    static let defaultValue = false
}

extension EnvironmentValues {
    var isPreview: Bool {
        get { self[IsPreviewKey.self] }
        set { self[IsPreviewKey.self] = newValue }
    }
}

Then when you create your preview inject the variable

MyView().environment(\.isPreview, true)

and you can use it in your view like this:

struct MyView: View {
    @Environment(\.isPreview) var isPreview
}

I usually have a method that generates all the various versions for a preview (light mode, dark mode, iPad, iPhone, …) so I inject it there to all of the previews.

Aleš Oskar Kocur
  • 1,381
  • 1
  • 13
  • 29
  • 1
    This is a good solution if you only need to access the var from `View`'s but you'll get `Accessing Environment's value outside of being installed on a View. This will always read the default value and will not update.` warnings trying to access it from other types like `ObservableObject` for your view model – Norman Jun 03 '23 at 21:37