Um tutorial sobre extensões de aplicativo para iOS 8
Publicados: 2022-03-11Poucos haviam tentado antes (dê uma olhada nisso), mas foi a Apple com o primeiro iPhone que definiu como um Smartphone e um sistema operacional móvel deveriam ser. A Apple fez um avanço incrível em hardware e experiência do usuário. No entanto, muitas vezes esquecemos que eles também definem padrões de como um sistema operacional móvel deve funcionar e como um aplicativo para smartphone deve ser feito.
Construir paredes de concreto entre os aplicativos, tornando-os completamente isolados e inconscientes uns dos outros, foi o melhor método para mantê-los seguros e proteger seus dados. Todas as atividades foram monitoradas de perto pelo iOS, e havia apenas um punhado de ações que um aplicativo poderia ter feito fora de seu escopo.
“A abstinência é a melhor proteção!” - mas onde está a graça nisso?
Demorou um pouco; muito tempo se você me perguntar, mas com o iOS 8 a Apple decidiu se divertir. O iOS 8 introduziu um novo conceito chamado App Extensions. Esse novo recurso não quebrou as paredes entre os aplicativos, mas abriu algumas portas, proporcionando um contato suave e tangível entre alguns aplicativos. A atualização mais recente deu aos desenvolvedores do iOS uma opção para personalizar o ecossistema do iOS, e estamos ansiosos para ver esse caminho se abrindo também.
Quais são as extensões de aplicativo do iOS 8 e como elas funcionam?
Em termos simples, o iOS 8 App Extensions fornece um novo método de interação com seu aplicativo, sem iniciá-lo ou exibi-lo na tela.
Como esperado, a Apple garantiu que eles ficassem no topo de tudo, então há apenas um punhado de novos pontos de entrada que seu aplicativo pode fornecer:
- Hoje (também chamado de widget) - uma extensão exibida na visualização Hoje da Central de Notificações mostra informações breves e permite a execução de tarefas rápidas.
- Compartilhar - uma extensão que permite que seu aplicativo compartilhe conteúdo com usuários em redes sociais e outros serviços de compartilhamento.
- Ação - uma extensão que permite criar botões de ação personalizados na planilha Ação para permitir que os usuários visualizem ou transformem o conteúdo originário de um aplicativo host.
- Edição de fotos - uma extensão que permite aos usuários editar uma foto ou um vídeo no aplicativo Fotos.
- Provedor de Documentos - uma extensão usada para permitir que outros aplicativos acessem os documentos gerenciados pelo seu aplicativo.
- Teclado personalizado - uma extensão que substitui o teclado do sistema.
As extensões de aplicativo não são aplicativos independentes. Eles estão fornecendo uma funcionalidade estendida do aplicativo (que pode ser acessada de outros aplicativos, chamados de aplicativos host) que deve ser eficiente e focada em uma única tarefa. Eles têm seu próprio binário, sua própria assinatura de código e seu próprio conjunto de elementos, mas são entregues pela App Store como parte do binário do aplicativo que os contém. Um aplicativo (contendo) pode ter mais de uma extensão. Depois que o usuário instalar um aplicativo com extensões, elas estarão disponíveis no iOS.
Vejamos um exemplo: um usuário encontra uma imagem usando o Safari, aperta o botão de compartilhamento e escolhe a extensão do seu aplicativo para compartilhamento. O Safari “fala” com o framework iOS Social, que carrega e apresenta a extensão. O código da extensão é executado, passa dados usando os canais de comunicação instanciados do sistema e, uma vez que a tarefa é concluída - o Safari desmonta a visualização da extensão. Logo após isso, o sistema encerra o processo, e seu aplicativo nunca foi exibido na tela. No entanto, completou uma função de compartilhamento de fotos.
O iOS, usando a comunicação entre processos, é o responsável por garantir que o aplicativo host e uma extensão do aplicativo funcionem juntos. Os desenvolvedores usam APIs de alto nível fornecidas pelo ponto de extensão e pelo sistema, para que nunca precisem se preocupar com os mecanismos de comunicação subjacentes.
Ciclo da vida
As extensões de aplicativo têm um ciclo de vida diferente dos aplicativos iOS. O aplicativo host inicia o ciclo de vida da extensão como resposta à ação de um usuário. Em seguida, o sistema instancia a extensão do aplicativo e configura um canal de comunicação entre eles. A visualização da extensão é exibida no contexto do aplicativo host usando os itens recebidos na solicitação do aplicativo host. Assim que a visualização da extensão for exibida, o usuário poderá interagir com ela. Em resposta à ação do usuário, a extensão conclui a solicitação do aplicativo host executando/cancelando imediatamente a tarefa ou, se necessário, iniciando um processo em segundo plano para realizá-la. Logo em seguida, o aplicativo host desmonta a visualização da extensão e o usuário retorna ao contexto anterior no aplicativo host. Os resultados da execução desse processo podem ser retornados ao aplicativo host assim que o processo for concluído. A extensão geralmente é encerrada logo após concluir a solicitação recebida do aplicativo host (ou iniciar um processo em segundo plano para realizá-la).
O sistema abre a extensão da ação de um usuário do aplicativo host, a extensão exibe a interface do usuário, executa algum trabalho e retorna dados ao aplicativo host (se for apropriado para o tipo de extensão). O aplicativo que o contém nem está em execução enquanto sua extensão está em execução.
Criando uma extensão de aplicativo - Exemplo prático usando a extensão Today
As extensões Hoje, também chamadas de widgets , estão localizadas na visualização Hoje da Central de Notificações. Eles são uma ótima maneira de apresentar um conteúdo atualizado para o usuário (como mostrar as condições climáticas) ou realizar tarefas rápidas (como marcar as coisas feitas no widget de um aplicativo de lista de tarefas). Devo salientar aqui que a entrada do teclado não é suportada .
Vamos criar uma extensão Today que exibirá as informações mais atualizadas do nosso aplicativo (código no GitHub). Para executar este código, certifique-se de ter (re)configurado o Grupo de Aplicativos para o projeto (selecione sua Equipe de Desenvolvimento, lembre-se de que o nome do Grupo de Aplicativos deve ser exclusivo e siga as instruções do Xcode).
Criando um novo widget
Como dissemos antes, as extensões de aplicativo não são aplicativos independentes. Precisamos de um aplicativo de contenção no qual construiremos a extensão do aplicativo. Assim que tivermos nosso aplicativo que o contém, optamos por adicionar um novo destino navegando até Arquivo -> Novo -> Destino no Xcode. A partir daqui, escolhemos o modelo para o nosso novo destino para adicionar uma Extensão Hoje.
Na próxima etapa, podemos escolher nosso nome do produto. Esse é o nome que aparecerá na visualização Hoje da Central de Notificações. Há uma opção para escolher o idioma entre Swift e Objective-C nesta etapa também. Ao concluir essas etapas, o Xcode cria um modelo Today, que fornece arquivos de cabeçalho e implementação padrão para a classe principal (chamada TodayViewController
) com o arquivo Info.plist
e um arquivo de interface (um storyboard ou arquivo .xib). O arquivo Info.plist
, por padrão, tem esta aparência:
<key>NSExtension</key> <dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.widget-extension</string> </dict>
Se você não quiser usar o storyboard fornecido pelo modelo, remova a chave NSExtensionMainStoryboard
e adicione a chave NSExtensionPrincipalClass
com o nome do seu controlador de exibição como um valor.
Um widget Hoje deve:
- garantir que o conteúdo esteja sempre atualizado
- responder adequadamente às interações do usuário
- têm um bom desempenho (os widgets do iOS devem usar a memória com sabedoria ou serão encerrados pelo sistema)
Compartilhando dados e um contêiner compartilhado
A extensão do aplicativo e o aplicativo que o contém têm acesso aos dados compartilhados em seu contêiner compartilhado definido de forma privada - que é uma forma de comunicação indireta entre o aplicativo que o contém e a extensão.
Você não adora como a Apple torna essas coisas tão “simples”? :)
O compartilhamento de dados por meio de NSUserDefaults
é simples e um caso de uso comum. Por padrão, a extensão e seu aplicativo que a contém usam conjuntos de dados NSUserDefaults
separados e não podem acessar os contêineres uns dos outros. Para mudar esse comportamento, o iOS introduziu os Grupos de Aplicativos . Depois de habilitar grupos de aplicativos no aplicativo que o contém e na extensão, em vez de usar [NSUserDefaults standardUserDefaults]
use [[NSUserDefaults alloc] initWithSuiteName:@"group.yourAppGroupName"]
para acessar o mesmo contêiner compartilhado.
Atualizando o widget
Para garantir que o conteúdo esteja sempre atualizado, a extensão Today fornece uma API para gerenciar o estado de um widget e lidar com atualizações de conteúdo. O sistema ocasionalmente captura instantâneos da visualização do widget, portanto, quando o widget se torna visível, o instantâneo mais recente é exibido até que seja substituído por uma versão ao vivo da visualização. Uma conformidade com o protocolo NCWidgetProviding
é importante para atualizar o estado de um widget antes que um instantâneo seja obtido. Uma vez que o widget recebe a chamada widgetPerformUpdateWithCompletionHandler:
a view do widget deve ser atualizada com o conteúdo mais recente e o handler de conclusão deve ser chamado com uma das seguintes constantes para descrever o resultado da atualização:

-
NCUpdateResultNewData
- O novo conteúdo requer o redesenho da visão -
NCUpdateResultNoDate
- O widget não requer atualização -
NCUpdateResultFailed
- Ocorreu um erro durante o processo de atualização
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData [self updateTableView]; completionHandler(NCUpdateResultNewData); }
Controlando quando o widget é visível
Para controlar quando um widget é exibido, use o setHasContent:forWidgetWithBundleIdentifier:
da classe NCWidgetController
. Este método permitirá que você especifique o estado do conteúdo do widget. Ele pode ser chamado do widget ou do aplicativo que o contém (se estiver ativo). Você pode passar um sinalizador NO
ou YES
para este método, definindo se o conteúdo do widget está pronto ou não. Se o conteúdo não estiver pronto, o iOS não exibirá seu widget quando a visualização Hoje for aberta.
NCWidgetController *widgetController = [[NCWidgetController alloc] init]; [widgetController setHasContent:YES forWidgetWithBundleIdentifier:@"com.your-company.your-app.your-widget"];
Abrindo o aplicativo que contém a partir do widget
O widget Today é a única extensão que pode solicitar a abertura do aplicativo que o contém chamando o método openURL:completionHandler:
Para garantir que o aplicativo que o contém abra de uma maneira que faça sentido no contexto da tarefa atual do usuário, um esquema de URL personalizado (que o widget e o aplicativo que o contém podem usar) deve ser definido.
[self.extensionContext openURL:[NSURL URLWithString:@"customURLsheme://URLpath"] completionHandler:nil];
Considerações sobre a interface do usuário
Ao projetar seu widget, aproveite a classe UIVisualEffectView
, tendo em mente que as visualizações que devem ser desfocadas/vibrantes devem ser adicionadas ao contentView
e não ao UIVisualEffectView
diretamente. Widgets (em conformidade com o protocolo NCWidgetProviding
) devem carregar estados em cache em viewWillAppear:
para corresponder ao estado da visualização da última viewWillDisappear:
e então fazer uma transição suave para os novos dados quando eles chegam, o que não é um caso com uma visualização normal controller (UI é configurada em viewDidLoad
e lida com animações e carregamento de dados em viewWillAppear
). Os widgets devem ser projetados para executar uma tarefa ou abrir o aplicativo que os contém com um único toque. A entrada do teclado não está disponível em um widget. Isso significa que qualquer IU que exija entrada de texto não deve ser usada.
A adição de pergaminhos em um widget, tanto vertical quanto horizontal, não é possível. Ou, mais precisamente, é possível adicionar uma visualização de rolagem, mas a rolagem não funcionará. O gesto de rolagem horizontal em uma visualização de rolagem na extensão Hoje será interceptado pelo centro de notificação, o que causará a rolagem de Hoje para o centro de notificação. A rolagem vertical de uma visualização de rolagem dentro de uma extensão Hoje será interrompida pela rolagem da Visualização de hoje.
Notas Técnicas
Aqui, vou apontar algumas coisas importantes a serem lembradas ao criar uma extensão de aplicativo.
Recursos comuns a todas as extensões
Os seguintes itens são verdadeiros para todas as extensões:
O objeto sharedApplication está fora dos limites : as extensões de aplicativo não podem acessar um objeto sharedApplication ou usar qualquer um dos métodos relacionados a esse objeto.
A câmera e o microfone estão fora dos limites : as extensões de aplicativo não podem acessar a câmera ou o microfone no dispositivo (mas esse não é um caso para todos os elementos de hardware). Isso é resultado da indisponibilidade de algumas APIs. Para acessar alguns elementos de hardware na extensão de aplicativo, você terá que verificar se sua API está disponível para extensões de aplicativo ou não (com a verificação de disponibilidade da API descrita acima).
A maioria das tarefas em segundo plano está fora dos limites : as extensões de aplicativo não podem executar tarefas em segundo plano de longa duração, exceto iniciar uploads ou downloads, o que é discutido abaixo.
O AirDrop está fora dos limites : as extensões de aplicativo não podem receber (mas podem enviar) dados usando o AirDrop.
Upload/Download em segundo plano
A única tarefa que pode ser executada em segundo plano é o upload/download, usando o NSURLSession object
.
Depois que a tarefa de upload/download é iniciada, a extensão pode concluir a solicitação do aplicativo host e ser encerrada sem qualquer efeito no resultado da tarefa. Se a extensão não estiver em execução no momento em que a tarefa em segundo plano for concluída, o sistema iniciará o aplicativo que o contém em segundo plano e o método de delegação application:handleEventsForBackgroundURLSession:completionHandler:
do aplicativo será chamado.
O aplicativo cuja extensão inicia uma tarefa NSURLSession
em segundo plano deve ter um contêiner compartilhado configurado que o aplicativo que o contém e sua extensão possam acessar.
Certifique-se de criar diferentes sessões em segundo plano para o aplicativo que o contém e cada uma de suas extensões de aplicativo (cada sessão em segundo plano deve ter um identificador exclusivo). Isso é importante porque apenas um processo pode usar uma sessão em segundo plano por vez.
Ação x Compartilhamento
As diferenças entre as extensões Action e Share não são totalmente claras do ponto de vista de um codificador, porque na prática elas são muito semelhantes. O modelo do Xcode para o destino da extensão de compartilhamento usa SLComposeServiceViewController
, que fornece uma interface do usuário de visualização de composição padrão que você pode usar para compartilhamento social, mas não é obrigatório. Uma extensão de compartilhamento também pode herdar diretamente de UIViewController para um design totalmente personalizado, da mesma forma que uma extensão de ação pode herdar de SLComposeServiceViewController
.
As diferenças entre esses dois tipos de extensões estão em como elas devem ser usadas. Com a extensão Action, você pode criar uma extensão sem IU própria (por exemplo, uma extensão usada para traduzir o texto selecionado e retornar a tradução para o aplicativo host). A extensão de compartilhamento permite compartilhar comentários, fotos, vídeos, áudio, links e muito mais diretamente do aplicativo host. O UIActivityViewController
aciona as extensões Action e Share, onde as extensões Share são apresentadas como ícones coloridos na linha superior e as extensões de ação são apresentadas como ícones monocromáticos na linha inferior (Imagem 2.1).
APIs proibidas
As APIs marcadas nos arquivos de cabeçalho com a macro NS_EXTENSION_UNAVAILABLE
ou macro semelhante para indisponibilidade não podem ser usadas (por exemplo: as estruturas de interface do usuário HealthKit e EventKit no iOS 8 não estão disponíveis para uso em nenhuma extensão de aplicativo).
Se você estiver compartilhando código entre um aplicativo e uma extensão, lembre-se de que mesmo fazer referência a uma API que não é permitida para a extensão do aplicativo levará à rejeição do seu aplicativo da App Store. Você pode optar por lidar com isso refatorando as classes compartilhadas em hierarquias, com um pai comum e diferentes subclasses para diferentes destinos. Outra maneira é usar o pré-processador por meio de verificações #ifdef
. Como ainda não há condicional de destino integrado, você precisa criar o seu próprio.
Outra boa maneira de fazer isso é criando sua própria estrutura incorporada. Apenas certifique-se de que ele não contenha APIs indisponíveis para extensões. Para configurar uma extensão de aplicativo para usar uma estrutura incorporada, navegue até as configurações de compilação do destino e defina a configuração "Exigir apenas API segura para extensão de aplicativo" como Sim. Ao configurar o projeto Xcode, na fase de construção Copiar Arquivos, “Frameworks” deve ser escolhido como destino para o framework embarcado. Se você escolher o destino “SharedFrameworks”, seu envio será rejeitado pela App Store.
Uma nota sobre a retrocompatibilidade
Embora as extensões de aplicativo estejam disponíveis apenas desde o iOS 8, você pode disponibilizar seu aplicativo contido nas versões anteriores do iOS.
Conformidade da interface humana da Apple
Lembre-se das Diretrizes de interface humana do iOS da Apple ao projetar uma extensão de aplicativo. Você deve garantir que sua extensão de aplicativo seja universal, independentemente do dispositivo compatível com seu aplicativo. Para garantir que a extensão do aplicativo seja universal, use a configuração de compilação da família de dispositivos de destino no Xcode especificando o valor "iPhone/iPad" (às vezes chamado de universal).
Conclusão
As extensões de aplicativo definitivamente têm o impacto mais visível no iOS 8. Como 79% dos dispositivos já estão usando o iOS 8 (conforme medido pela App Store em 13 de abril de 2015), as extensões de aplicativo são recursos incríveis que os aplicativos devem aproveitar. Ao combinar as restrições da API e a forma de compartilhamento de dados entre as extensões e o app que as contém, parece que a Apple conseguiu resolver uma das maiores reclamações sobre a plataforma sem comprometer seu modelo de segurança. Ainda não há como os aplicativos de terceiros compartilharem diretamente seus dados entre si. Embora este seja um conceito muito novo, parece muito promissor.