Quand on écrit la logique des boutons dans ShieldActionDelegate, on finit par vouloir ouvrir l’app principale depuis cet endroit pour afficher un écran de déverrouillage. Mais ce flux ne peut pas être écrit de manière directe. Quand on débute en iOS, c’est une zone où il est difficile de comprendre pourquoi ce n’est pas possible, donc je repars du contexte.
Pourquoi une Extension ne peut pas ouvrir directement l’app
ShieldActionDelegate ne peut renvoyer que deux valeurs, .close et .defer. Sur d’autres plateformes, on utiliserait ici un schéma d’URL pour lancer l’app, mais dans le contexte du shield, ce chemin ne fonctionne pas.
Apple ferme probablement cette possibilité pour préserver la cohérence de l’expérience du shield. Si le shield devient une échappatoire, le blocage ne veut plus rien dire. Autoriser un saut direct depuis l’extension vers l’app principale permettrait, selon l’implémentation, de contourner la restriction depuis l’intérieur même de la restriction. C’est d’ailleurs vrai pour les extensions iOS en général : les opérations directes vers l’app principale sont fortement limitées.
Le modèle de handoff via App Group
L’implémentation réaliste passe par un handoff asynchrone via le stockage partagé.
App Group est le mécanisme qui permet à l’app principale et à une extension de partager des données. Normalement, les processus iOS n’ont pas accès aux données les uns des autres, mais s’ils appartiennent au même App Group, ils peuvent lire et écrire dans des UserDefaults partagés ou un conteneur de fichiers commun.
Dans la Shield Action Extension, on écrit un indicateur dans le conteneur partagé App Group pour signaler qu’une demande de déverrouillage est en attente. Quand l’utilisateur ouvre ensuite l’app manuellement, l’app principale lit cet indicateur et démarre le flux de déverrouillage.
// Shield Action Extension 内override func handle( action: ShieldAction, for webDomain: WebDomainToken, completionHandler: @escaping (ShieldActionResponse) -> Void) { let defaults = UserDefaults(suiteName: "group.com.example.stoicdns") defaults?.set(true, forKey: "pendingUnlockRequest") defaults?.set(Date(), forKey: "unlockRequestedAt") completionHandler(.close)}// メインアプリの起動時 / フォアグラウンド復帰時func checkPendingUnlock() { let defaults = UserDefaults(suiteName: "group.com.example.stoicdns") guard defaults?.bool(forKey: "pendingUnlockRequest") == true else { return } defaults?.removeObject(forKey: "pendingUnlockRequest") showUnlockFlow()}Le shield se ferme avec .close. L’utilisateur doit revenir à l’écran d’accueil puis ouvrir l’app manuellement.
Pourquoi stocker aussi un horodatage
Si la date est enregistrée en même temps que la demande, il devient plus simple d’ignorer les anciennes requêtes. Une règle du type “toute demande de plus de 30 minutes est invalide” évite l’apparition inattendue de l’écran de déverrouillage bien après que l’utilisateur a oublié cette action.
L’accepter comme choix d’UX
Si on s’attend à un flux “un tap depuis le shield vers l’écran de déverrouillage”, cette approche peut sembler insuffisante. Mais c’est la limite autorisée par l’interface d’Apple.
On peut aussi voir les choses autrement : le fait de remarquer le shield puis d’ouvrir volontairement l’app fait partie du self-control. Ajouter une étape de pause peut mieux correspondre au but du produit qu’une sortie en un seul tap.
Du point de vue de l’implémentation, le plus rapide est d’accepter cette contrainte et d’avancer.
hsb.horse