Pubblicato da donnywals il 28 ottobre 2019Ottobre 2019
Quando crei un nuovo progetto in Xcode 11, potresti notare qualcosa che non hai visto prima. Invece di creare solo un file AppDelegate.swift
, un ViewController.swift
, uno storyboard e alcuni altri file, Xcode ora crea un nuovo file per te; il file SceneDelegate.swift
. Se non avete mai visto questo file prima, potrebbe essere abbastanza confuso capire cos’è, e come dovreste usare questo nuovo delegato di scena nella vostra app.
Per la fine del post di questa settimana saprete:
- A cosa serve il delegato di scena.
- Come potete implementare efficacemente il vostro delegato di scena.
- Perché il delegato di scena è una parte importante di iOS 13.
Partiamo subito, che ne dite?
Esaminare il nuovo modello di progetto Xcode
Ogni volta che create un nuovo progetto Xcode, avete la possibilità di scegliere se volete usare SwiftUI o Storyboard. Indipendentemente dalla tua scelta, Xcode genererà un nuovo tipo di modello di progetto su cui costruire. Daremo un’occhiata più da vicino ai file SceneDelegate.swift
e AppDelegate.swift
nella prossima sezione, ciò che è importante per ora è che tu capisca che Xcode ha creato questi file per te.
Oltre a questi due file delegati, Xcode fa qualcosa di un po’ più sottile. Date un’occhiata da vicino al vostro file Info.plist
. Dovresti vedere una nuova chiave chiamata Application Scene Manifest
con un contenuto simile alla seguente immagine:
Questo manifesto di scena specifica un nome e una classe di delegato per la tua scena. Nota che queste proprietà appartengono a un array (Application Session Role
), suggerendo che puoi avere più configurazioni nel tuo Info.plist
. Una chiave molto più importante che potresti aver già notato nello screenshot qui sopra è Enable Multiple Windows
. Questa proprietà è impostata di default su NO
. Impostare questa proprietà su YES
permetterà agli utenti di aprire più finestre della vostra applicazione su iPadOS (o anche su macOS). Essere in grado di eseguire più finestre di un’applicazione iOS fianco a fianco è una differenza enorme rispetto all’ambiente a finestra singola con cui abbiamo lavorato fino ad ora, e la capacità di avere più finestre è l’intera ragione per cui il ciclo di vita della nostra applicazione è ora mantenuto in due posti piuttosto che uno.
Diamo un’occhiata più da vicino a AppDelegate
e SceneDelegate
per capire meglio come questi due delegati lavorano insieme per abilitare il supporto per le finestre multiple.
Comprendere i ruoli di AppDelegate e SceneDelegate
Se avete costruito applicazioni prima di iOS 13, probabilmente conoscete il vostro AppDelegate
come l’unico posto che fa praticamente tutto ciò che riguarda il lancio della vostra applicazione, il foregrounding, il backgrounding e poi altro. In iOS 13, Apple ha spostato alcune delle responsabilità AppDelegate
al SceneDelegate
. Diamo un breve sguardo a ciascuno di questi due file.
Le responsabilità diAppDelegate
Il AppDelegate
è ancora il principale punto di ingresso per un’applicazione in iOS 13. Apple chiama i metodi AppDelegate
per diversi eventi del ciclo di vita dell’applicazione. Nel modello predefinito di Apple troverete tre metodi che Apple considera importanti da usare:
func application(_:didFinishLaunchingWithOptions:) -> Bool
func application(_:configurationForConnecting:options:) -> UISceneConfiguration
func application(_:didDiscardSceneSessions:)
Questi metodi hanno alcuni commenti che descrivono effettivamente ciò che fanno in modo abbastanza dettagliato da capire cosa fanno. Ma esaminiamoli comunque velocemente.
Quando la vostra applicazione è appena lanciata, viene chiamato func application(_:didFinishLaunchingWithOptions:) -> Bool
. Questo metodo è usato per eseguire la configurazione dell’applicazione. In iOS 12 o precedente, potreste aver usato questo metodo per creare e configurare un oggetto UIWindow
e assegnato un’istanza UIViewController
alla finestra per farla apparire.
Se la vostra applicazione sta usando delle scene, il vostro AppDelegate
non è più responsabile di fare questo. Dal momento che la vostra applicazione può ora avere più finestre, o UISceneSession
s attive, non ha molto senso gestire un oggetto finestra singola nel AppDelegate
.
Il func application(_:configurationForConnecting:options:) -> UISceneConfiguration
viene chiamato ogni volta che la vostra applicazione deve fornire una nuova scena, o finestra da visualizzare per iOS. Notate che questo metodo non viene chiamato quando la vostra applicazione viene lanciata inizialmente, viene chiamato solo per ottenere e creare nuove scene. Daremo uno sguardo più approfondito alla creazione e alla gestione di più scene in un post successivo del blog.
L’ultimo metodo nel modello AppDelegate
è func application(_:didDiscardSceneSessions:)
. Questo metodo viene chiamato ogni volta che un utente scarta una scena, per esempio facendola scorrere via nella finestra del multitasking o se lo fai programmaticamente. Se la vostra app non è in esecuzione quando l’utente lo fa, questo metodo sarà chiamato per ogni scena scartata poco dopo che func application(_:didFinishLaunchingWithOptions:) -> Bool
è stato chiamato.
In aggiunta a questi metodi predefiniti, il vostro AppDelegate
può ancora essere usato per aprire URL, catturare avvisi di memoria, rilevare quando la vostra app terminerà, se l’orologio del dispositivo è cambiato significativamente, rilevare quando un utente si è registrato per le notifiche remote e altro.
Suggerimento:
È importante notare che se attualmente stai usandoAppDelegate
per gestire l’aspetto della barra di stato della tua app, potresti dover fare alcune modifiche in iOS 13. Diversi metodi relativi alla barra di stato sono stati deprecati in iOS 13.
Ora che abbiamo un quadro migliore di quali sono le nuove responsabilità del vostro AppDelegate
, diamo un’occhiata al nuovo SceneDelegate
.
Responsabilità dello SceneDelegate
Se considerate il AppDelegate
come l’oggetto responsabile del ciclo di vita della vostra applicazione, il SceneDelegate
è responsabile di ciò che viene mostrato sullo schermo; le scene o finestre. Prima di continuare, stabiliamo un po’ di vocabolario relativo alle scene, perché non tutti i termini significano ciò che potreste pensare che significhino.
Quando avete a che fare con le scene, ciò che sembra una finestra al vostro utente è in realtà chiamato un UIScene
che è gestito da un UISceneSession
. Quindi, quando ci riferiamo alle finestre, in realtà ci riferiamo agli oggetti UISceneSession
. Cercherò di attenermi a questa terminologia il più possibile nel corso di questo post sul blog.
Ora che siamo sulla stessa pagina, guardiamo il file SceneDelegate.swift
che Xcode ha creato quando ha creato il nostro progetto.
Ci sono diversi metodi nel file SceneDelegate.swift
di default:
scene(_:willConnectTo:options:)
sceneDidDisconnect(_:)
sceneDidBecomeActive(_:)
sceneWillResignActive(_:)
sceneWillEnterForeground(_:)
sceneDidEnterBackground(_:)
Questi metodi dovrebbero sembrarvi molto familiari se avete familiarità con i AppDelegate
esistenti prima di iOS 13. Diamo prima un’occhiata a scene(_:willConnectTo:options:)
, questo metodo probabilmente sembra il meno familiare per voi ed è il primo metodo chiamato nel ciclo di vita di un UISceneSession
.
L’implementazione predefinita di scene(_:willConnectTo:options:)
crea la vostra vista iniziale del contenuto (ContentView
se state usando SwiftUI), crea un nuovo UIWindow
, imposta il rootViewController
della finestra e rende questa finestra la finestra chiave. Potreste pensare a questa finestra come la finestra che l’utente vede. Questo, sfortunatamente, non è il caso. Le finestre esistono da prima di iOS 13 e rappresentano la viewport in cui opera la vostra app. Quindi, il UISceneSession
controlla la finestra visibile che l’utente vede, il UIWindow
che crei è la vista contenitore per la tua applicazione.
Oltre a impostare le viste iniziali, puoi usare scene(_:willConnectTo:options:)
per ripristinare l’interfaccia utente della tua scena nel caso la tua scena si sia scollegata in passato. Per esempio, perché è stata mandata in background. Puoi anche leggere l’oggetto connectionOptions
per vedere se la tua scena è stata creata a causa di una richiesta di HandOff o forse per aprire un URL. Vi mostrerò come farlo più avanti in questo post del blog.
Una volta che la vostra scena si è collegata, il prossimo metodo nel ciclo di vita della vostra scena è sceneWillEnterForeground(_:)
. Questo metodo viene chiamato quando la vostra scena prenderà la scena. Questo potrebbe essere quando la vostra applicazione passa dallo sfondo al primo piano, o se sta diventando attiva per la prima volta. Successivamente, viene chiamato sceneDidBecomeActive(_:)
. Questo è il punto in cui la vostra scena è impostata, visibile e pronta per essere utilizzata.
Quando la vostra app passa allo sfondo, vengono chiamati sceneWillResignActive(_:)
e sceneDidEnterBackground(_:)
. Non mi addentrerò in questi metodi ora, poiché il loro scopo varia per ogni applicazione, e i commenti nel modello Xcode fanno un buon lavoro per spiegare quando questi metodi vengono chiamati. In realtà, sono sicuro che puoi capire da solo quando questi metodi vengono chiamati.
Un metodo più interessante è sceneDidDisconnect(_:)
. Ogni volta che la vostra scena viene mandata in background, iOS potrebbe decidere di disconnettersi e cancellare la vostra scena per liberare risorse. Questo non significa che la vostra app è stata uccisa o non è più in esecuzione, significa semplicemente che la scena passata a questo metodo non è più attiva e si disconnetterà dalla sua sessione.
Nota che anche la sessione stessa non è necessariamente scartata, iOS potrebbe decidere di ricollegare una scena a una sessione di scena in qualsiasi momento, per esempio quando un utente porta di nuovo una particolare scena in primo piano.
La cosa più importante da fare in sceneDidDisconnect(_:)
è scartare qualsiasi risorsa che non è necessario tenere in giro. Questi potrebbero essere dati che sono facilmente caricati dal disco o dalla rete o altri dati che puoi ricreare facilmente. È anche importante assicurarsi di conservare tutti i dati che non possono essere facilmente ricreati, come per esempio qualsiasi input che l’utente ha fornito in una scena e che si aspetterebbe di trovare ancora lì quando ritorna in una scena.
Considera un’applicazione di elaborazione del testo che supporta scene multiple. Se un utente sta lavorando in una scena, poi la sfonda per fare qualche ricerca su Safari e cambiare la sua musica in Spotify, si aspetterebbe assolutamente che tutto il suo lavoro esista ancora nell’app di elaborazione del testo, anche se iOS potrebbe aver disconnesso la scena dell’app di elaborazione del testo per un po’. Per ottenere questo, l’app deve conservare i dati richiesti, e dovrebbe codificare lo stato attuale dell’app in un oggetto NSUserActivity
che può essere letto più tardi in scene(_:willConnectTo:options:)
quando la scena viene ricollegata.
Siccome questo flusso di lavoro di collegare, scollegare e ricollegare le scene separerà le buone app dalle grandi, diamo un’occhiata a come puoi implementare il ripristino dello stato nella tua app.
Eseguire una configurazione aggiuntiva della scena
Ci sono diverse ragioni per cui devi eseguire una configurazione aggiuntiva quando una scena viene configurata. Potresti dover aprire un URL, gestire una richiesta di Handoff o ripristinare lo stato. In questa sezione, mi concentrerò principalmente sul ripristino dello stato, poiché è probabilmente lo scenario più complesso che potresti dover gestire.
Il ripristino dello stato inizia quando la tua scena viene scollegata e viene chiamato sceneDidDisconnect(_:)
. A questo punto, è importante che la tua applicazione abbia già uno stato impostato che può essere ripristinato in seguito. Il modo migliore per farlo è usare NSUserActivity
nella vostra applicazione. Se state usando NSUserActivity
per supportare Handoff, Siri Shortcuts, indicizzazione Spotlight e altro, non avete molto lavoro extra da fare. Se non usate ancora NSUserActivity
, non preoccupatevi. Una semplice attività utente potrebbe apparire un po’ come segue:
let activity = NSUserActivity(activityType: "com.donnywals.DocumentEdit")activity.userInfo =
Nota che questa attività utente non è strutturata come Apple raccomanda, è un esempio molto scarno inteso a illustrare il ripristino dello stato. Per una guida completa su NSUserActivity
, ti consiglio di dare un’occhiata alla documentazione di Apple su questo argomento.
Quando arriva il momento di fornire un’attività utente che può essere ripristinata in un momento successivo, il sistema chiama il metodo stateRestorationActivity(for:)
sul tuo SceneDelegate
. Nota che questo metodo non fa parte del template di default
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { return scene.userActivity}
Facendo questo associa l’attività utente attualmente attiva per una scena alla sessione della scena. Ricordate che ogni volta che una scena viene scollegata, il UISceneSession
che possiede il UIScene
non viene scartato per permettere alla sessione di riconnettersi ad una scena. Quando questo accade, scene(_:willConnectTo:options:)
viene chiamato di nuovo. In questo metodo, avete accesso al UISceneSession
che possiede il UIScene
in modo da poter leggere il stateRestorationActivity
della sessione e ripristinare lo stato dell’applicazione come necessario:
if let activity = session.stateRestorationActivity, activity.activityType == "com.donnywals.DocumentEdit", let documentId = activity.userInfo as? String { // find document by ID // create document viewcontroller and present it}
Naturalmente, i dettagli di questo codice varieranno in base alla vostra applicazione, ma l’idea generale dovrebbe essere chiara.
Se ci si aspetta che il vostro UISceneSession
gestisca un URL, potete ispezionare il urlContexts
dell’oggetto connectionOptions
per trovare gli URL che la vostra scena dovrebbe aprire e le informazioni su come la vostra applicazione dovrebbe farlo:
for urlContext in connectionOptions.urlContexts { let url = urlContext.url let options = urlContext.options // handle url and options as needed}
L’oggetto options
conterrà informazioni sul fatto che la vostra scena dovrebbe aprire l’URL sul posto, quale applicazione ha richiesto l’apertura dell’URL e altri metadati sulla richiesta.
Le basi del ripristino dello stato in iOS 13 con il SceneDelegate
sono sorprendentemente semplici, soprattutto perché è costruito su NSUserActivity
, il che significa che molte applicazioni non dovranno fare troppo lavoro per iniziare a supportare il ripristino dello stato per le loro scene.
Tenete a mente che se volete avere il supporto per più scene per la vostra applicazione su iPadOS, il ripristino della scena è particolarmente importante in quanto iOS potrebbe disconnettere e riconnettere le scene quando passano dal primo piano allo sfondo e viceversa. Soprattutto se la vostra applicazione permette all’utente di creare o manipolare oggetti in una scena, un utente non si aspetterebbe che il suo lavoro sparisca se sposta una scena sullo sfondo per un momento.
In sintesi
In questo post del blog, avete imparato molto. Avete imparato quali ruoli i AppDelegate
e i SceneDelegate
svolgono in iOS 13 e come sono i loro cicli di vita. Ora sapete che il AppDelegate
è responsabile della reazione agli eventi a livello di applicazione, come il lancio dell’app, per esempio. Il SceneDelegate
è responsabile degli eventi relativi al ciclo di vita della scena. Per esempio, la creazione della scena, la distruzione e il ripristino dello stato di un UISceneSession
. In altre parole, la ragione principale per cui Apple ha aggiunto UISceneDelegate
a iOS 13 è stata quella di creare un buon punto di ingresso per le applicazioni multi-finestra.
Dopo aver imparato le basi di UISceneDelegate
, avete visto un esempio molto semplice di come appare il ripristino dello stato in iOS 13 con UISceneSession
e UIScene
. Naturalmente, c’è molto di più da imparare su come la vostra app si comporta quando un utente genera più UISceneSession
per la vostra app, e come queste scene potrebbero dover rimanere sincronizzate o condividere i dati.
Se volete saperne di più sul supporto di finestre multiple per la vostra app per iPad (o la vostra app per macOS), assicuratevi di controllare il mio post Aggiungere il supporto per finestre multiple alla vostra app iPadOS. Grazie per aver letto, e non esitare a contattarmi su Twitter se hai domande o feedback da darmi.
Practical Combine
Impara tutto quello che devi sapere su Combine e come puoi usarlo nei tuoi progetti con il mio nuovo libro Practical Combine. Avrai tredici capitoli, un Playground e una manciata di progetti di esempio per aiutarti a diventare operativo con Combine il prima possibile.
Il libro è disponibile come download digitale a soli $29.99!
Ottieni Practical Combine