How to setup UIApplicationDelegate that uses UIScenes for mirroring with AirPlay

Hi!

I am developing a game for iOS using Objective-C and C++.

I am trying to migrate an app to scene-based life cycle, but having a problem while mirroring screen from iPhone to MacBook using AirPlay. At this moment I don't want to implement multi-window (or multi-scene) support. The only thing I want is to have ability of screen mirroring.

From the documentation from here and here I can't understand which UISceneConfiguration should I return. If I define a UIWindowSceneDelegate for the configuration, how should I handle scene:willConnectToSession:options: if the window has been already created for main device screen? Returning nil is not documented. Is there any examples?

Also, I would expect that defining UIApplicationSupportsMultipleScenes to NO in Info.plist will automatically disable second scene creating. This is mentioned in documentation here, but this is not true, because I still see second scene creation (its pointer differs from one that was already created) in UIWindowSceneDelegate.

What am I doing wrong? Any hints are highly appreciated!

Dear s_petrovskiy2,

Just for clarification, can you please answer:

  1. By "mirroring," you mean that the external device would be displaying identical content to what is on your iPhone, correct?

  2. Are you using storyboards, and defining your scenes in your Info.plist? If so, then you would not need to handle scene:willConnectToSession:options: for the default configuration of your root view controller main device screen. (If you are loading it programmatically, you would need to, as seen in Tech Note TN3187: Migrating to the UIKit scene-based life cycle; but since your window has already been created, it sounds like you are using the Info.plist method.

  3. In which UIWindowSceneDelegate delegate method are you seeing a second scene created? When does it take place during your app's operation? After connecting to the AirPlay receiver (your MacBook)?

  4. How are you initiating an AirPlay connection with your app- are you using the built-in Screen Mirroring button in iOS?

Please let me know,

Richard Yeh  Developer Technical Support

Dear Richard,

Thank you for reply!

  1. Yes, I would like external device (MacBook) to display the same content as iOS device.
  2. I use fully programmatical approach. Sorry for the misleading information provided, by saying "window has already been created" I meant that the instance of UIWindow and its rootViewController has already been created, before I enabled "Screen Mirroring". After enabling "Screen Mirroring" I see the instantiation of second UIWindowSceneDelegate and willConnectToSession of the second instance is supplied with new scene instance. For my app this is actually the same as creating new instance of my app.

To avoid creating second instances I've placed the following code:

if(connectingSceneSession.role == UIWindowSceneSessionRoleExternalDisplayNonInteractive)
{
    return nil;
}

in AppDelegate's configurationForConnectingSceneSession method. But this is not documented, and I am not sure if this will work for all cases.

  1. In willConnectToSession. It takes place after connecting to the AirPlay receiver.
  2. Yes, using the built-in Screen Mirroring button in iOS:

I would expect that:

<dict>
    <key>UIApplicationSceneManifest</key>
    <dict>
       <key>UIApplicationSupportsMultipleScenes</key>
       <false/>
    </dict>
</dict>

should be enough that application(_:configurationForConnecting:options:) is called at most once, because I explicitly said that I don't support multiple scenes.

But even if I define scene configuration in Info.plist:

<dict>
    <key>UIApplicationSceneManifest</key>
    <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>SceneDelegate</string>
             </dict>
          </array>
       </dict>
    </dict>
</dict>

application(_:configurationForConnecting:options:) is still called twice. I defined the single scene configuration I support and defined that I don't support multi scene, so I would expect that this method is not called at all.

Best regards.

Dear s_petrovskiy2,

Thank you for providing additional context with your answers! Here's clarification on how mirroring fits in the context of the scene-based lifecycle, which is a bit of an edge case:

When mirroring is activated, a scene session with the role windowExternalDisplayNonInteractive is indeed sent to the delegate callback. However, you do not need to put anything in the scene unless you want to stop mirroring. You are free to ignore it, as you did in your code by returning nil. Identical mirroring is not actually creating a new scene, despite the callback getting triggered.

In the case that you do want to end mirroring and display different content on the display, you would create a UIWindow and attach it to the scene. Afterwards, removing that UIWindow from the UIWindowScene would revert the display back to identical mirroring.

This behavior is alluded to in the mirroredScreen property for the pre-scene UIScreen lifecycle:

To disable mirroring and use the external display for presenting unique content, create a window and associate it with a UIWindowSceneSessionRoleExternalDisplayNonInteractive scene.

I believe that when additional displays are added, the callback is triggered regardless of the Info.plist. But the system should otherwise respect your UIApplicationSupportsMultipleScenes value in all other cases, since your intention isn't to introduce additional scene instances.

Let me know if you have more questions,

Richard Yeh  Developer Technical Support

How to setup UIApplicationDelegate that uses UIScenes for mirroring with AirPlay
 
 
Q