12

I'm trying to find where to put app initialization code that should NOT be run in preview mode. I've seen several answers suggesting it's incorrect to split the app behaviour this way (launch vs preview), but I disagree: many apps need to do additional setup (eg connect to database, launch background tasks, call APIs, etc) that isn't appropriate for the preview (where static test data makes most sense).

In preview mode, Xcode actually runs the app and calls AppDelegate.applicationDidFinishLaunching, so any post-launch initialization code there will be triggered.

What is the recommended way to run app setup code so that it doesn't run in preview?

3 Answers 3

16

It appears that Xcode sets an environment variable when running the app for SwiftUI previews. The key is "XCODE_RUNNING_FOR_PREVIEWS", which will have a value of "1".

Given this I found putting a guard statement that checks that environment value in my applicationDidFinishLaunching implementation before the initialization I didn't want to occur for previews fixed my preview (my initialization was making them fail entirely).

I also wrapped it in a DEBUG check to ensure it would not ever accidentally break a production build.

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Initialization needed for previews

    #if DEBUG
    guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1" else {
        return
    }
    #endif

    // Further initialization not needed for previews
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Charles, this is what I was looking for.
2

The Preview Live, I assume you mean it, creates complete window scene context and injects there view to be previewed, so all application & scene delegate methods are called including instantiating root ContentView, but the root view is not shown, ie. its body is not called.

Thus you can achieve your goal by placing code, initiating all heavy/network/etc. operations in root view's .onAppear callback. And this, actually, will be good for your users as well, because such approach gives fast application start and initial UI presented.

1 Comment

Thanks Asperi! This looks like the right approach, unfortunately it still has the same issue; the root view's onAppear gets called even though it is NOT previewed, eg I'm previewing some small view. It actually gets called a few times. I've confirmed this using os_log (developer.apple.com/documentation/os/os_log) and seeing the messages in the Console.app. Very strange, I'll keep poking around. Thanks for your help!
1

TL;DR:

Workaround this issue by putting your SwiftUI code in a framework and make it the selected scheme in Xcode before viewing your previews.

Details:

Looks like SwiftUI Previews need to build a target that contains the SwiftUI code (makes sense), but that target doesn't have to be an app! So it's unclear why it needs to run the app at all, but clearly it does. Xcode picks which target to build from the currently selected scheme (if you pick a target that doesn't build the required SwiftUI files, you'll get an error in the previews). So here's a workaround:

  • Add a new framework target
  • Put your view code in that new target
  • Select the scheme for that new target
  • Run your preview

This also has the advantage of only compiling the new target (and its dependencies) for rendering previews, which is likely to be faster that building the whole app.

Any dependencies will of course need to be accessible from that new target.

This works at least in Xcode 12.5.1

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.