Why is UIApplicationDelegate method `application(_:configurationForConnecting:options:)` not called reliably
Asked Answered
P

1

21

Issue:

I find some unexpected behavior regarding the AppDelegate method application(_:configurationForConnecting:options:).

The documentation states:

UIKit calls this method shortly before creating a new scene.

I would expect that this is the case every time the app is launched.
The method is indeed called when I launch my app for the first time, however for all subsequent launches, it is not.

Reproduce:

I have a very simple test case to reproduce:

  • Xcode 12 > Create new Project > iOS > App (UIKit/Storyboard)
  • add a debugging statement in the method in AppDelegate like so:
      // from Apple's sample project:
      func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
          // Called when a new scene session is being created.
          // Use this method to select a configuration to create the new scene with.
          print("I was called!").  // <--- debugging statement
          return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
      }
    
  • run the app > "I was called!" gets printed in the console
  • run the app again > nothing get's printed.

Question:

Why is application(_:configurationForConnecting:options:) not being called on second launch?
(Is it expected behavior, if yes why / Is it a bug by Apple)

Pawnshop answered 21/8, 2020 at 9:18 Comment(0)
S
38

This seems to be expected behaviour, and makes sense once you understand what's going on, but it's not documented. I've just spent some fairly traumatic time getting to the bottom of it. Oh, Apple.

The key thing to know is that when you relaunch an app, the windows from the previous run are restored.

(It also helps to remember that an app can have multiple types of window – each represented by a scene configuration – which is why you might implement this delegate method in the first place.)

Case 1: App launched for the first time ever

The app doesn't know what type of scene to put in the window, and calls application(_:configurationForConnecting:options:) to find out. So far things are as we expect. (If you don't implement this delegate method, it just falls back to the first suitable entry in your Info.plist's scene manifest, if it has one.)

Case 2: New window created (for apps that support multiple windows)

(e.g. by dragging the dock icon on iPad). The app doesn't know what to put in this window either. Same as case 1.

Case 3: App relaunched

The OS wants to restore your windows. To do this, it has remembered the scene configs of the windows you had open last time. Surprise! It knows what scenes to put in the windows, and doesn't ask your app delegate. It just goes ahead and creates the scenes using the remembered configs.

For the poor developer thinking in terms of a window being created when the app starts up, this is confusing. But if you think in terms of windows being restored at startup, not created - even when there is only one - it starts to make sense.


Now, if you want to reset things so your windows are forgotten and your delegate method is called on next launch:

  • for iOS, delete the app
  • for Catalyst, delete the app's container

Note 1: In Catalyst, it seems that only the first window is restored on relaunch, but otherwise behaviour is the same as above. Have now observed this not to be true. Perhaps it's inconsistent.

Note 2: You can also restore your windows' content, not just their type, using UIWindowSceneDelegate and UISceneSession.stateRestorationActivity, but that's another story.

Stinson answered 21/10, 2020 at 5:10 Comment(3)
Basically this is correct. I filed a bug and Apple told me.Icy
Thanks for the detailed explanation – I was planning to use this method to setup dependencies for the scene's viewController but it is apparently a bad idea. Actually I am still looking for a way to properly do dependency injection on iOS but that too is another story.Pawnshop
Ah, dependency injection. People's definition of "properly" seems to vary a lot! Personally I find a lot of solutions require too much plumbing and I wrote a blog post about that (luxmentis.org/ridiculously-simple-dependency-injection-in-swift). But that's for a bunch of globals, and it sounds like you want per-scene dependencies. However you go about it, perhaps your scene delegates would be the right place to manage them? I'm currently experimenting with supplying context-specific dependencies via the view hierarchy - like @EnvironmentObject, but I'm working in UIKit, not SwiftUI.Stinson

© 2022 - 2024 — McMap. All rights reserved.