Perché il metodo UIApplicationDelegate `application (_: configurationForConnecting: options:)` non viene chiamato in modo affidabile

Aug 21 2020

Problema:

Trovo qualche comportamento inaspettato riguardo al metodo AppDelegate application(_:configurationForConnecting:options:).

La documentazione afferma:

UIKit chiama questo metodo poco prima di creare una nuova scena.

Mi aspetto che questo sia il caso ogni volta che l'app viene avviata.
Il metodo viene effettivamente chiamato quando avvio la mia app per la prima volta, tuttavia per tutti i lanci successivi non lo è .

Riprodurre:

Ho un test case molto semplice da riprodurre:

  • Xcode 12> Crea nuovo progetto> iOS> App (UIKit / Storyboard)
  • aggiungi un'istruzione di debug nel metodo in questo AppDelegatemodo:
      // 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)
      }
    
  • esegui l'app> "Sono stato chiamato!" viene stampato nella console
  • esegui di nuovo l'app> non viene stampato nulla.

Domanda:

Perché application(_:configurationForConnecting:options:)non viene chiamato al secondo lancio?
(È un comportamento previsto, se sì perché / È un bug di Apple)

Risposte

3 Adrian Oct 21 2020 at 05:10

Questo sembra essere un comportamento previsto e ha senso una volta capito cosa sta succedendo, ma non è documentato. Ho appena passato un po 'di tempo abbastanza traumatico per arrivare in fondo. Oh, Apple.

La cosa fondamentale da sapere è che quando riavvii un'app, vengono ripristinate le finestre dell'esecuzione precedente.

(Aiuta anche a ricordare che un'app può avere più tipi di finestra, ognuno rappresentato da una configurazione di scena, motivo per cui potresti implementare questo metodo delegato in primo luogo.)

Caso 1: App lanciata per la prima volta in assoluto

L'app non sa che tipo di scena inserire nella finestra e chiama application(_:configurationForConnecting:options:)per scoprirlo. Finora le cose sono come ci aspettiamo. (Se non si implementa questo metodo delegato, torna alla prima voce adatta nel Info.plistmanifesto della scena, se disponibile.)

Caso 2: nuova finestra creata (per app che supportano più finestre)

(ad esempio trascinando l'icona del dock su iPad). L'app non sa nemmeno cosa inserire in questa finestra. Come nel caso 1.

Caso 3: App rilanciata

Il sistema operativo vuole ripristinare le tue finestre. Per fare questo, ha ricordato le configurazioni di scena delle finestre che avevi aperto l'ultima volta. Sorpresa! Sa quali scene inserire nelle finestre e non chiede al delegato dell'app. Va solo avanti e crea le scene usando le configurazioni ricordate.

Per il povero sviluppatore che pensa in termini di una finestra creata all'avvio dell'app, questo crea confusione. Ma se pensi in termini di finestre che vengono ripristinate all'avvio, non create - anche quando ce n'è una sola - inizia ad avere senso.


Ora, se vuoi ripristinare le cose in modo che le tue finestre vengano dimenticate e il tuo metodo delegato viene chiamato al prossimo avvio:

  • per iOS, elimina l'app
  • per Catalyst, elimina il contenitore dell'app

Nota 1: in Catalyst, sembra che solo la prima finestra venga ripristinata al riavvio, ma per il resto il comportamento è lo stesso di sopra. Ora ho osservato che questo non è vero. Forse è incoerente.

Nota 2: puoi anche ripristinare il contenuto delle tue finestre , non solo il loro tipo , usando UIWindowSceneDelegatee UISceneSession.stateRestorationActivity, ma questa è un'altra storia.