Entendendo o Delegado da Cena iOS 13

Publicado por donnywals em 28 de outubro de 201928 de outubro de 2019

Quando você cria um novo projeto no Xcode 11, você pode notar algo que você nunca viu antes. Em vez de criar apenas um ficheiro AppDelegate.swift, um ficheiro ViewController.swift, um storyboard e alguns outros ficheiros, o Xcode cria agora um novo ficheiro para si; o ficheiro SceneDelegate.swift. Se você nunca viu este arquivo antes, pode ser bastante confuso entender o que é, e como você deve usar este novo delegado de cena no seu app.

Até o final do post do blog desta semana você saberá:

  • Para que o delegado de cena é usado.
  • Como você pode implementar efetivamente o seu delegado de cena.
  • Por que o delegado de cena é uma parte importante do iOS 13.

Pulemos logo para dentro, vamos?

Examinando o novo modelo de projeto Xcode

Quando você criar um novo projeto Xcode, você tem a opção de escolher se quer usar SwiftUI ou Storyboards. Independentemente da sua escolha aqui, o Xcode irá gerar um novo tipo de template de projeto para você construir. Vamos dar uma olhada nos arquivos SceneDelegate.swift e AppDelegate.swift na próxima seção, o importante por enquanto é que você entenda que o Xcode criou esses arquivos para você.

Além desses dois arquivos delegados, o Xcode faz algo um pouco mais sutil. Dê uma olhada de perto no seu arquivo Info.plist. Você deve ver uma nova chave chamada Application Scene Manifest com conteúdo semelhante à seguinte imagem:

Screenshot do manifesto de cena do arquivo Info.plist

Screenshot do manifesto de cena do arquivo Info.plist

Este manifesto de cena especifica um nome e uma classe de delegado para sua cena. Note que estas propriedades pertencem a um array (Application Session Role), sugerindo que você pode ter múltiplas configurações em seu arquivo Info.plist. Uma chave muito mais importante que você já deve ter visto na captura de tela acima é Enable Multiple Windows. Esta propriedade é definida como NO por padrão. Definindo esta propriedade como YES, os usuários poderão abrir várias janelas da sua aplicação no iPadOS (ou mesmo no MacOS). Poder executar múltiplas janelas de uma aplicação iOS lado a lado é uma enorme diferença do ambiente de janela única com que trabalhamos até agora, e a capacidade de ter múltiplas janelas é a razão pela qual o ciclo de vida da nossa aplicação é agora mantido em dois locais em vez de um.

Vamos dar uma olhada nos AppDelegate e SceneDelegate para entender melhor como esses dois delegados trabalham juntos para habilitar o suporte a múltiplas janelas.

Entendendo as funções do AppDelegate e SceneDelegate

Se você construiu aplicações antes do iOS 13, você provavelmente conhece o seu AppDelegate como o único lugar que faz praticamente tudo relacionado ao lançamento da sua aplicação, foregrounding, backgrounding e depois alguns. No iOS 13, a Apple mudou algumas das AppDelegate responsabilidades para a SceneDelegate. Vamos dar uma breve olhada em cada um desses dois arquivos.

AppDelegate’s responsibilities

The AppDelegate ainda é o principal ponto de entrada para uma aplicação no iOS 13. A Apple chama AppDelegate métodos para vários eventos do ciclo de vida da aplicação em nível de aplicação. No template padrão da Apple você encontrará três métodos que a Apple considera importantes para você usar:

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

Estes métodos têm alguns comentários que realmente descrevem o que eles fazem com detalhes suficientes para entender o que eles fazem. Mas vamos revisá-los rapidamente de qualquer maneira.

Quando sua aplicação acaba de ser lançada, func application(_:didFinishLaunchingWithOptions:) -> Bool é chamada. Este método é usado para executar a configuração da aplicação. No iOS 12 ou anterior, você pode ter usado este método para criar e configurar um objeto UIWindow e atribuído uma instância UIViewController à janela para fazê-lo aparecer.

Se sua aplicação está usando cenas, seu AppDelegate não é mais responsável por fazer isso. Como seu aplicativo agora pode ter várias janelas, ou UISceneSessions ativas, não faz muito sentido gerenciar um objeto de janela única no objeto AppDelegate.

O func application(_:configurationForConnecting:options:) -> UISceneConfiguration é chamado sempre que se espera que seu aplicativo forneça uma nova cena, ou janela para o iOS exibir. Note que este método não é chamado quando o seu aplicativo é iniciado inicialmente, ele é chamado apenas para obter e criar novas cenas. Vamos dar uma olhada mais profunda na criação e gerenciamento de múltiplas cenas em um post de blog posterior.

O último método no template AppDelegate é func application(_:didDiscardSceneSessions:). Este método é chamado sempre que um usuário descarta uma cena, por exemplo, deslizando-a na janela multitarefa ou se você o fizer de forma programática. Se o seu aplicativo não estiver sendo executado quando o usuário fizer isso, esse método será chamado para cada cena descartada logo após func application(_:didFinishLaunchingWithOptions:) -> Bool ser chamado.

Além desses métodos padrão, seu AppDelegate ainda pode ser usado para abrir URLs, capturar avisos de memória, detectar quando seu aplicativo terminará, se o relógio do dispositivo mudou significativamente, detectar quando um usuário se registrou para notificações remotas e muito mais.

Dica:
É importante notar que se você está usando atualmente AppDelegate para gerenciar a aparência da barra de status da sua aplicação, você pode ter que fazer algumas alterações no iOS 13. Vários métodos relacionados com a barra de status foram depreciados no iOS 13.

Agora temos uma imagem melhor de quais são as novas responsabilidades do seu AppDelegate, vamos dar uma olhada no novo SceneDelegate.

As responsabilidades do Delegado de cena

Quando você considera o AppDelegate como o objeto responsável pelo ciclo de vida da sua aplicação, o SceneDelegate é responsável pelo que é mostrado na tela; as cenas ou janelas. Antes de continuarmos, vamos estabelecer algum vocabulário relacionado a cenas porque nem todos os termos significam o que você pode pensar que significam.

Quando você está lidando com cenas, o que parece uma janela para o seu usuário é na verdade chamado de UIScene que é gerenciado por um UISceneSession. Então quando nos referimos a janelas, estamos realmente nos referindo a UISceneSession objetos. Vou tentar manter esta terminologia o máximo possível ao longo deste post.

Agora estamos na mesma página, vamos olhar para o arquivo SceneDelegate.swift que o Xcode criou quando criou nosso projeto.

Existem vários métodos no arquivo SceneDelegate.swift por padrão:

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

Estes métodos devem parecer muito familiares se você estiver familiarizado com os AppDelegate que existiam antes do iOS 13. Vamos dar uma olhada em scene(_:willConnectTo:options:) primeiro, este método provavelmente parece menos familiar para você e é o primeiro método chamado no ciclo de vida de um UISceneSession.

A implementação padrão de scene(_:willConnectTo:options:) cria sua visualização inicial de conteúdo (ContentView se você estiver usando SwiftUI), cria um novo UIWindow, define a janela de rootViewController e faz desta janela a janela chave. Você pode pensar nesta janela como a janela que o seu usuário vê. Este, infelizmente, não é o caso. As janelas já existem desde antes do iOS 13 e representam a viewport na qual o seu aplicativo opera. Então, o UISceneSession controla a janela visível que o usuário vê, o UIWindow que você cria é a vista de container para sua aplicação.

Além de configurar as vistas iniciais, você pode usar o scene(_:willConnectTo:options:) para restaurar sua IU de cena caso sua cena tenha se desconectado no passado. Por exemplo, porque ela foi enviada para o fundo. Você também pode ler o objeto connectionOptions para ver se a sua cena foi criada devido a um pedido de HandOff ou talvez para abrir uma URL. Mostrarei como fazer isso mais tarde neste post do blog.

Após a sua cena ter se conectado, o próximo método no ciclo de vida da sua cena é sceneWillEnterForeground(_:). Este método é chamado quando a sua cena subirá ao palco. Isto pode ser quando sua aplicação transitar do fundo para o primeiro plano, ou se ela estiver apenas se tornando ativa pela primeira vez. A seguir, sceneDidBecomeActive(_:) é chamado. Este é o ponto onde sua cena é configurada, visível e pronta para ser usada.

Quando sua aplicação vai para o fundo, sceneWillResignActive(_:) e sceneDidEnterBackground(_:) são chamados. Eu não vou entrar nesses métodos agora, já que seu propósito varia para cada aplicação, e os comentários no template do Xcode fazem um bom trabalho explicando quando esses métodos são chamados. Na verdade, tenho certeza que você pode descobrir o tempo de quando esses métodos são chamados por você mesmo.

Um método mais interessante é sceneDidDisconnect(_:). Sempre que sua cena é enviada para o fundo, o iOS pode decidir desconectar e limpar sua cena para liberar recursos. Isto não significa que seu aplicativo foi morto ou não está mais rodando, simplesmente significa que a cena passada para este método não está mais ativa e irá desconectar de sua sessão.

Note que a sessão em si não é necessariamente descartada também, o iOS pode decidir reconectar uma cena a uma sessão de cena a qualquer momento, por exemplo, quando um usuário traz uma cena em particular para o primeiro plano novamente.

A coisa mais importante a fazer em sceneDidDisconnect(_:) é descartar quaisquer recursos que você não precisa manter por perto. Isto pode ser dados que são facilmente carregados do disco ou da rede ou outros dados que você pode recriar facilmente. Também é importante garantir que você retenha quaisquer dados que não possam ser facilmente recriados, como por exemplo qualquer entrada que o usuário forneceu em uma cena que ele esperaria ainda estar lá quando retornasse a uma cena.

Considerar um aplicativo de processamento de texto que suporte múltiplas cenas. Se um usuário está trabalhando em uma cena, então faz uma pesquisa no Safari e muda sua música no Spotify, ele espera absolutamente que todo o seu trabalho ainda exista no aplicativo de processamento de texto, mesmo que o iOS tenha desconectado a cena do aplicativo de processamento de texto por algum tempo. Para conseguir isso, o aplicativo deve reter os dados necessários, e deve codificar o estado atual do aplicativo em um objeto NSUserActivity que pode ser lido mais tarde em scene(_:willConnectTo:options:) quando a cena for reconectada.

Desde que este fluxo de trabalho de conectar, desconectar e reconectar cenas vai separar os bons aplicativos do ótimo, vamos dar uma olhada em como você pode implementar a restauração do estado em seu aplicativo.

Executar configuração de cenas adicionais

Existem várias razões para você ter que executar configuração adicional quando uma cena é configurada. Você pode ter que abrir uma URL, lidar com um pedido de Handoff ou restaurar o estado. Nesta seção, eu vou focar principalmente na restauração do estado, já que esse é possivelmente o cenário mais complexo que você pode ter que lidar.

State restoration starts when your scene gets disconnected and sceneDidDisconnect(_:) is called. Neste ponto, é importante que a sua aplicação já tenha um estado configurado que possa ser restaurado mais tarde. A melhor maneira de fazer isso é usar NSUserActivity na sua aplicação. Se você está usando NSUserActivity para suportar Handoff, Siri Shortcuts, Spotlight indexing e muito mais, você não tem muito trabalho extra a fazer. Se você ainda não usa NSUserActivity, não se preocupe. Uma simples atividade do usuário pode parecer um pouco como a seguinte:

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

Note que esta atividade do usuário não está estruturada como a Apple a recomenda, é um exemplo muito nu destinado a ilustrar a restauração do estado. Para um guia completo sobre NSUserActivity, eu recomendo que você dê uma olhada na documentação da Apple sobre este tópico.

Quando chegar a hora de fornecer uma atividade do usuário que possa ser restaurada posteriormente, o sistema chama o método stateRestorationActivity(for:) no seu SceneDelegate. Note que este método não faz parte do modelo padrão

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

Fazendo isto associa a atividade do usuário atualmente ativa para uma cena com a sessão da cena. Lembre-se que sempre que uma cena é desconectada, o UISceneSession que possui o UIScene não é descartado para permitir que a sessão se reconecte a uma cena. Quando isto acontece, o scene(_:willConnectTo:options:) é chamado novamente. Neste método, você tem acesso ao UISceneSession que possui o UIScene para que você possa ler o stateRestorationActivity da sessão e restaurar o estado da aplicação conforme necessário:

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}

Obviamente, os detalhes finos deste código irão variar com base na sua aplicação, mas a idéia geral deve ser clara.

Se for esperado que o seu UISceneSession trate de uma URL, você pode inspecionar o objeto connectionOptions para encontrar URLs que sua cena deve abrir e informações sobre como sua aplicação deve fazer isso:

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

O objeto options conterá informações sobre se sua cena deve abrir a URL no local, qual aplicação solicitou que essa URL fosse aberta e outros metadados sobre a solicitação.

Os princípios básicos da restauração de estado no iOS 13 com o SceneDelegate são surpreendentemente simples, especialmente porque é construído sobre NSUserActivity o que significa que muitos aplicativos não terão que fazer muito trabalho para começar a suportar restauração de estado para suas cenas.

Cuidado que se você quiser ter suporte para múltiplas cenas para seu aplicativo no iPadOS, a restauração de cenas é especialmente importante uma vez que o iOS pode desconectar e reconectar suas cenas quando elas mudarem do primeiro plano para o plano de fundo e para trás novamente. Especialmente se seu aplicativo permite que um usuário crie ou manipule objetos em uma cena, um usuário não esperaria que seu trabalho desaparecesse se ele movesse uma cena para o fundo por um momento.

Em resumo

Neste post de blog, você aprendeu muito. Você aprendeu quais papéis os AppDelegate e SceneDelegate cumprem no iOS 13 e como são os seus ciclos de vida. Você agora sabe que o AppDelegate é responsável por reagir a eventos em nível de aplicação, como o lançamento de aplicações, por exemplo. O SceneDelegate é responsável por eventos relacionados ao ciclo de vida da cena. Por exemplo, a criação, destruição e restauração do estado de uma cena UISceneSession. Em outras palavras, a principal razão para a Apple adicionar UISceneDelegate ao iOS 13 foi para criar um bom ponto de entrada para aplicações multi-window.

Após aprender sobre o básico de UISceneDelegate, você viu um exemplo muito simples de como é a restauração de estados no iOS 13 com UISceneSession e UIScene. Claro, há muito mais a aprender sobre como seu aplicativo se comporta quando um usuário cria múltiplos UISceneSessions para seu aplicativo, e como essas cenas podem ter que permanecer em sincronia ou compartilhar dados.

Se você quiser aprender mais sobre o suporte a múltiplas janelas para seu aplicativo iPad (ou seu aplicativo MacOS), não deixe de conferir meu post Adicionando suporte a múltiplas janelas ao seu aplicativo iPadOS. Obrigado por ler, e não hesite em entrar em contato no Twitter se você tiver alguma dúvida ou feedback para mim.

Estar atualizado com a minha newsletter semanal

Combinação prática

Aprenda tudo que você precisa saber sobre Combine e como você pode usá-lo em seus projetos com o meu novo livro Combinação prática. Você terá treze capítulos, um Playground e um punhado de exemplos de projetos para ajudá-lo a começar a trabalhar com Combine o mais rápido possível.

O livro está disponível para download digital por apenas $29,99!

Obtenha Combine Prático

Deixe uma resposta

O seu endereço de email não será publicado.