SwiftUI View Not Initialized on Background Relaunch (CoreBluetooth / Cold Start?)

I have a question regarding cold start and pre-warming behavior on iOS.

I’m developing a SwiftUI app that continuously receives data from a BLE device in the background. We’ve observed that after the BLE stream stops, the OS often terminates our app. Later, when the sensor comes back into range, iOS appears to relaunch (or reinitialize) the app.

In our app, we use a WindowGroup like this:

WindowGroup {
    AppView(store: store)
}

We’ve placed our BLE reconnection logic inside a .task modifier in AppView.

What’s confusing is this:

  • Most of the time, when the app is relaunched, AppView is created and the .task runs as expected.
  • However, in about 1 out of 10 cases, AppView is not created at all, so the .task does not execute.

I’m trying to understand:

  • Under what conditions does iOS relaunch an app without fully initializing the SwiftUI view hierarchy?
  • Is this related to pre-warming or background relaunch mechanisms (e.g., CoreBluetooth state restoration)?
  • What determines whether the WindowGroup and root view are actually instantiated?

Any insight into the system’s relaunch behavior or lifecycle in this scenario would be greatly appreciated.

I will leave the discussion of why UI elements are sometimes not initialized for non-UI (background) launches to others who have more expertise on these things. Here, I will address the CoreBluetooth problem you are obviously having due to this behavior.

In general it is not a correct pattern to make app-wide non-UI functionality dependent on UI elements like Views.

Indeed, CoreBluetooth works at the app level, and needs to be initialized and used at the app level, not hidden behind a view. When the system relaunches your app due to the sensor coming back in range, it is doing so because the expectation is your app needs to take some action and respond to the BLE event that occurred (whether it is the sensor coming in or out of range, new data arriving, connection dropping, etc.) as soon as possible, and it should not be waiting for the app views to be rebuilt after launch, even in the 9 out of 10 case this seems to work for you.

The correct use is to bring all CoreBluetooth functionality to the app level. Prior to SwiftUI, this would have been done in the Application Delegate class, which controls the app's life cycle, and it is executed before anything else in the app.

In SwiftUI, this can still be done, but in a roundabout way.

For CoreBluetooth to function when relaunched in the background (by Bluetooth State Restoration), before anything else, you need to reinstantiate your CBManager (CBCentralManager or CBPeripheralManager based on your use case) immediately, preferably inside applicationDidFinishLaunching() with the same CBCentralManagerOptionRestoreIdentifierKey, so the system can hand over the preserved manager back to your app. To do that your willRestore() function will be called when ready, so make sure you also move that to the same class I will describe below.

This needs to be don in an app'a AppDelegate. Because SwiftUI does not expose an AppDelegate you can add code to, you will need to do it the way it is described at UIApplicationDelegateAdaptor

Basically you would create a class that contains the necessary app delegate functions, and then use the @UIApplicationDelegateAdaptor construct so that class is executed when the app is launched =- this will happen whether it is launched in the background or the foreground.

If how to make this change is not clear, you can probably find examples of it on the internet. And we also do have a CoreBluetooth sample Interacting with Bluetooth peripherals during background app refresh that implements this construct.

As this is a watchOS sample, the code itself may not be of direct use to you, but the project will demonstrate how the "App Delegate" is constructed for CoreBluetooth purposes in a SwiftUI app.


Argun Tekant /  WWDR Engineering / Core Technologies

SwiftUI View Not Initialized on Background Relaunch (CoreBluetooth / Cold Start?)
 
 
Q