Förstå iOS 13 Scene Delegate

Publicerad av donnywals den 28 oktober 201928 oktober 2019

När du skapar ett nytt projekt i Xcode 11 kanske du märker något som du inte har sett förut. Istället för att bara skapa en AppDelegate.swift-fil, en ViewController.swift, en storyboard och några andra filer skapar Xcode nu en ny fil åt dig; SceneDelegate.swift-filen. Om du aldrig har sett den här filen tidigare kan det vara ganska förvirrande att förstå vad den är och hur du ska använda den nya scendelegaten i din app.

I slutet av veckans blogginlägg kommer du att veta:

  • Vad scendelegaten används till.
  • Hur du effektivt kan implementera din scendelegat.
  • Varför scene delegate är en viktig del av iOS 13.

Vi hoppar in direkt, eller hur?

Undersökning av den nya mallen för Xcode-projektet

När du skapar ett nytt Xcode-projekt har du möjlighet att välja om du vill använda SwiftUI eller Storyboards. Oavsett vilket val du gör här kommer Xcode att generera en ny typ av projektmall som du kan bygga på. Vi kommer att titta närmare på SceneDelegate.swift– och AppDelegate.swift-filerna i nästa avsnitt, det viktiga för tillfället är att du förstår att Xcode har skapat dessa filer åt dig.

Förutom dessa två delegatfiler gör Xcode något lite mer subtilt. Titta noga på din Info.plist-fil. Du bör se en ny nyckel som heter Application Scene Manifest med ett innehåll som liknar följande bild:

Skärmdump av scenmanifestet i filen Info.plist

Skärmdump av scenmanifestet i filen Info.plist

Detta scenmanifest anger ett namn och en delegatklass för din scen. Observera att dessa egenskaper tillhör en array (Application Session Role), vilket tyder på att du kan ha flera konfigurationer i din Info.plist. En mycket viktigare nyckel som du kanske redan har upptäckt i skärmbilden ovan är Enable Multiple Windows. Den här egenskapen är som standard inställd på NO. Om du ställer in den här egenskapen till YES kan användare öppna flera fönster av ditt program på iPadOS (eller till och med på macOS). Att kunna köra flera fönster av en iOS-applikation sida vid sida är en enorm skillnad från den miljö med ett enda fönster som vi har arbetat med hittills, och möjligheten att ha flera fönster är hela anledningen till att vår applikations livscykel nu upprätthålls på två ställen i stället för ett.

Låt oss ta en närmare titt på AppDelegate och SceneDelegate för att bättre förstå hur dessa två delegater arbetar tillsammans för att möjliggöra stöd för flera fönster.

Understanding the roles of AppDelegate and SceneDelegate

Om du har byggt appar före iOS 13 känner du förmodligen till din AppDelegate som den enda plats som gör så gott som allt som har att göra med lansering av din applikations start, förgrundsbild, bakgrundsbild och lite till. I iOS 13 har Apple flyttat en del av AppDelegates ansvar till SceneDelegate. Låt oss ta en kort titt på var och en av dessa två filer.

AppDelegate’s responsibilities

Den AppDelegate är fortfarande den viktigaste ingångspunkten för ett program i iOS 13. Apple kallar AppDelegate-metoder för flera livscykelhändelser på applikationsnivå. I Apples standardmall hittar du tre metoder som Apple anser vara viktiga för dig att använda:

  • func application(_:didFinishLaunchingWithOptions:) -> Bool
  • func application(_:configurationForConnecting:options:) -> UISceneConfiguration
  • func application(_:didDiscardSceneSessions:)

De här metoderna har några kommentarer som faktiskt beskriver vad de gör tillräckligt detaljerat för att du ska förstå vad de gör. Men låt oss gå igenom dem snabbt ändå.

När ditt program precis har startats anropas func application(_:didFinishLaunchingWithOptions:) -> Bool. Den här metoden används för att utföra inställningen av programmet. I iOS 12 eller tidigare kan du ha använt den här metoden för att skapa och konfigurera ett UIWindow-objekt och tilldelat en UIViewController-instans till fönstret för att få det att visas.

Om din app använder scener är din AppDelegate inte längre ansvarig för att göra detta. Eftersom din applikation nu kan ha flera fönster, eller UISceneSessions aktiva, är det inte särskilt meningsfullt att hantera ett enda fönsterobjekt i AppDelegate.

Den func application(_:configurationForConnecting:options:) -> UISceneConfiguration anropas när din applikation förväntas tillhandahålla en ny scen, eller ett nytt fönster för iOS att visa. Observera att den här metoden inte anropas när din app startar initialt, den anropas endast för att erhålla och skapa nya scener. Vi kommer att titta närmare på hur man skapar och hanterar flera scener i ett senare blogginlägg.

Den sista metoden i AppDelegate-mallen är func application(_:didDiscardSceneSessions:). Den här metoden anropas när en användare kastar en scen, till exempel genom att svepa bort den i multitaskingfönstret eller om du gör det programmatiskt. Om din app inte körs när användaren gör detta kommer den här metoden att anropas för varje kasserad scen strax efter att func application(_:didFinishLaunchingWithOptions:) -> Bool anropats.

Förutom de här standardmetoderna kan din AppDelegate fortfarande användas för att öppna webbadresser, fånga upp minnesvarningar, upptäcka när appen kommer att avslutas, om enhetens klocka har ändrats väsentligt, upptäcka när en användare har registrerat sig för fjärranmälningar med mera.

Tips:
Det är viktigt att notera att om du för närvarande använder AppDelegate för att hantera appens statusfältets utseende kan du behöva göra vissa ändringar i iOS 13. Flera metoder relaterade till statusfältet har blivit föråldrade i iOS 13.

Nu när vi har en bättre bild av vad de nya ansvarsområdena för din AppDelegate är, ska vi ta en titt på den nya SceneDelegate.

SceneDelegates ansvar

När du betraktar AppDelegate som det objekt som ansvarar för din applikations livscykel är SceneDelegate ansvarig för det som visas på skärmen; scenerna eller fönstren. Innan vi fortsätter ska vi fastställa en viss scenrelaterad vokabulär eftersom inte alla termer betyder vad du tror att de betyder.

När du har att göra med scener kallas det som ser ut som ett fönster för användaren i själva verket för en UIScene som hanteras av en UISceneSession. Så när vi hänvisar till fönster hänvisar vi egentligen till UISceneSession-objekt. Jag kommer att försöka hålla mig till denna terminologi så mycket som möjligt under loppet av det här blogginlägget.

Nu när vi är på samma sida, låt oss titta på SceneDelegate.swift-filen som Xcode skapade när det skapade vårt projekt.

Det finns flera metoder i SceneDelegate.swift-filen som standard:

  • scene(_:willConnectTo:options:)
  • sceneDidDisconnect(_:)
  • sceneDidBecomeActive(_:)
  • sceneWillResignActive(_:)
  • sceneWillEnterForeground(_:)
  • sceneDidEnterBackground(_:)

De här metoderna borde kännas bekanta för dig om du är bekant med den AppDelegate som fanns före iOS 13. Låt oss ta en titt på scene(_:willConnectTo:options:) först, den här metoden ser förmodligen minst bekant ut för dig och det är den första metoden som anropas i livscykeln för en UISceneSession.

Standardimplementationen av scene(_:willConnectTo:options:) skapar din initiala innehållsvy (ContentView om du använder SwiftUI), skapar en ny UIWindow, ställer in fönstrets rootViewController och gör det här fönstret till nyckelfönster. Du kan tänka dig det här fönstret som det fönster som användaren ser. Detta är tyvärr inte fallet. Fönster har funnits sedan före iOS 13 och de representerar det visningsfönster som din app fungerar i. Så UISceneSession styr det synliga fönstret som användaren ser, UIWindow som du skapar är containervyn för din applikation.

Förutom att ställa in initiala vyer kan du använda scene(_:willConnectTo:options:) för att återställa scenens användargränssnitt om din scen har kopplats bort tidigare. Till exempel för att den har skickats till bakgrunden. Du kan också läsa connectionOptions-objektet för att se om din scen skapades på grund av en HandOff-förfrågan eller kanske för att öppna en URL. Jag kommer att visa dig hur du gör detta senare i det här blogginlägget.

När din scen har anslutit sig är nästa metod i din scens livscykel sceneWillEnterForeground(_:). Den här metoden anropas när din scen ska inta scenen. Detta kan vara när din app övergår från bakgrunden till förgrunden, eller om den precis blir aktiv för första gången. Därefter anropas sceneDidBecomeActive(_:). Detta är den punkt där din scen är inställd, synlig och redo att användas.

När din app övergår till bakgrunden anropas sceneWillResignActive(_:) och sceneDidEnterBackground(_:). Jag kommer inte att gå in på dessa metoder just nu eftersom deras syfte varierar för varje applikation och kommentarerna i Xcode-mallen gör ett ganska bra jobb med att förklara när dessa metoder anropas. Jag är faktiskt säker på att du själv kan räkna ut tidpunkten för när dessa metoder anropas.

En mer intressant metod är sceneDidDisconnect(_:). När din scen skickas till bakgrunden kan iOS bestämma sig för att koppla bort och rensa din scen för att frigöra resurser. Detta betyder inte att din app dödades eller inte körs längre, det betyder helt enkelt att scenen som överlämnats till den här metoden inte längre är aktiv och kommer att kopplas bort från sin session.

Bemärk att själva sessionen inte nödvändigtvis också kasseras, iOS kan bestämma sig för att återansluta en scen till en scensession när som helst, till exempel när en användare för en viss scen till förgrunden igen.

Den viktigaste saken att göra i sceneDidDisconnect(_:) är att kasserar alla resurser som du inte behöver ha kvar. Detta kan vara data som lätt laddas från disk eller nätverket eller andra data som du enkelt kan återskapa. Det är också viktigt att se till att du behåller alla data som inte lätt kan återskapas, som till exempel all input som användaren gav i en scen och som de förväntar sig ska finnas kvar när de återvänder till en scen.

Tänk på en textbearbetningsapplikation som har stöd för flera scener. Om en användare arbetar i en scen och sedan tar bakgrunden till den för att göra lite efterforskningar i Safari och byta musik i Spotify, skulle han/hon absolut förvänta sig att allt hans/hennes arbete fortfarande finns kvar i textbehandlingsappen, även om iOS kanske har kopplat bort textbehandlingsappens scen för en stund. För att uppnå detta måste appen behålla de nödvändiga uppgifterna, och den bör koda det aktuella apptillståndet i ett NSUserActivity-objekt som kan läsas senare i scene(_:willConnectTo:options:) när scenen åter ansluts.

Då det här arbetsflödet med att ansluta, koppla bort och återansluta scener kommer att skilja de bra apparna från de bra, ska vi ta en titt på hur du kan implementera tillståndsåterställning i din app.

Uppföra ytterligare scenkonfiguration

Det finns flera anledningar till att du måste utföra ytterligare konfiguration när en scen sätts upp. Du kan behöva öppna en URL, hantera en Handoff-förfrågan eller återställa tillståndet. I det här avsnittet kommer jag att fokusera mest på tillståndsåterställning eftersom det kanske är det mest komplexa scenariot du kan behöva hantera.

Tillståndsåterställning börjar när din scen kopplas bort och sceneDidDisconnect(_:) anropas. Vid denna tidpunkt är det viktigt att ditt program redan har ett tillstånd som kan återställas senare. Det bästa sättet att göra detta är att använda NSUserActivity i din applikation. Om du använder NSUserActivity för att stödja Handoff, Siri Shortcuts, Spotlight-indexering med mera har du inte mycket extra arbete att göra. Om du inte använder NSUserActivity ännu behöver du inte oroa dig. En enkel användaraktivitet kan se ut ungefär så här:

let activity = NSUserActivity(activityType: "com.donnywals.DocumentEdit")activity.userInfo = 

Bemärk att den här användaraktiviteten inte är strukturerad så som Apple rekommenderar den, det är ett mycket naket exempel som är tänkt att illustrera tillståndsåterställning. För en fullständig guide om NSUserActivity rekommenderar jag att du tar en titt på Apples dokumentation om detta ämne.

När det är dags för dig att tillhandahålla en användaraktivitet som kan återställas vid en senare tidpunkt anropar systemet stateRestorationActivity(for:)-metoden på din SceneDelegate. Observera att den här metoden inte är en del av standardmallen

func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { return scene.userActivity}

Gör du detta associeras den för närvarande aktiva användaraktiviteten för en scen med scensessionen. Kom ihåg att när en scen kopplas bort så slängs inte UISceneSession som äger UIScene för att sessionen ska kunna återanslutas till en scen. När detta sker anropas scene(_:willConnectTo:options:) igen. I den här metoden har du tillgång till UISceneSession som äger UIScene så att du kan läsa sessionens stateRestorationActivity och återställa programtillståndet vid behov:

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}

Självklart kommer de fina detaljerna i den här koden att variera beroende på din applikation, men den allmänna idén bör vara tydlig.

Om din UISceneSession förväntas hantera en URL kan du inspektera connectionOptions-objektets urlContexts för att hitta URL:er som din scen ska öppna och information om hur ditt program ska göra detta:

for urlContext in connectionOptions.urlContexts { let url = urlContext.url let options = urlContext.options // handle url and options as needed}

Objektet options kommer att innehålla information om huruvida din scen ska öppna URL:n på plats, vilket program som begärde att URL:n skulle öppnas och andra metadata om begäran.

De grundläggande principerna för tillståndsåterställning i iOS 13 med SceneDelegate är förvånansvärt okomplicerade, särskilt eftersom det bygger på NSUserActivity, vilket innebär att många program inte behöver göra alltför mycket arbete för att börja stödja tillståndsåterställning för sina scener.

Håll i åtanke att om du vill ha stöd för flera scener för din app på iPadOS är scenåterställning särskilt viktigt eftersom iOS kan koppla bort och koppla ihop dina scener igen när de växlar från förgrunden till bakgrunden och tillbaka igen. Särskilt om din applikation gör det möjligt för en användare att skapa eller manipulera objekt i en scen skulle en användare inte förvänta sig att deras arbete försvinner om de flyttar en scen till bakgrunden för ett ögonblick.

Sammanfattningsvis

I det här blogginlägget har du lärt dig mycket. Du har lärt dig vilka roller AppDelegate och SceneDelegate fyller i iOS 13 och hur deras livscykler ser ut. Du vet nu att AppDelegate ansvarar för att reagera på händelser på applikationsnivå, som till exempel applansering. SceneDelegate ansvarar för scenlivcykelrelaterade händelser. Till exempel skapande, förstörelse och återställande av scentillståndet för en UISceneSession. Med andra ord var huvudskälet till att Apple lade till UISceneDelegate i iOS 13 att skapa en bra ingång för tillämpningar med flera fönster.

Efter att ha lärt dig grunderna för UISceneDelegate såg du ett mycket enkelt exempel på hur tillståndsåterställning ser ut i iOS 13 med UISceneSession och UIScene. Naturligtvis finns det mycket mer att lära om hur din app beter sig när en användare spawnar flera UISceneSession för din app, och hur dessa scener kan behöva förbli synkroniserade eller dela data.

Om du vill lära dig mer om stöd för flera fönster för din iPad-app (eller din macOS-app) ska du se till att kolla in mitt inlägg Adding support for multiple windows to your iPadOS app. Tack för att du läste och tveka inte att höra av dig på Twitter om du har några frågor eller feedback till mig.

Håll dig uppdaterad med mitt veckobrev

Practical Combine

Lär dig allt du behöver veta om Combine och hur du kan använda det i dina projekt med min nya bok Practical Combine. Du får tretton kapitel, en Playground och en handfull exempelprojekt som hjälper dig att komma igång med Combine så snart som möjligt.

Boken finns som digital nedladdning för endast 29,99 kr!

Få Practical Combine

Lämna ett svar

Din e-postadress kommer inte publiceras.