Почему метод UIApplicationDelegate `application (_: configurationForConnecting: options:)` не вызывается надежно

Aug 21 2020

Проблема:

Я обнаружил неожиданное поведение метода AppDelegate application(_:configurationForConnecting:options:).

В документации указано:

UIKit вызывает этот метод незадолго до создания новой сцены.

Я ожидал, что это будет происходить каждый раз при запуске приложения.
Метод действительно вызывается, когда я запускаю свое приложение в первый раз, однако при всех последующих запусках это не так .

Воспроизвести:

У меня есть очень простой тестовый пример для воспроизведения:

  • Xcode 12> Создать новый проект> iOS> Приложение (UIKit / Storyboard)
  • добавьте в метод отладочную инструкцию AppDelegateследующим образом:
      // 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)
      }
    
  • запустите приложение> "Меня позвали!" печатается в консоли
  • запустите приложение снова> ничего не печатается.

Вопрос:

Почему application(_:configurationForConnecting:options:)не вызывается на второй запуск?
(Это ожидаемое поведение, если да, почему / Это ошибка Apple)

Ответы

3 Adrian Oct 21 2020 at 05:10

Это кажется ожидаемым поведением и имеет смысл, если вы понимаете, что происходит, но это не задокументировано. Я только что потратил довольно болезненное время на то, чтобы разобраться в этом. О, Apple.

Главное, что нужно знать, - это то, что при перезапуске приложения окна из предыдущего запуска восстанавливаются.

(Это также помогает помнить, что приложение может иметь несколько типов окон - каждый из которых представлен конфигурацией сцены - поэтому вы можете в первую очередь реализовать этот метод делегата.)

Случай 1: приложение запущено впервые

Приложение не знает, какую сцену поместить в окно, и звонит, application(_:configurationForConnecting:options:)чтобы узнать. Пока все так, как мы ожидали. (Если вы не реализуете этот метод делегата, он просто вернется к первой подходящей записи в Info.plistманифесте вашей сцены, если она есть.)

Случай 2: создано новое окно (для приложений, поддерживающих несколько окон)

(например, перетащив значок док-станции на iPad). Приложение также не знает, что поместить в это окно. То же, что и в случае 1.

Случай 3: приложение перезапущено

ОС хочет восстановить ваши окна. Для этого он запомнил конфигурации сцен окон, которые вы открывали в прошлый раз. Сюрприз! Он знает, какие сцены помещать в окна, и не спрашивает делегата вашего приложения. Он просто идет вперед и создает сцены, используя запомненные конфигурации.

Это сбивает с толку плохого разработчика, который думает о том, что окно создается при запуске приложения. Но если вы думаете о том, что окна восстанавливаются при запуске, а не создаются - даже когда есть только одно - это начинает иметь смысл.


Теперь, если вы хотите сбросить настройки, чтобы ваши окна были забыты, а ваш метод делегата вызывался при следующем запуске:

  • для iOS удалите приложение
  • для Catalyst удалите контейнер приложения

Примечание 1: в Catalyst кажется, что при перезапуске восстанавливается только первое окно, но в остальном поведение такое же, как указано выше. Теперь заметили, что это неправда. Возможно, это непоследовательно.

Примечание 2: вы также можете восстановить содержимое ваших окон , а не только их тип , используя UIWindowSceneDelegateи UISceneSession.stateRestorationActivity, но это уже другая история.