← Tous les articles

MetricKit reconstruit : télémétrie sensible à l'état dans iOS 27

L’app de démonstration de la WWDC 2026 d’Apple a signalé un taux de saccades de défilement de 15 millisecondes par seconde, calculé en moyenne sur une journée entière d’utilisation. Réparties par onglet, les mêmes données racontaient une histoire complètement différente : 1 ms/s sur un onglet, 71 ms/s sur l’autre.1 Un écran était presque parfait ; l’autre était, selon les termes de la session, « en proie à des interruptions critiques ».1 Le chiffre agrégé masquait ces deux réalités. La session 222, « Meet the new MetricKit », raconte comment iOS 27 comble cet écart : une refonte complète de la surface d’API du framework, et un nouveau framework compagnon, StateReporting, qui transforme les métriques de terrain à l’échelle de l’app en métriques par état. La télémétrie de terrain peut enfin répondre à la question que tout ingénieur performance se pose en premier : quel écran est lent ?

TL;DR

  • Dans iOS 27, MetricKit « a été reconstruit de A à Z avec une API moderne, Swift-first, riche en contexte et expressive », et chaque nouvelle capacité de la session est exclusive aux nouvelles API.1
  • Le point d’entrée est la classe MetricManager. Les apps attendent les flux asynchrones metricReports et diagnosticReports au lancement, et les deux types de rapports sont Codable, de sorte qu’un JSONEncoder les envoie directement vers votre serveur d’analytique.1
  • Les rapports sont structurés : les intervalEntries contiennent une entrée pour la journée complète ainsi que des découpages plus fins, organisés en groupes de métriques comme .cpu, .memory, .display et .gpu, jusqu’à des valeurs individuelles telles que peakMemory.1
  • Nouvelles données dans iOS 27 : une métrique de fréquence d’images Metal pour les performances de rendu, des diagnostics d’exception mémoire pour les arrêts liés à la limite mémoire, et une category de plantage qui relie les diagnostics de plantage individuels à vos tendances de métriques.1
  • La fonctionnalité phare est le framework StateReporting : signalez l’état dans lequel se trouve votre app (onglet actif, branche d’expérimentation, configuration de vue) et MetricKit agrège les métriques par état, remplaçant un chiffre agrégé unique par un découpage écran par écran.1

Reconstruit de A à Z

Watch on Apple Developer ↗

Yonni, ingénieur de l’équipe MetricKit, présente la refonte d’iOS 27 à partir de 1:23.

La mission de MetricKit n’a pas changé : il reste « la pièce de collecte » du workflow de performance, fournissant deux types de données. Les métriques vous indiquent si un domaine de performance s’améliore ou se dégrade dans l’ensemble ; les diagnostics vous disent quel chemin de code a provoqué un problème.1 Ce qui a changé, c’est toute la manière dont vous recevez ces données. La session l’affirme clairement : dans iOS 27, le framework « a été reconstruit de A à Z avec une API moderne, Swift-first, riche en contexte et expressive », et « toutes les avancées dont je vais parler aujourd’hui sont exclusives à ce nouvel ensemble d’API ».1

Le nouveau point d’entrée est la classe MetricManager. Au lieu d’enregistrer un délégué et d’analyser des charges utiles, vous attendez les rapports via la propriété metricReports sous forme de flux asynchrone. Deux règles opérationnelles proviennent directement de la session : effectuez la configuration au démarrage de l’app « afin d’éviter toute perte de données due à un abonnement tardif », et maintenez MetricManager en vie « pour que les flux puissent continuer à livrer des rapports à mesure que de nouvelles données deviennent disponibles ».1 Apple recommande d’exécuter ce travail dans une tâche détachée ou une classe de service dédiée dès le lancement de l’app.1

La session présente le code sur des diapositives, donc les extraits ci-dessous sont des formes d’appel illustratives correspondant à sa description ; vérifiez les signatures exactes dans la documentation d’Apple avant la mise en production.

// Illustrative call shape based on session 222; verify against the docs.
let manager = MetricManager()

Task.detached {
    for await report in manager.metricReports {
        // Encode and ship, or inspect specific groups.
    }
}

Expédier un rapport vers votre serveur signifiait autrefois manipuler des données de charge utile opaques. Désormais, les valeurs MetricReport sont Codable : « Créez simplement un JSONEncoder et encodez le rapport entier ».1 Si vous voulez une valeur précise plutôt que le document complet, le rapport est entièrement structuré. Vous parcourez les intervalEntries, qui « incluent une entrée agrégée pour la journée complète ainsi que des fenêtres de découpage plus petites lorsqu’elles sont disponibles », généralement de quelques heures chacune et présentes uniquement lorsque des métriques existent pour elles.1 À l’intérieur de chaque intervalle, les métriques sont organisées en groupes de métriques, où « chaque groupe représente un aspect du système, des choses comme .cpu, .memory, .display et .gpu ».1 Filtrez jusqu’au groupe qui vous intéresse (l’exemple de la session récupère memoryMetrics), puis parcourez les cas de métriques pour atteindre une valeur individuelle telle que peakMemory.1

Le catalogue de métriques s’étoffe lui aussi dans iOS 27. Aux côtés des histogrammes de temps de lancement (l’exemple de la session montre la plupart des lancements se situant entre 510 et 540 millisecondes), des blocages, des métriques d’animation et de la consommation de ressources comme le CPU, le GPU, les écritures disque et les transferts réseau, MetricKit ajoute une métrique de fréquence d’images Metal. La session qualifie la fréquence d’images de « métrique clé pour que les développeurs de jeux comprennent les performances de rendu » et renvoie à « Find and fix performance issues in your Metal game » pour le volet optimisation.1

Appuyez-vous sur la métrique de lancement de MetricKit plutôt que d’instrumenter le lancement vous-même. Apple mesure le temps de lancement à partir du moment où l’utilisateur tape sur l’icône de l’app jusqu’à la première image dessinée, ce qui se produit avant même que votre processus n’existe.3 Un chronomètre fait maison ne peut démarrer qu’une fois votre code en cours d’exécution, il manque donc entièrement la fenêtre antérieure à main et sous-estime le lancement que vos utilisateurs ressentent réellement. Lisez l’histogramme que MetricKit fournit plutôt que de construire un chronomètre incapable de voir la partie qui compte.

Diagnostics : backtraces, exceptions mémoire et catégories de plantage

Les métriques vous indiquent qu’une régression est survenue ; les diagnostics vous disent où. Lorsqu’un problème se produit, comme un plantage ou un blocage, « le système capture un diagnostic sur l’appareil » et un rapport de diagnostic « rassemble les détails et le livre immédiatement à votre app via MetricKit ».1 De nombreux diagnostics incluent des backtraces montrant la pile d’appels exacte au moment de l’événement. Dans la démonstration de la session, le backtrace symbolisé commence au démarrage du thread dans le code système, franchit la limite vers l’app, et s’arrête à la fonction submitReport() de l’app, qui marque le point de défaillance et l’endroit à cibler pour un correctif.1

Les diagnostics de plantage transportent un backtrace, la raison de l’arrêt et un type d’exception. Nouveauté d’iOS 27, une category d’arrêt « indique comment chaque plantage a été comptabilisé dans les métriques », de sorte que « si les arrêts anormaux augmentent, vous pouvez les corréler directement avec des diagnostics individuels ».1 La ligne de métrique de votre tableau de bord et les rapports de plantage individuels qui la sous-tendent partagent enfin une clé.

iOS 27 ajoute également des diagnostics d’exception mémoire : « lorsque votre app ou extension est arrêtée pour avoir dépassé sa limite mémoire, vous obtenez plus d’informations sur ce qui s’est passé ».1 Les extensions sont explicitement concernées, ce qui importe à quiconque débogue à distance des arrêts mémoire de widget ou d’extension.

La consommation reflète le volet des métriques. Vous attendez diagnosticReports sur votre instance MetricManager, là encore dès le lancement de l’app dans une tâche détachée ou une classe de service, et les valeurs DiagnosticReport sont Codable pour le même pipeline d’encodage et d’expédition.1 Parce que les rapports sont structurés, vous pouvez parcourir les cas de diagnostic : le cas de plantage fournit le backtrace, la raison et la category, tandis qu’un cas de blocage peut être routé vers un traitement différent.1

// Illustrative call shape based on session 222; verify against the docs.
for await report in manager.diagnosticReports {
    switch /* diagnostic case */ {
    case /* crash */: break  // backtrace, reason, category
    case /* hang */:  break  // handle separately
    default:          break
    }
}

StateReporting : d’un chiffre agrégé unique à la vérité écran par écran

Tout ce qui précède décrit encore la télémétrie à l’échelle de l’app, et la télémétrie à l’échelle de l’app a un plafond. L’app de notes de frais de la session rend le problème concret. L’app organise ses fonctionnalités en un onglet Reports et un onglet Spending. Sur une journée, MetricKit signale 4,5 secondes de temps de saccade total sur 5 minutes de défilement : un taux de saccades de 15 ms/s. Mais ce chiffre est « un taux moyen de saccades de défilement sur l’ensemble de l’utilisation de l’app, même si quelqu’un fait des allers-retours entre l’onglet Reports et l’onglet Spending ».1 Vous savez que l’app saccade. Vous ne savez pas où.

Le nouveau framework StateReporting supprime l’agrégation. Les états sont « des informations que vous définissez et qui décrivent la configuration ou le comportement de votre app, afin que MetricKit puisse agréger les métriques en fonction de ces caractéristiques ».1 À mesure que les gens se déplacent entre les onglets, l’app signale chaque transition, et MetricKit croise ces états avec les données de métriques et de diagnostics.1

Le résultat de la démonstration est le moment qui justifie la refonte. Au lieu d’un unique chiffre agrégé de 15 ms/s, les métriques arrivent par état : l’onglet Spending défilait « de manière incroyablement fluide » à 1 ms/s, tandis que l’onglet Reports « a grimpé à 71 ms/s ».1 La session tire la conclusion que le chiffre agrégé ne pourrait jamais étayer : « l’onglet Spending fonctionne parfaitement ! Mais l’onglet Reports subit des interruptions critiques, et c’est exactement là que votre effort d’optimisation devrait se concentrer ».1 Un chiffre est devenu un verdict et un ordre de mission.

Les états suivent un modèle de transition, et non un encadrement. « Il n’y a pas de paires de début ou de fin : l’app signale l’état dans lequel elle se trouve, à tout moment », et MetricKit suit la durée pendant laquelle l’app reste dans chaque état.1

Domaines, métadonnées et encodage par état

Chaque état est rattaché à un domaine, qui « décrit une fonction ou un secteur d’une app ». Un domaine ne peut contenir qu’un seul état actif à la fois, et des domaines distincts permettent à plusieurs états d’être en cours simultanément.1 L’exemple de la session est une expérience A/B : avec un changement expérimental activé, les dépenses sont récupérées depuis la base de données par petits lots ; désactivé, par lots plus volumineux. Placer l’état d’onglet et l’état de taille de lot dans des domaines distincts signifie que « MetricKit livrera des métriques distinctes pour chaque onglet et chaque taille de lot ».1 Télémétrie par écran et résultats d’expérimentation issus du même pipeline, sur le terrain.

L’adoption comporte trois étapes dans la session : importez le framework StateReporting, créez un domaine (« généralement une chaîne en DNS inversé ») et enregistrez-le lorsque vous configurez votre instance MetricManager, puis signalez les transitions à mesure que l’app entre dans chaque état, comme la transition vers un état identifié par la chaîne « Reports ».1 Pour une granularité plus fine, vous définissez votre propre struct avec la macro ReportableMetadata, créez un StateReporter avec ce type de métadonnées, et signalez les transitions à la fois avec l’étiquette et votre type personnalisé. L’exemple ViewConfiguration de la session transporte une valeur listSize et indique si la liste est triée.1 Là encore : la session montre ce flux sur des diapositives sans signatures complètes, donc traitez cette forme comme quelque chose à confirmer dans la documentation plutôt que comme une syntaxe à copier.

Du côté réception, le rapport gagne un second axe. Avant que des états ne soient signalés, la propriété stateEntries de votre rapport de métriques est vide. Après adoption, le rapport transporte des valeurs StateEntry, chacune contenant « des valeurs de métriques agrégées sur le temps passé dans cet état individuel ».1 Pour le pipeline serveur, vous pouvez regrouper la sortie encodée par domaine : définissez la clé encodingFormatKey sur la propriété userInfo de votre JSONEncoder à byStateReportingDomain, et le rapport encodé présente à la fois les entrées d’état et les entrées d’intervalle « regroupées par chaque domaine et état présents dans le rapport ».1

Bonnes pratiques, et par où commencer

La session se termine sur des conseils qui se lisent comme une sagesse de conception de schéma durement acquise. Les domaines devraient être étroitement délimités à un seul secteur de l’app. Les transitions d’état « devraient représenter des phases stables et significatives, et non des événements d’UI transitoires ».1 Concevez chaque état de sorte que, lorsqu’une régression apparaît, l’état seul vous donne assez d’informations pour cibler le correctif. Et résistez à l’envie de tout instrumenter : « Trop d’états peuvent produire des données trop granulaires et peuvent en fait rendre plus difficile l’interprétation de la situation globale », et des limites supérieures sur le nombre d’états existent pour minimiser la surcharge (la session ne donne pas de chiffre).1 Avant la mise en production, vérifiez que les états signalés correspondent à vos attentes avec l’instrument Points of Interest.1

La cardinalité est le même piège du côté des métadonnées. Regroupez les valeurs d’état à évolution rapide dans des catégories grossières (petit, moyen, grand) plutôt que de signaler des comptes exacts. Au laboratoire de performance de la WWDC 2026 d’Apple, où l’équipe a répondu en direct à des questions sur l’adoption de MetricKit et sur le workflow énergétique plus large (relaté dans ce que l’équipe de performance d’Apple a dit au laboratoire de la WWDC26), elle a noté qu’enregistrer « 1 000 contre 1 001 éléments » ajoute du coût sans apporter d’éclairage : les deux valeurs tombent dans le même régime de performance, donc un état distinct pour chacune n’achète que de la surcharge et rien d’autre.4 Choisissez les frontières qui changent le comportement et regroupez tout ce qui se trouve entre elles.

Le volet collecte n’est que la moitié du système. La session est directe : « analyser les métriques sur tous les appareils est un problème de science des données » : vous montez un serveur qui ingère les rapports, vous agrégez selon les dimensions qui vous intéressent, vous établissez une référence et vous surveillez tout mouvement dans un sens comme dans l’autre.1 Les rapports Codable et l’encodage byStateReportingDomain existent pour alimenter précisément ce pipeline.

Pour les adopteurs existants, l’instruction finale est explicite : « si vous utilisez l’API MXMetricManager, migrez vers la nouvelle API MetricManager pour tirer parti de toutes ces nouvelles capacités ».1 La documentation d’Apple formalise désormais cette migration : MXMetricManager est marquée comme dépréciée depuis la version 27.0, avec la consigne « Use MetricManager instead ».2 La même application échelonnée à la frontière de la version 27 a été déployée ailleurs ce cycle, y compris la dépréciation d’ImageCreator d’Image Playground dans iOS 27, où un avertissement de dépréciation cède la place à une rupture nette lors de la publication publique. Les nouvelles API sont là où vit chaque avancée de la session, et la session les présente comme « l’avenir du framework ».1

FAQ

Qu’est-ce qui a réellement changé dans MetricKit dans iOS 27 ?

Le framework a été reconstruit avec une API moderne, Swift-first. Le point d’entrée est la nouvelle classe MetricManager ; les rapports de métriques et de diagnostics arrivent sous forme de flux asynchrones que l’on peut attendre (metricReports, diagnosticReports) ; les rapports sont Codable pour un encodage JSON direct ; et la structure est navigable dans le code via intervalEntries et les groupes de métriques. iOS 27 ajoute aussi une métrique de fréquence d’images Metal, des diagnostics d’exception mémoire, une category de plantage qui relie les diagnostics de plantage à la comptabilisation des métriques, et le framework StateReporting pour des métriques par état.1

Comment StateReporting décide-t-il quelles métriques appartiennent à quel état ?

Votre app signale des transitions : l’état vers lequel elle se dirige, au sein d’un domaine que vous définissez. MetricKit suit la durée pendant laquelle l’app reste dans chaque état et agrège les valeurs de métriques sur le temps qui y est passé. Il n’y a pas de paires de début/fin ; l’app signale simplement l’état dans lequel elle se trouve à tout moment. Chaque état obtient alors sa propre StateEntry dans le rapport de métriques.1

Puis-je suivre plus d’une dimension à la fois, comme l’écran et la branche d’expérimentation ?

Oui. Chaque domaine peut contenir un seul état actif à la fois, mais des domaines distincts s’exécutent simultanément. L’app de notes de frais de la session place l’onglet actif dans un domaine et une expérience de taille de lot de base de données dans un autre, et MetricKit livre des métriques distinctes pour chaque onglet et chaque taille de lot.1

Devrais-je signaler chaque événement d’UI comme un état ?

Non. La session recommande des états qui représentent des phases stables et significatives plutôt que des événements d’UI transitoires, des domaines étroitement délimités à un seul secteur de l’app, et de la retenue dans l’ensemble : trop d’états rendent les données plus difficiles à interpréter, et le système impose des limites supérieures sur le nombre d’états pour minimiser la surcharge. Validez vos états avec l’instrument Points of Interest avant la mise en production.1

Suis-je obligé d’abandonner MXMetricManager ?

La consigne de la session est de migrer de MXMetricManager vers la nouvelle API MetricManager, car chaque nouvelle capacité couverte (flux asynchrones, rapports Codable, métriques sensibles à l’état, les nouveaux types de métriques et de diagnostics) est exclusive au nouvel ensemble d’API.1


MetricKit est la moitié « terrain » d’une histoire en deux parties cette année : Instruments vous montre la saccade en laboratoire, et MetricKit sensible à l’état vous indique quels écrans saccadent pour de vrais utilisateurs, couvert du côté laboratoire dans Instruments 27 et la réactivité des apps. Le travail de rendu qui corrige réellement un onglet à 71 ms/s se trouve dans Performances et interopérabilité SwiftUI dans iOS 27. Et la raison pour laquelle les moyennes agrégées induisent en erreur en premier lieu fait l’objet de l’angle mort des performances. Le hub complet de la série est la série Apple Ecosystem.

Références


  1. Apple, WWDC 2026 session 222, Meet the new MetricKit. Source pour la refonte complète d’iOS 27 et le cadrage de l’API Swift-first, le point d’entrée MetricManager et les flux asynchrones metricReports / diagnosticReports, les rapports Codable et l’usage de JSONEncoder, les intervalEntries et les groupes de métriques (.cpu, .memory, .display, .gpu, peakMemory), la métrique de fréquence d’images Metal, les diagnostics d’exception mémoire, la category d’arrêt de plantage, la démonstration du backtrace de submitReport(), le framework StateReporting (domaines, modèle de transition, StateReporter, ReportableMetadata, stateEntries, byStateReportingDomain via encodingFormatKey), les chiffres de la démonstration de l’app de notes de frais (15 ms/s agrégés ; 1 ms/s sur l’onglet Spending contre 71 ms/s sur l’onglet Reports), les bonnes pratiques d’état et la validation par Points of Interest, et la consigne de migrer de MXMetricManager vers MetricManager

  2. Documentation pour développeurs Apple : MXMetricManager. Marquée comme dépréciée depuis la version 27.0, avec la consigne « Use MetricManager instead ». 

  3. Documentation pour développeurs Apple : Reducing your app’s launch time. Le temps de lancement est mesuré à partir du moment où l’utilisateur tape sur l’icône de l’app jusqu’à la première image dessinée, avant que le processus de l’app n’existe. 

  4. Apple, laboratoire du groupe performance de la WWDC 2026, session 8003. Paraphrasé d’un enregistrement transcrit localement ; aucune transcription officielle n’est publiée. L’équipe a conseillé de regrouper les métadonnées d’état à évolution rapide dans des catégories grossières, notant qu’enregistrer « 1 000 contre 1 001 éléments » ajoute du coût sans apporter d’éclairage. 

Articles connexes

Ce que l'équipe Performance d'Apple a dit lors du lab de la WWDC26

L'équipe Power & Performance d'Apple a répondu en direct aux questions des développeurs à la WWDC26. Conseils de terrain…

15 min de lecture

Les nouveautés d'Instruments 27 pour la réactivité des apps

Instruments 27 ajoute Top Functions, Run Comparisons, un instrument Swift executors et un nouveau panneau Inspector pour…

13 min de lecture

De 76 à 100 : obtenir un score Lighthouse parfait

Comment un site portfolio personnel est passé d'un score de performance mobile Lighthouse de 76 avec un CLS de 0,493 à u…

7 min de lecture