Un tutoriel sur les extensions d’applications iOS 8

Peu de gens avaient essayé avant (regardez ça), mais c’est Apple avec le premier iPhone qui a défini à quoi devait ressembler un Smartphone et un OS mobile. Apple a fait une percée incroyable en matière de matériel et d’expérience utilisateur. Cependant, nous oublions souvent qu’ils ont également établi des normes sur la façon dont un OS mobile devrait fonctionner, et comment les applications d’un Smartphone devraient être faites.

Construire des murs de béton entre les applications, les rendant complètement isolées et ignorantes les unes des autres, était la meilleure méthode pour les garder en sécurité et protéger leurs données. Toutes les activités étaient étroitement surveillées par iOS, et il n’y avait qu’une poignée d’actions qu’une application aurait pu faire en dehors de son champ d’action.

« L’abstinence est la meilleure protection ! » – mais où est le plaisir dans tout cela ?

Ils ont mis du temps ; trop longtemps si vous voulez mon avis, mais avec iOS 8, Apple a décidé de s’amuser un peu. iOS 8 a introduit un nouveau concept appelé App Extensions. Cette nouvelle fonctionnalité n’a pas fait tomber les murs entre les applications, mais elle a ouvert quelques portes permettant un contact doux mais tangible entre certaines applications. La dernière mise à jour a donné aux développeurs iOS une option pour personnaliser l’écosystème iOS, et nous sommes impatients de voir cette voie s’ouvrir également.

Qu’est-ce que les extensions d’applications iOS 8 et comment fonctionnent-elles ?

En termes simples, les extensions d’applications iOS 8 fournissent une nouvelle méthode d’interaction avec votre application, sans la démarrer ou l’afficher à l’écran.

Comme prévu, Apple s’est assuré de rester à la pointe de tout, il n’y a donc qu’une poignée de nouveaux points d’entrée que votre application peut fournir :

  • Aujourd’hui (également appelé widget) – une extension affichée dans la vue Aujourd’hui du centre de notifications montre de brèves informations et permet l’exécution de tâches rapides.
  • Partager – une extension qui permet à votre application de partager du contenu avec les utilisateurs sur les réseaux sociaux et autres services de partage.
  • Action – une extension qui permet de créer des boutons d’action personnalisés dans la feuille d’action pour permettre aux utilisateurs de visualiser ou de transformer le contenu provenant d’une application hôte.
  • Édition de photos – une extension qui permet aux utilisateurs de modifier une photo ou une vidéo dans l’app Photos.
  • Fournisseur de documents – une extension utilisée pour permettre à d’autres apps d’accéder aux documents gérés par votre app.
  • Clavier personnalisé – une extension qui remplace le clavier système.

Les extensions d’apps ne sont pas des apps autonomes. Elles fournissent une fonctionnalité étendue de l’app (à laquelle on peut accéder à partir d’autres apps, appelées apps hôtes) qui est censée être efficace et concentrée vers une seule tâche. Elles ont leur propre binaire, leur propre signature de code et leur propre ensemble d’éléments, mais sont livrées via l’App Store en tant que partie du binaire de l’application qui les contient. Une application (contenant) peut avoir plus d’une extension. Une fois que l’utilisateur installe une app qui a des extensions, celles-ci seront disponibles sur tout iOS.

Regardons un exemple : Un utilisateur trouve une photo en utilisant Safari, appuie sur le bouton de partage et choisit l’extension de votre application pour le partage. Safari « parle » au framework iOS Social, qui charge et présente l’extension. Le code de l’extension s’exécute, transmet des données en utilisant les canaux de communication instanciés du système et, une fois la tâche accomplie, Safari ferme la vue de l’extension. Peu après, le système met fin au processus, et votre application n’a jamais été affichée à l’écran. Pourtant, elle a rempli une fonction de partage de photos.

iOS, en utilisant la communication interprocessus, est celui qui est chargé de s’assurer que l’application hôte et une extension d’application peuvent travailler ensemble. Les développeurs utilisent des API de haut niveau fournies par le point d’extension et le système, de sorte qu’ils n’ont jamais à se soucier des mécanismes de communication sous-jacents.

Cycle de vie

Les extensions d’apps ont un cycle de vie différent de celui des apps iOS. L’app hôte donne le coup d’envoi du cycle de vie de l’extension en réponse à une action de l’utilisateur. Ensuite, le système instancie l’extension d’app et met en place un canal de communication entre eux. La vue de l’extension est affichée dans le contexte de l’application hôte à l’aide des éléments reçus dans la demande de l’application hôte. Une fois la vue de l’extension affichée, l’utilisateur peut interagir avec elle. En réponse à l’action de l’utilisateur, l’extension complète la demande de l’application hôte en exécutant/annulant immédiatement la tâche ou, si nécessaire, en lançant un processus d’arrière-plan pour l’exécuter. Juste après, l’application hôte ferme la vue de l’extension et l’utilisateur revient à son contexte précédent dans l’application hôte. Les résultats de l’exécution de ce processus peuvent être renvoyés à l’application hôte une fois le processus terminé. L’extension se termine généralement peu après avoir terminé la demande reçue de l’app hôte (ou démarre un processus d’arrière-plan pour l’exécuter).

Le système ouvre l’extension d’une action de l’utilisateur à partir de l’app hôte, l’extension affiche l’interface utilisateur, effectue un certain travail et renvoie des données à l’app hôte (si cela est approprié au type d’extension). L’app contenant n’est même pas en cours d’exécution pendant que son extension est en cours d’exécution.

Création d’une extension d’app – Exemple pratique utilisant l’extension Today

Les extensions Today, également appelées widgets, sont situées dans la vue Today du centre de notification. Elles sont un excellent moyen de présenter un contenu actualisé pour l’utilisateur (comme l’affichage des conditions météorologiques) ou d’effectuer des tâches rapides (comme marquer les choses faites dans le widget d’une application de liste de tâches). Je dois signaler ici que la saisie au clavier n’est pas supportée.

Créons une extension Today qui affichera les informations les plus récentes de notre app (code sur GitHub). Afin d’exécuter ce code, veuillez vous assurer que vous avez (re)configuré l’App Group pour le projet (sélectionnez votre équipe de développement, gardez à l’esprit que le nom de l’App Group doit être unique et suivez les instructions de Xcode).

Création d’un nouveau widget

Comme nous l’avons dit précédemment, les extensions d’apps ne sont pas des apps autonomes. Nous avons besoin d’une app contenant sur laquelle nous allons construire l’extension d’app. Une fois que nous avons notre app contenant, nous choisissons d’ajouter une nouvelle cible en naviguant vers File -> New -> Target dans Xcode. De là, nous choisissons le modèle de notre nouvelle cible pour ajouter une extension Today.

Dans l’étape suivante, nous pouvons choisir notre nom de produit. C’est le nom qui apparaîtra dans la vue Aujourd’hui du centre de notification. Il y a une option pour choisir le langage entre Swift et Objective-C dans cette étape aussi. En terminant ces étapes, Xcode crée un modèle Today, qui fournit des fichiers d’en-tête et d’implémentation par défaut pour la classe principale (nommée TodayViewController) avec un fichier Info.plist et un fichier d’interface (un storyboard ou un fichier .xib). Le fichier Info.plist, par défaut, ressemble à ceci:

Si vous ne voulez pas utiliser le storyboard fourni par le modèle, supprimez la clé NSExtensionMainStoryboard et ajoutez la clé NSExtensionPrincipalClass avec le nom de votre contrôleur de vue comme valeur.

Un widget Today doit :

  • assurer que le contenu semble toujours à jour
  • répondre de manière appropriée aux interactions de l’utilisateur
  • être performant (les widgets iOS doivent utiliser la mémoire de manière judicieuse ou ils seront terminés par le système)

Partage de données et d’un conteneur partagé

L’extension d’application et son application contenante ont toutes deux accès aux données partagées dans leur conteneur partagé défini de manière privée -. ce qui est un moyen d’une communication indirecte entre l’app contenant et l’extension.

N’aimez-vous pas comment Apple rend ces choses si « simples » ? 🙂

Le partage de données par le biais de NSUserDefaults est simple et un cas d’utilisation commun. Par défaut, l’extension et l’app qui la contient utilisent des ensembles de données NSUserDefaults séparés, et ne peuvent pas accéder aux conteneurs de l’autre. Pour modifier ce comportement, iOS a introduit les groupes d’applications. Après avoir activé les groupes d’applications sur l’app contenant et l’extension, au lieu d’utiliser , utilisez initWithSuiteName:@"group.yourAppGroupName"] pour accéder au même conteneur partagé.

Mise à jour du widget

Pour s’assurer que le contenu est toujours à jour, l’extension Today fournit une API pour gérer l’état d’un widget et traiter les mises à jour de contenu. Le système capture occasionnellement des instantanés de la vue du widget, ainsi lorsque le widget devient visible, l’instantané le plus récent est affiché jusqu’à ce qu’il soit remplacé par une version en direct de la vue. Une conformation au protocole NCWidgetProviding est importante pour mettre à jour l’état d’un widget avant qu’un instantané ne soit pris. Une fois que le widget reçoit l’appel widgetPerformUpdateWithCompletionHandler:, la vue du widget doit être mise à jour avec le contenu le plus récent et le gestionnaire d’achèvement doit être appelé avec l’une des constantes suivantes pour décrire le résultat de la mise à jour :

  • NCUpdateResultNewData – Le nouveau contenu nécessite un redessin de la vue
  • NCUpdateResultNoDate – Le widget ne nécessite pas de mise à jour
  • NCUpdateResultFailed – Une erreur s’est produite pendant le processus de mise à jour

Contrôle du moment où le widget est visible

Pour contrôler le moment où un widget est affiché, utilisez la méthode setHasContent:forWidgetWithBundleIdentifier: de la classe NCWidgetController. Cette méthode vous permettra de spécifier l’état du contenu du widget. Elle peut être appelée depuis le widget ou depuis son contenant (s’il est actif). Vous pouvez passer un drapeau NO ou YES à cette méthode, définissant que le contenu du widget est prêt ou non. Si le contenu n’est pas prêt, iOS n’affichera pas votre widget lors de l’ouverture de la vue Today.

Ouvrir l’app contenue depuis le widget

Le widget Today est la seule extension qui peut demander l’ouverture de son app contenue en appelant la méthode openURL:completionHandler:. Pour s’assurer que l’app contenante s’ouvre d’une manière qui a du sens dans le contexte de la tâche actuelle de l’utilisateur, un schéma URL personnalisé (que le widget et l’app contenante peuvent tous deux utiliser) doit être défini.

 completionHandler:nil];

Considérations de l’interface utilisateur

Lors de la conception de votre widget, tirez parti de la classe UIVisualEffectView, en gardant à l’esprit que les vues qui doivent être floues/vibrées doivent être ajoutées à la contentView et non à la UIVisualEffectView directement. Les widgets (conformes au protocole NCWidgetProviding) devraient charger les états en cache dans viewWillAppear: afin de correspondre à l’état de la vue de la dernière viewWillDisappear: et ensuite faire une transition en douceur vers les nouvelles données lorsqu’elles arrivent, ce qui n’est pas un cas avec un contrôleur de vue normal (l’interface utilisateur est configurée dans viewDidLoad et gère les animations et le chargement des données dans viewWillAppear). Les widgets doivent être conçus pour effectuer une tâche, ou ouvrir l’application qui les contient d’un simple tap. La saisie au clavier n’est pas disponible au sein d’un widget. Cela signifie que toute interface utilisateur nécessitant une saisie de texte ne devrait pas être utilisée.

Ajouter des défilements dans un widget, à la fois verticaux et horizontaux, n’est pas possible. Ou plus précisément, l’ajout d’une vue de défilement est possible mais le défilement ne fonctionnera pas. Le geste de défilement horizontal dans une vue de défilement dans l’extension Today sera intercepté par le centre de notification, ce qui provoquera un défilement de Today vers le centre de notification. Le défilement vertical d’une vue de défilement à l’intérieur d’une extension Aujourd’hui sera interrompu par le défilement de la vue d’Aujourd’hui.

Notes techniques

Je vais ici souligner quelques éléments importants à garder à l’esprit lors de la création d’une extension d’App.

Fonctionnalités communes à toutes les extensions

Les éléments suivants sont vrais pour toutes les extensions:

  • l’objet sharedApplication est hors limites : Les extensions d’App ne peuvent pas accéder à un objet sharedApplication, ni utiliser aucune des méthodes liées à cet objet.

  • La caméra et le microphone sont hors limites : Les extensions d’apps ne peuvent pas accéder à la caméra ou au microphone de l’appareil (mais ce n’est pas un cas pour tous les éléments matériels). Ceci est le résultat de l’indisponibilité de certaines APIs. Pour accéder à certains éléments matériels dans l’extension d’app, vous devrez vérifier si son API est disponible pour les extensions d’app ou non (avec la vérification de la disponibilité des API décrite ci-dessus).

  • La plupart des tâches d’arrière-plan sont hors limites : Les extensions d’apps ne peuvent pas effectuer de tâches d’arrière-plan à long terme, à l’exception du lancement de téléchargements ou de téléversement, qui est abordé ci-dessous.

  • AirDrop est hors limites : Les extensions d’applications ne peuvent pas recevoir (mais peuvent envoyer) des données en utilisant AirDrop.

Téléchargement/téléchargement en arrière-plan

La seule tâche qui peut être exécutée en arrière-plan est le téléchargement/téléchargement, en utilisant le NSURLSession object.

Après le lancement de la tâche de téléchargement/téléchargement, l’extension peut compléter la demande de l’application hôte et être terminée sans aucun effet sur le résultat de la tâche. Si l’extension n’est pas en cours d’exécution au moment où la tâche d’arrière-plan se termine, le système lance l’app contenant en arrière-plan et la méthode de délégué de l’app application:handleEventsForBackgroundURLSession:completionHandler: est appelée.

L’app dont l’extension initie une tâche d’arrière-plan NSURLSession doit avoir un conteneur partagé configuré auquel l’app contenant et son extension peuvent accéder.

S’assurer de créer différentes sessions d’arrière-plan pour l’app contenant et chacune de ses extensions d’app (chaque session d’arrière-plan doit avoir un identifiant unique). Ceci est important car un seul processus peut utiliser une session d’arrière-plan à la fois.

Action vs. Share

Les différences entre les extensions Action et Share ne sont pas complètement claires du point de vue d’un codeur, car en pratique elles sont très similaires. Le modèle de Xcode pour la cible de l’extension share utilise SLComposeServiceViewController, qui fournit une interface utilisateur de vue de composition standard que vous pouvez utiliser pour le partage social, mais ce n’est pas obligatoire. Une extension de partage peut également hériter directement de UIViewController pour une conception entièrement personnalisée, de la même manière qu’une extension d’action peut hériter de SLComposeServiceViewController.

Les différences entre ces deux types d’extensions résident dans la manière dont elles sont censées être utilisées. Avec l’extension Action, vous pouvez construire une extension sans interface utilisateur propre (par exemple, une extension utilisée pour traduire le texte sélectionné et renvoyer la traduction à l’hôte). L’extension Share vous permet de partager des commentaires, des photos, des vidéos, de l’audio, des liens et bien plus encore, directement à partir de l’application hôte. Le UIActivityViewController pilote à la fois les extensions d’action et de partage, où les extensions de partage sont présentées sous forme d’icônes de couleur dans la rangée supérieure et les extensions d’action sont présentées sous forme d’icônes monochromes dans la rangée inférieure (Image 2.1).

API interdites

Les API marquées dans les fichiers d’en-tête avec la macro NS_EXTENSION_UNAVAILABLE, ou une macro similaire pour l’indisponibilité, ne peuvent pas être utilisées (par exemple : Les cadres d’interface utilisateur HealthKit et EventKit d’iOS 8 ne peuvent être utilisés dans aucune extension d’application).

Si vous partagez du code entre une application et une extension, vous devez garder à l’esprit que le simple fait de référencer une API qui n’est pas autorisée pour l’extension d’application entraînera le rejet de votre application de l’App Store. Vous pouvez choisir de traiter cela en refactorisant les classes partagées en hiérarchies, avec un parent commun et différentes sous-classes pour différentes cibles.Une autre façon est d’utiliser le pré-processeur par #ifdef contrôles. Parce qu’il n’y a toujours pas de conditionnel de cible intégré, vous devez créer le vôtre.

Une autre façon agréable de le faire est de créer votre propre framework embarqué. Assurez-vous simplement qu’il ne contiendra pas d’APIs indisponibles pour les extensions. Pour configurer une extension d’app pour l’utilisation d’un framework embarqué, naviguez dans les paramètres de construction de la cible et définissez le paramètre  » Require Only App-Extension-Safe API  » sur Oui. Lors de la configuration du projet Xcode, dans la phase de construction Copier les fichiers, « Frameworks » doit être choisi comme destination pour le framework embarqué. Si vous choisissez la destination « SharedFrameworks », votre soumission sera rejetée par App Store.

Une note sur la compatibilité ascendante

Bien que les extensions d’apps ne soient disponibles que depuis iOS 8, vous pouvez rendre votre app contenant disponible pour les versions iOS antérieures.

Conformité de l’interface humaine d’Apple

Gardez à l’esprit les directives d’interface humaine d’iOS d’Apple lors de la conception d’une extension d’app. Vous devez vous assurer que votre extension d’app est universelle, quel que soit l’appareil pris en charge par votre app contenante. Pour vous assurer que l’extension d’app est universelle, utilisez le paramètre de construction de la famille d’appareils ciblée dans Xcode en spécifiant la valeur « iPhone/iPad » (parfois appelée universelle).

Conclusion

Les extensions d’app ont définitivement l’impact le plus visible dans iOS 8. Étant donné que 79% des appareils utilisent déjà iOS 8 (comme mesuré par l’App Store le 13 avril 2015), les extensions d’apps sont des fonctionnalités incroyables dont les apps devraient tirer parti. En combinant les restrictions de l’API et la manière de partager les données entre les extensions et l’application qui les contient, il semble qu’Apple ait réussi à répondre à l’une des plus grandes plaintes concernant la plateforme sans compromettre son modèle de sécurité. Il n’y a toujours aucun moyen pour les applications tierces de partager directement leurs données entre elles. Bien qu’il s’agisse d’un concept très nouveau, il semble très prometteur.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.