How to stop SwiftUI Previews from crashing when using Firebase

If you've been working with SwiftUI, you've likely come across this dreaded error message a few times.

CrashError: ExampleApp crashed
ExampleApp crashed. Check ~/Library/Logs/DiagnosticReports for crash logs from your application.
| HumanReadableNSError: Rendering service was interrupted
| (12):

Ugh, seeing that error message sends chills down my spine.
Googling it doesn't lead us to any useful answers.

So I, like most diligent XCode users, reached out to how we knew best to deal with XCode funkiness - ⌘⇧K - clear the build folder.

After waiting 10 minutes an eternity, the preview spins back up again. Only to crash, AGAIN!

Seriously Apple, that's not helpful at all. What the hell caused the rendering service to interrupt!?

Now there are a myriad of reasons why this particular error can happen, but if you're using Firebase and SwiftUI, it's very likely due to how your app initialises Firebase.

In a typical Firebase setup, your @main App looks like this:

Nothing looks wrong here, but consider what happens when you are already running an instance of your app on your Mac.

Because Firebase maintains a local cache, it has to open and read from a database file on your device.

When your app runs on your Mac, it has to open the database file.
When you run a SwiftUI Preview, it has to initialise the App.
It then tries to read from the same database file again.

So how do we tell Firebase not to do so when we're previewing our SwiftUI code?
We have to tell the App to not do anything when it's rendering a SwiftUI preview.

How do we do this?

We have to find out if the app is currently in Preview mode.
There are two ways to do this:

We can check if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] is "1" at run time, like so

Or, we can make use of dependency injection.
If you're not familiar with the terminology, I highly recommend reading A Gentle Introduction to Dependency Injection.

In short, it's basically a way of

Giving an object the things it needs to do its job.

There are many ways to do this, but the simplest way I've found is Resolver, a lightweight Swift dependency injection framework.

You can refer to the code mentioned in the article in this Github repository.