logo hsb.horse
← Voltar para o índice do blog

Blog

A interface Shield no iOS precisa de duas extensions, não de uma

A tela Shield exibida pelo ManagedSettings é composta por dois targets separados: uma Action Extension e uma Configuration Extension. Este artigo organiza os NSExtensionPointIdentifier corretos e as principal classes.

Publicado:

Quando você define regras de bloqueio com ManagedSettings, o iOS mostra uma tela Shield ao acessar um site bloqueado. Existe um ponto em que quase todo mundo trava na primeira tentativa de personalizar essa tela: adicionar só uma extension não basta. São necessários dois targets.

Uma extension no iOS é um pequeno processo que roda separado do app principal. No Xcode, ela é criada adicionando um novo target. A Shield Extension é uma extension dedicada à tela Shield e roda de forma independente do app principal.

Shield Action Extension

Ela cuida do que acontece quando o usuário toca em um botão na tela Shield. A principal class, ou seja, a classe que funciona como ponto de entrada da extension, herda de ShieldActionDelegate.

import ManagedSettings
class ShieldActionExtension: ShieldActionDelegate {
override func handle(
action: ShieldAction,
for application: ApplicationToken,
completionHandler: @escaping (ShieldActionResponse) -> Void
) {
completionHandler(.close)
}
}

O Extension Point Identifier é com.apple.ManagedSettings.shield-action-service.

Aqui você importa ManagedSettings, não ManagedSettingsUI. Essa confusão acontece com facilidade, então vale registrar explicitamente.

Shield Configuration Extension

Ela personaliza o visual da tela Shield. Use quando quiser ajustar título, texto e rótulos dos botões ao contexto do app. A principal class herda de ShieldConfigurationDataSource.

import ManagedSettingsUI
import ManagedSettings
class ShieldConfigurationExtension: ShieldConfigurationDataSource {
override func configuration(
shielding application: Application
) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: .systemUltraThinMaterial,
title: ShieldConfiguration.Label(text: "集中モード中です", color: .label)
)
}
}

O Extension Point Identifier é com.apple.ManagedSettingsUI.shield-configuration-service. Esta extension importa ManagedSettingsUI.

Configuração do Info.plist

Defina o Extension Point Identifier correto no Info.plist de cada target.

No caso da Action Extension:

<key>NSExtensionPointIdentifier</key>
<string>com.apple.ManagedSettings.shield-action-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShieldActionExtension</string>

No caso da Configuration Extension:

<key>NSExtensionPointIdentifier</key>
<string>com.apple.ManagedSettingsUI.shield-configuration-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShieldConfigurationExtension</string>

Não esqueça da configuração de App Group

As duas extensions e o app principal precisam compartilhar o mesmo App Group. App Group é o mecanismo usado para compartilhar dados entre vários targets. No iOS, apps normalmente não conseguem trocar dados diretamente, mas targets no mesmo App Group conseguem ler e gravar no contêiner compartilhado.

No Xcode, adicione App Groups em Signing & Capabilities para cada target e configure o mesmo group ID. Isso precisa existir nos três targets: app principal, Action Extension e Configuration Extension. Para quem está começando no desenvolvimento iOS, é fácil achar que configurou tudo e ainda deixar um target de fora. Se faltar um único target, os dados não serão compartilhados no dispositivo real.

O que dá para fazer a partir da tela Shield

ShieldActionDelegate só pode retornar dois valores: .close e .defer. .close fecha a tela Shield e mantém o bloqueio. .defer é usado para disparar uma interação adicional do usuário.

Abrir o app principal diretamente a partir da tela Shield e mostrar um fluxo de desbloqueio não é algo que essa interface permita fazer de forma direta. Esse também é um ponto que costuma ser pouco intuitivo para quem está começando no desenvolvimento iOS. Separei esse padrão de implementação em outro artigo.