Migrating SwiftUI lifecycle to UIKit's

Hi, everyone

I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes.

Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app.

I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application.

Thank you very much.

Answered by marcos-alvarez in 803255022

After messing around with it I've found the problem and how to fix it.

The problem was that there's an option in the Build Settings of Xcode called Application Scene Manifest (Generation) by default when the project is created with SwiftUI (I don't know about UIKit) this option is set to YES. This configuration seems to override whatever values you set in your Info.plist regarding the Scene Configuration, thus leaving the app with a black screen with no content. The solution wat setting that configuration to NO and, magically, the screen appeared correctly on my device.

As I've not seen anything related to this setting anywhere on the internet, I'll mark this post as the answer in hopes that anyone fighting with this behaviour can find this post and, maybe, make their lives easier.

@marcos-alvarez Your UIKit Lifecycle should be scene based and not reply on your AppDelegate.

Could you please post a copy of your app’s Info.plist file and SceneDelegate. Thanks

What DTS engineer said, plus an idea there. Have you created a Scene Manifest in the info.plist.

It should look like this:

Note: interesting to see a case of migrating from SwiftUI to UIKit… Most often we see the reverse.

Thank you both @Claude31 and @DTS Engineer for the quick response.

This is how my Info.plist file is looking right now"

I am not using Storyboards, so I am not including the Storyboard name property. Also, the schema/target that I am running has a name like my_app-stg, for what I've seen, $(PRODUCT_MODULE_NAME).SceneDelegateproduces the value my_app_stg.SceneDelegate which, for what I've tested, does not exist, whereas my_app-stg.SceneDelegate does exist. Because of this I am relying on the AppDelegate to create the proper configuration this is why I do the following in the application(application:connectingSceneSession:options):

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
        configuration.delegateClass = SceneDelegate.self
        
        return configuration
    }

This indeed allows me to set the correct SceneDelegate for my app, but as this method is not called, the SceneDelegate cannot be set correctly. I've also tried to replace $(PRODUCT_MODULE_NAME).SceneDelegate by my_app-stg.SceneDelegate to not avail.

After a lot of debugging and messing with the existing code, I've found a way to circumvent the problem. It seems like using the SwiftUI lifecycle and attaching the SceneDelegate does work. At least partially, because right now I am using an EmptyView as the root of my app in SwiftUI even though I then initialise the app in the SceneDelegate correctly. This allows me to partially use the UIKit lifecycle, but I cannot abandon SwiftUI's lifecycle completely. I like the solution if it can be used as a partial update for the app that should be followed by another update that really uses only UIKit's lifecycle.

When I check the UIApplication object in the AppDelegate, I see that it has an openSession already configured, and I've seen that it has a AppSceneDelegate associated (which presumably is created by SwiftUI). Now, my question is, is there a way to remove this opened session? It will be great as a final update for my app, because having a hybrid SwiftUI-UIKit's lifecycle is a solution I don't really love right now.

Accepted Answer

After messing around with it I've found the problem and how to fix it.

The problem was that there's an option in the Build Settings of Xcode called Application Scene Manifest (Generation) by default when the project is created with SwiftUI (I don't know about UIKit) this option is set to YES. This configuration seems to override whatever values you set in your Info.plist regarding the Scene Configuration, thus leaving the app with a black screen with no content. The solution wat setting that configuration to NO and, magically, the screen appeared correctly on my device.

As I've not seen anything related to this setting anywhere on the internet, I'll mark this post as the answer in hopes that anyone fighting with this behaviour can find this post and, maybe, make their lives easier.

Migrating SwiftUI lifecycle to UIKit's
 
 
Q