Supporting Scenes in existing code

I have tested creating a new App in XCode 11 with IOS13 target.


I noticed the new SceneDelegate.swift and all the changes that occured in appDelegate.


So far so good.


But now, what should I change in an existing app to make it support scenes ?

I have tried modiying the AppDelegate and creating an UISceneDelegate to mimic what is in this small demo app.


It compiles, but I just get the launchscreen and then a black screen. Clearly, I am not connected to the right view.


What am I missing ?

Answered by Claude31 in 386601022

OK, just got it !


In infos.plist, need to add Application Scene Manifest section.


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>UIApplicationSupportsMultipleScenes</key>

<false/>

<key>UISceneConfigurations</key>

<dict>

<key>UIWindowSceneSessionRoleApplication</key>

<array>

<dict>

<key>UISceneConfigurationName</key>

<string>Default Configuration</string>

<key>UISceneDelegateClassName</key>

<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>

<key>UISceneStoryboardFile</key>

<string>Main</string>

</dict>

</array>

</dict>

</dict>

</plist>



And everything works.


I just have a few open points :

- When I introduce a print in any of the sceneDelegate, does not get printed ; seems that API not called ?

    @available(iOS 13.0, *)
    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
        print("resign Scene")
    }

Unless I specify IOS 13 as target.


- I have removed manually the unneeded API from AppDelegate ; but they will be needed to run < IOS12.

Do I need to remove (seems to work OK if I leave it) to avoid interference with sceneDelegate ?


If needed, how can I do it with an @available statement (to test unavailable for IOS < 13) ?

I could not find the @available syntax for that.

Accepted Answer

OK, just got it !


In infos.plist, need to add Application Scene Manifest section.


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>UIApplicationSupportsMultipleScenes</key>

<false/>

<key>UISceneConfigurations</key>

<dict>

<key>UIWindowSceneSessionRoleApplication</key>

<array>

<dict>

<key>UISceneConfigurationName</key>

<string>Default Configuration</string>

<key>UISceneDelegateClassName</key>

<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>

<key>UISceneStoryboardFile</key>

<string>Main</string>

</dict>

</array>

</dict>

</dict>

</plist>



And everything works.


I just have a few open points :

- When I introduce a print in any of the sceneDelegate, does not get printed ; seems that API not called ?

    @available(iOS 13.0, *)
    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
        print("resign Scene")
    }

Unless I specify IOS 13 as target.


- I have removed manually the unneeded API from AppDelegate ; but they will be needed to run < IOS12.

Do I need to remove (seems to work OK if I leave it) to avoid interference with sceneDelegate ?


If needed, how can I do it with an @available statement (to test unavailable for IOS < 13) ?

I could not find the @available syntax for that.

Hi, Claude31

I have the same question as you meet. But the difference is that I had added the Application Scene Manifest section. In a new app I created, everything is ok. When I add the same config in an existing app, it's not working. Just like you said, it seems that API not called. So I want to make sure that you just forget to add the Application Scene Manifest section in your info.plist?

Effectively, there is on thing more to check: have you declared a UIWindow in SceneDelegate ?

The complete file should look like this:


import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    @available(iOS 13.0, *)
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
    }

    @available(iOS 13.0, *)
    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    @available(iOS 13.0, *)
    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    @available(iOS 13.0, *)
    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    @available(iOS 13.0, *)
    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
        // Do it here, not in app delegate (iOS 13)
        NotificationCenter.default.post(name: .kRefreshPrefs, object: self)     // I send a notification
    }

    @available(iOS 13.0, *)
    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }

}

And keep also a

    var window: UIWindow?

in AppDelegate, for iOS 12 compatibility.

Thanks for your reply,

I'm sure that I have created that property (an UIWindow object) and keep it in AppDelegate. When I run the project, every method defined in SceneDelegate is never to be called. It's so confusing. Ummm. Maybe something needs to change in my .project file or .workspace file? And my min deployment target is iOS 9. Do you have a clue?

Do you print logs to check if functions are called ?


Have you included @available statement ?


What is the iOS version on device or simulator ?

Supporting Scenes in existing code
 
 
Q