Les extensions iOS 8
Alors que la publication d’iOS 8 approche à grand pas, nous souhaitions vous proposer un aperçu des extensions, ainsi qu’un listing des grandes étapes dans la création d’un widget. Une extension est un moyen pour les applications de venir enrichir l’OS sur de multiples points. Autrefois hors d’atteintes, de nouvelles zones de l’OS sont maintenant accessibles aux développeurs, venant ainsi enrichir le panel de lieux pour aller à la rencontre des utilisateurs. Après une présentation des extensions dans leur globalité, nous nous concentrerons sur les Today widgets, qui sont à nos yeux celles offrant les interactions les plus innovantes.
Les extensions iOS 8
Les points d’extension
L’API d’une extension est définie par un point d’extension particulier. Les points d’extensions sont des zones de l’OS dans lesquels une extension peut venir s’insérer. Cela peut aussi bien être le centre de nofitications, que le clavier. Chaque point d’extension est intégré au sein d’un framework système, ce qui en conséquence bloque la route aux points d’extension tiers. Un point d’extension possède 2 principales caractéristiques : ses conditions de lancement (le type de fichier qu’une Share extension peut partager par exemple) et la manière dont il est présenté dans l’application hôte.
Point d’extension | Exemple d’extension |
---|---|
Today | Informer brièvement ou réaliser une tâche succinte dans la vue “Aujourd’hui” du centre de notifications (une extension Today est dénommée Widget) |
Share | Partager sur un plateforme internet ou avec d’autres utilisateurs |
Action | Manipuler une vue ou son contenu dans le contexte d’une autre application |
Photo Editing | Éditer une photo ou vidéo depuis la pellicule |
Document provider | Offrir l’accès à ou gérer un répertoire de fichiers |
Custom keyboard | Remplacer le clavier système d’iOS avec un clavier personnalisé au sein de toutes les applications |
Que ce soit pour traduire instantanément du texte dans Safari ou réaliser des actions succintes dans le centre de notifications, les extensions gomment les frictions présentes dans les précédentes versions d’iOS.
Qu’est-ce qu’une extension ?
Une extension est destinée à réaliser une unique tâche, et à réduire son interface à sa plus simple expression. Cependant, il est important de réaliser qu’une extension n’est pas une application à part entière. Plus précisément, une extension possède ses propres permissions et son propre binaire. Chaque extension doit être délivrée au sein d’une application conteneur, une application qui peut contenir une ou plusieurs extensions. Quand l’utilisateur installe cette application conteneur, l’extension est installée dans le même temps.
Le cycle de vie
Une extension est quasiment toujours instanciée depuis une application hôte. Prenons l’exemple d’un utilisateur qui trouve une image sur Safari, presse le bouton partage, et choisit l’extension qu’il souhaite. Safari est alors l’application hôte, et définit ainsi le contexte dans lequel l’extension fonctionne, mais ne lance pas l’extension elle même. Safari émet sa demande au framework Social, qui à son tour recense, charge et présente les extensions disponibles. L’extension est alors lancée, et communique au travers de canaux de communication mis en place par l’OS. Une fois l’opération finie, Safari cache la vue de l’extension et l’OS stoppe alors le processus.
La communication
Lors de son exécution, une extension communique seulement avec son application hôte. Il n’y a pas de communication directe entre l’application hôte et l’application conteneur. Une communication indirecte est cependant possible entre l’extension et son conteneur au moyen d’APIs bien définies. Elles peuvent être utilisées lorsque l’extension souhaite lancer son conteneur (openURL:), ou partager des ressources (NSUserDefaults).
L’exemple du widget Today
Les widgets Today, comme leur nom l’indique, sont contenus dans la section “Aujourd’hui” du centre de notifications. Ils sont conçus pour offrir à l’utilisateur un accès rapide aux informations qui lui importent sur le moment. Apple a utilisé les widgets au sein des précédentes versions d’iOS pour afficher la météo, les évènements de l’agenda ou marquer un rappel comme effectué. Un widget est dédié à l’affichage de brèves ou la réalisation de tâches succintes (comme afficher les nouvelles du moment ou le statut de tâches en arrière plan). Les performances sont primordiales pour un widget, il est donc fortement déconseillé d’y réaliser des tâches longues ou intensives.
Ajouter une target extension à votre application
Quand vous êtes prêt à créer votre widget, ajoutez simplement une target extension à votre application conteneur. Vous aurez alors à votre disposition un fichier recensant les propriétés de votre widget (Info.plist), un contrôleur de vue, et une fichier d’interface par défaut. Vous verrez s’afficher “1 nouveau widget disponible” dans le centre de notifications, widget que vous pourrez ajouter en cliquant sur “Édition”.
Activer l’App Group
Afin de garder votre widget et votre application conteneur synchronisés, vous devez créer un conteneur de données partagées. Cette étape passe par l’activation de l’App Group pour l’application conteneur et l’extension.
Dans Xcode aller dans l’onglet “Capabilities” de l’application conteneur et activer “App Groups”. Sélectionner votre provisioning profile et ajouter un nouveau groupe. Répéter cette étape avec le widget, mais cette fois-ci utiliser l’App Group précédemment créé.
Le nouvel App Group nécessite maintenant d’être enregistré sur le portail développpeur. L’App ID de votre conteneur et de votre widget doivent alors être ajoutés à ce groupe. Si tout s’est déroulé sans encombre, vous pouvez maintenant utiliser NSUserDefaults pour partager des données entre votre application conteneur et le widget.
Synchroniser des données
Pour activer le partage de données entre l’application conteneur et le widget au moyen de NSUserDefaults, utiliser initWithSuiteName: pour créer une nouvelle instance, en lui passant l’identifiant de l’App Group créé précédemment. Vous n’avez plus qu’à mettre à jour ce conteneur pour partager des données avec votre widget. Par exemple :
// Create and share access to an NSUserDefaults object NSUserDefaults * mySharedDefaults = [[NSUserDefaults alloc] initWithSuitName:@"group.com.example.domain.MyTodayWidget"]; //Use the shared user defaults object to update the user's account [mySharedDefaults setObject:theAccountName forKey:"lastAccountName"];Mettre à jour l’aperçu
L’OS génère régulièrement des aperçus, les ajoute au cache, et les présente au prochain affichage du widget. Pour s’assurer que l’aperçu est effectué au bon moment, implémenter le protocole NCWidgetProviding et la méthode widgetPerformUpdateWithCompletionHandler:. Au sein de cette méthode, vérifier si vous avez un nouveau contenu à afficher et retourner le NSUpdateResult approprié.
Designer l’interface
L’espace à disposition d’un widget étant limité, il est important de l’utiliser intelligemment, afin de conserver une expérience utilisateur simple et efficace. Idéalement votre widget devrait sembler avoir été désigné par Apple. La largeur d’un widget est contrainte par la vue “Aujourd’hui”, la hauteur reste cependant paramétrable.
- Pour ajuster la hauteur, Apple recommande d’utilise l’Auto Layout. Cependant il reste possible d’utiliser preferredContentSize:.
- Pour changer les marges par défaut, implémenter le protocole NCWidgetProviding et la méthode widgetMarginInsetsForProposedMarginInsets:.
- Pour animer le redimensionnement de votre vue, mettre à profit la méthode viewWillTransitionToSize:withTransitionCoordinator:.
- Pour imiter le flou dynamique du centre de notifications, vous pouvez utiliser UIVisualEffectView. Pour un détail des possibilités offertes par cette classe, rendez-vous sur le blogpost consacré à ce sujet.
Il est important de préciser au passage que l’utilisation du clavier n’est pas supportée dans un widget.
Performance et autres considérations
Un widget étant intrinsèquement un contrôleur de vue, cela signifie qu’il suit le même cycle de vie que celui auquel les développeurs sont habitués (viewWillAppear, viewDidDisappear, etc). Les performances étant un élément clé des widgets, assurez-vous que vous êtes prêt à afficher le contenu au moment du viewWillAppear. Cela passe généréalement par la mise en cache de données, et la réalisation des opérations coûteuses en arrière-plan. Du fait que de nombreux widgets peuvent être lancés simultanément, les limites de mémoire sont scrupuleusement appliquées. Être un bon élève passe aussi par éviter de bloquer le thread principal ou monopoliser le GPU.
Toutes les APIs ne sont pas disponibles dans les extensions. Celles qui ne le sont pas sont annotées d’une macro d’indisponibilité, telle que NS_EXTENSION_UNAVAILABLE. Apple les définit comme inappropriées pour les extensions. Un exemple notable est par exemple l’instance partagée de UIApplication.
Le debug
Débugger un widget ou une application dans Xcode est très similaire. En premier lieu choisir l’extension scheme du widget. Au lancement il est alors demandé de choisir une application hôte, dans notre cas “Today”. Le débogueur n’est pas toujours très stable dans les versions beta de Xcode 6, et il est fréquent de perdre la connexion avec l’extension. Dans ce cas là on peut alors l’attacher manuellement au processus en allant dans Debug > Attach to Process. Garder un oeil sur les indicateurs du débogueur durant l’exécution de l’extension se révèle aussi être une bonne idée.
Conclusion
Les extensions sont les fonctionnalités d’iOS 8 qui selon toute vraisemblance auront le plus fort impact visuel. Elles sont utilisées pour gommer les frictions et rendre les interactions plus rapides et plus agréables pour tous les utilisateurs. Grâce aux widgets, le centre de notifications va ainsi devenir un hub pour toutes les informations et tâches qui ne nécessitent qu’un simple coup d’oeil.