MetricKit reconstruido: telemetría por estado en iOS 27
La app de demostración de Apple en la WWDC 2026 informó una tasa de tirones de desplazamiento de 15 milisegundos por segundo, promediada a lo largo de un día entero de uso. Desglosada por pestaña, esa misma cifra contaba una historia completamente distinta: 1 ms/s en una pestaña, 71 ms/s en la otra.1 Una pantalla era casi impecable; la otra, en palabras de la sesión, “experimentaba interrupciones críticas”.1 El número combinado ocultaba ambos hechos. La sesión 222, “Meet the new MetricKit”, cuenta cómo iOS 27 cierra esa brecha: una reconstrucción desde cero de la superficie de API del framework y un nuevo framework complementario, StateReporting, que convierte las métricas de campo de toda la app en métricas por estado. La telemetría de campo por fin puede responder la pregunta que todo ingeniero de rendimiento se hace primero: ¿qué pantalla va lenta?
TL;DR
- En iOS 27, MetricKit “se ha reconstruido desde cero con una API moderna, expresiva, rica en contexto y centrada en Swift”, y todas las capacidades nuevas de la sesión son exclusivas de las nuevas API.1
- El punto de entrada es la clase
MetricManager. Las apps esperan (await) los flujos asíncronosmetricReportsydiagnosticReportsal iniciarse, y ambos tipos de informe sonCodable, así que unJSONEncoderlos envía directamente a tu servidor de analítica.1 - Los informes están estructurados:
intervalEntriescontiene una entrada de día completo más desgloses más pequeños, organizados en grupos de métricas como.cpu,.memory,.displayy.gpu, hasta llegar a valores individuales comopeakMemory.1 - Datos nuevos en iOS 27: una métrica de tasa de fotogramas de Metal para el rendimiento de renderizado, diagnósticos de excepción de memoria para las terminaciones por superar el límite de memoria, y una
categoryde fallo que vincula los diagnósticos de fallos individuales con tus tendencias de métricas.1 - La función estrella es el framework
StateReporting: informa del estado en el que se encuentra tu app (pestaña activa, rama de experimento, configuración de vista) y MetricKit agrega las métricas por estado, sustituyendo un único número combinado por un desglose pantalla por pantalla.1
Reconstruido desde cero
Yonni, ingeniero del equipo de MetricKit, presenta la reconstrucción de iOS 27 a partir del minuto 1:23.
El trabajo de MetricKit no ha cambiado: sigue siendo “la pieza de recolección” del flujo de trabajo de rendimiento y proporciona dos tipos de datos. Las métricas te dicen si un área de rendimiento mejora o empeora en general; los diagnósticos te dicen qué ruta de código causó un problema.1 Lo que cambió es todo en cuanto a cómo recibes esos datos. La sesión lo dice sin rodeos: en iOS 27 el framework “se ha reconstruido desde cero con una API moderna, expresiva, rica en contexto y centrada en Swift”, y “todos los avances de los que hablaré hoy son exclusivos de este nuevo conjunto de API”.1
El nuevo punto de entrada es la clase MetricManager. En lugar de registrar un delegado y analizar cargas útiles, esperas los informes a través de la propiedad metricReports como un flujo asíncrono. Dos reglas operativas vienen directas de la sesión: realiza la configuración al iniciar la app “para evitar cualquier pérdida de datos por una suscripción tardía”, y mantén MetricManager con vida “para que los flujos puedan seguir entregando informes a medida que los datos posteriores estén listos”.1 Apple recomienda ejecutar este trabajo en una tarea desacoplada (detached) o en una clase de servicio dedicada en cuanto la app se inicia.1
La sesión presenta el código en diapositivas, así que los fragmentos siguientes son formas de llamada ilustrativas que coinciden con su descripción; confirma las firmas exactas en la documentación de Apple antes de lanzar.
// 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.
}
}
Antes, enviar un informe a tu servidor significaba manejar datos de carga útil opacos. Ahora los valores MetricReport son Codable: “Solo crea un JSONEncoder y codifica el informe entero”.1 Si quieres un valor concreto en lugar del documento completo, el informe está totalmente estructurado. Recorres intervalEntries, que “incluye una entrada agregada de día completo y ventanas de desglose más pequeñas cuando están disponibles”, normalmente de unas pocas horas cada una y presentes solo cuando existen métricas para ellas.1 Dentro de cada intervalo, las métricas se organizan en grupos de métricas, donde “cada grupo representa un aspecto del sistema, cosas como .cpu, .memory, .display y .gpu”.1 Filtra hasta el grupo que te interese (el ejemplo de la sesión extrae memoryMetrics) y luego recorre con switch los casos de métricas para llegar a un valor individual como peakMemory.1
El catálogo de métricas también crece en iOS 27. Junto a los histogramas de tiempo de lanzamiento (el ejemplo de la sesión muestra que la mayoría de los lanzamientos quedan entre 510 y 540 milisegundos), los bloqueos, las métricas de animación y el consumo de recursos como CPU, GPU, escrituras en disco y transferencias de red, MetricKit añade una métrica de tasa de fotogramas de Metal. La sesión llama a la tasa de fotogramas “una métrica clave para que los desarrolladores de juegos entiendan el rendimiento de renderizado” y remite a “Find and fix performance issues in your Metal game” para la parte de optimización.1
Diagnósticos: backtraces, excepciones de memoria y categorías de fallos
Las métricas te dicen que algo retrocedió; los diagnósticos te dicen dónde. Cuando algo sale mal, como un fallo o un bloqueo, “el sistema captura un diagnóstico en el dispositivo” y un informe de diagnóstico “empaqueta los detalles y los entrega de inmediato a tu app a través de MetricKit”.1 Muchos diagnósticos incluyen backtraces que muestran la pila de llamadas exacta en el momento del evento. En el recorrido de la sesión, el backtrace simbolizado empieza en el inicio del hilo dentro del código del sistema, cruza hacia la app y se detiene en la función submitReport() de la app, lo que marca el punto del fallo y el lugar al que dirigir una corrección.1
Los diagnósticos de fallo llevan un backtrace, el motivo de terminación y un tipo de excepción. Novedad en iOS 27: una category de terminación “indica cómo se contabilizó cada fallo en las métricas”, de modo que “si las terminaciones anómalas tienden al alza, puedes correlacionarlas directamente con los diagnósticos individuales”.1 La línea de métrica de tu panel y los informes de fallos individuales que la sustentan por fin comparten una clave.
iOS 27 también añade diagnósticos de excepción de memoria: “cuando tu app o extensión se termina por superar su límite de memoria, obtienes más información sobre lo que ocurrió”.1 Las extensiones entran explícitamente en el alcance, algo que importa a cualquiera que depure de forma remota cierres por memoria de widgets o extensiones.
El consumo refleja el lado de las métricas. Esperas diagnosticReports en tu instancia de MetricManager, de nuevo desde el inicio de la app en una tarea desacoplada o una clase de servicio, y los valores DiagnosticReport son Codable para el mismo flujo de codificar y enviar.1 Como los informes están estructurados, puedes hacer switch sobre los casos de diagnóstico: el caso de fallo produce el backtrace, el motivo y la category, mientras que un caso de bloqueo puede dirigirse a un procesamiento distinto.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: de un único número combinado a la verdad pantalla por pantalla
Todo lo anterior sigue describiendo telemetría de toda la app, y la telemetría de toda la app tiene un techo. La app de informes de gastos de la sesión hace concreto el problema. La app organiza sus funciones en una pestaña Reports y una pestaña Spending. A lo largo de un día, MetricKit informa 4,5 segundos de tiempo total de tirones en 5 minutos de desplazamiento: una tasa de tirones de 15 ms/s. Pero ese número es “una tasa media de tirones de desplazamiento sobre todo el uso de la app, incluso si alguien va y viene entre la pestaña Reports y la pestaña Spending”.1 Sabes que la app da tirones. No sabes dónde.
El nuevo framework StateReporting elimina la mezcla. Los estados son “información que tú defines y que describe la configuración o el comportamiento de tu app, para que MetricKit pueda agregar métricas en función de esas características”.1 A medida que las personas se mueven entre pestañas, la app informa cada transición, y MetricKit cruza esos estados con los datos de métricas y diagnósticos.1
La recompensa en la demo es el momento que justifica la reconstrucción. En lugar de una única cifra combinada de 15 ms/s, las métricas llegan por estado: la pestaña Spending se desplazaba “increíblemente suave” a 1 ms/s, mientras que la pestaña Reports “se disparaba a 71 ms/s”.1 La sesión saca la conclusión que el número combinado nunca pudo respaldar: “¡la pestaña Spending rinde de maravilla! Pero la pestaña Reports experimenta interrupciones críticas, y ahí es exactamente donde debes concentrar tu esfuerzo de optimización”.1 Un número se convirtió en un veredicto y una orden de trabajo.
Los estados siguen un modelo de transición, no de delimitación por pares. “No hay pares de inicio o fin: la app informa la condición en la que está, en cada momento dado”, y MetricKit registra cuánto tiempo permanece la app en cada estado.1
Dominios, metadatos y codificación por estado
Cada estado se acota a un dominio, que “describe una función o un área de una app”. Un dominio solo puede contener un estado activo a la vez, y dominios separados permiten que varios estados estén en curso de forma simultánea.1 El ejemplo de la sesión es un experimento A/B: con un cambio experimental activado, los gastos se obtienen de la base de datos en lotes pequeños; desactivado, en lotes más grandes. Colocar el estado de la pestaña y el estado del tamaño de lote en dominios separados significa que “MetricKit entregará métricas separadas para cada pestaña y cada tamaño de lote”.1 Telemetría pantalla por pantalla y lecturas de experimentos desde la misma tubería, en el campo.
La adopción tiene tres pasos en la sesión: importa el framework StateReporting, crea un dominio (“normalmente una cadena en DNS inverso”) y regístralo cuando configures tu instancia de MetricManager, y luego informa las transiciones a medida que la app entra en cada estado, como una transición a un estado identificado por la cadena “Reports”.1 Para mayor granularidad, defines tu propio struct con la macro ReportableMetadata, creas un StateReporter con ese tipo de metadatos e informas las transiciones con la etiqueta y tu tipo personalizado a la vez. El ejemplo ViewConfiguration de la sesión lleva un valor listSize y si la lista está ordenada.1 De nuevo: la sesión muestra este flujo en diapositivas sin firmas completas, así que trata la forma como algo que confirmar en la documentación y no como sintaxis para copiar.
En el lado receptor, el informe gana un segundo eje. Antes de que se informe cualquier estado, la propiedad stateEntries de tu informe de métricas está vacía. Tras la adopción, el informe lleva valores StateEntry, cada uno con “valores de métricas agregados a lo largo del tiempo que se pasó en ese estado individual”.1 Para la tubería del servidor, puedes agrupar la salida codificada por dominio: establece la clave encodingFormatKey en la propiedad userInfo de tu JSONEncoder en byStateReportingDomain, y el informe codificado presenta tanto las entradas de estado como las de intervalo “agrupadas por cada dominio y estado que existe en el informe”.1
Buenas prácticas y por dónde empezar
La sesión cierra con orientaciones que se leen como consejos de diseño de esquemas ganados con esfuerzo. Los dominios deben acotarse de forma estrecha a una sola área de la app. Las transiciones de estado “deben representar fases estables y significativas, no eventos de interfaz transitorios”.1 Diseña cada estado de modo que, cuando aparezca una regresión, el estado por sí solo te dé información suficiente para dirigir la corrección. Y resiste el impulso de instrumentarlo todo: “Demasiados estados pueden generar datos demasiado granulares y, de hecho, dificultar la interpretación del panorama general”, y existen límites superiores en el número de estados para minimizar la sobrecarga (la sesión no da una cifra).1 Antes de lanzar, valida que los estados informados coinciden con tus expectativas con el instrumento Points of Interest.1
El lado de la recolección es solo la mitad del sistema. La sesión es directa: “analizar métricas en todos los dispositivos es un problema de ciencia de datos”. Montas un servidor que ingiere los informes, agregas según las dimensiones que te interesan, estableces una línea base y vigilas el movimiento en cualquiera de las dos direcciones.1 Los informes Codable y la codificación byStateReportingDomain existen para alimentar exactamente esa tubería.
Para quienes ya lo adoptaron, la instrucción final es explícita: “si usas la API MXMetricManager, migra a la nueva API MetricManager para aprovechar todas estas nuevas capacidades”.1 Es en las nuevas API donde vive cada avance de la sesión, y la sesión las presenta como “el futuro del framework”.1
Preguntas frecuentes
¿Qué cambió realmente en MetricKit en iOS 27?
El framework se reconstruyó con una API moderna y centrada en Swift. El punto de entrada es la nueva clase MetricManager; los informes de métricas y diagnósticos llegan como flujos asíncronos que se pueden esperar (metricReports, diagnosticReports); los informes son Codable para una codificación JSON directa; y la estructura se puede navegar en código mediante intervalEntries y los grupos de métricas. iOS 27 también añade una métrica de tasa de fotogramas de Metal, diagnósticos de excepción de memoria, una category de fallo que vincula los diagnósticos de fallos con la contabilización de métricas, y el framework StateReporting para métricas por estado.1
¿Cómo decide StateReporting qué métricas pertenecen a qué estado?
Tu app informa transiciones: el estado al que se mueve, dentro de un dominio que tú defines. MetricKit registra cuánto tiempo permanece la app en cada estado y agrega los valores de métricas a lo largo del tiempo que pasa allí. No hay pares de inicio/fin; la app simplemente informa la condición en la que está en cada momento dado. Cada estado obtiene entonces su propia StateEntry en el informe de métricas.1
¿Puedo seguir más de una dimensión a la vez, como la pantalla y la rama de experimento?
Sí. Cada dominio puede contener un estado activo a la vez, pero dominios separados se ejecutan de forma concurrente. La app de gastos de la sesión coloca la pestaña activa en un dominio y un experimento de tamaño de lote de base de datos en otro, y MetricKit entrega métricas separadas para cada pestaña y cada tamaño de lote.1
¿Debo informar cada evento de interfaz como un estado?
No. La sesión recomienda estados que representen fases estables y significativas en lugar de eventos de interfaz transitorios, dominios acotados de forma estrecha a una sola área de la app y moderación en general: demasiados estados hacen que los datos sean más difíciles de interpretar, y el sistema impone límites superiores al número de estados para minimizar la sobrecarga. Valida tus estados con el instrumento Points of Interest antes de lanzar.1
¿Tengo que dejar MXMetricManager?
La orientación de la sesión es migrar de MXMetricManager a la nueva API MetricManager, porque todas las capacidades nuevas tratadas (flujos asíncronos, informes Codable, métricas conscientes del estado, los nuevos tipos de métricas y diagnósticos) son exclusivas del nuevo conjunto de API.1
MetricKit es la mitad de campo de una historia de dos partes este año: Instruments te muestra el tirón en el laboratorio, y el MetricKit consciente del estado te dice qué pantallas dan tirones para usuarios reales, algo que se cubre desde el lado del laboratorio en Instruments 27 y la capacidad de respuesta de las apps. El trabajo de renderizado que de verdad arregla una pestaña a 71 ms/s vive en Rendimiento e interoperabilidad de SwiftUI en iOS 27. Y la razón por la que los promedios combinados engañan desde el principio es el tema de el punto ciego del rendimiento. El centro de la serie completa es la serie Apple Ecosystem.
Referencias
-
Apple, WWDC 2026 session 222, Meet the new MetricKit. Fuente de la reconstrucción desde cero de iOS 27 y del enfoque centrado en Swift, el punto de entrada
MetricManagery los flujos asíncronosmetricReports/diagnosticReports, los informesCodabley el uso deJSONEncoder,intervalEntriesy los grupos de métricas (.cpu,.memory,.display,.gpu,peakMemory), la métrica de tasa de fotogramas de Metal, los diagnósticos de excepción de memoria, lacategoryde terminación de fallo, el recorrido del backtrace desubmitReport(), el frameworkStateReporting(dominios, modelo de transición,StateReporter,ReportableMetadata,stateEntries,byStateReportingDomainmedianteencodingFormatKey), las cifras de la demo de la app de gastos (15 ms/s combinados; 1 ms/s en la pestaña Spending frente a 71 ms/s en la pestaña Reports), las buenas prácticas de estados y la validación con Points of Interest, y la orientación de migrar deMXMetricManageraMetricManager. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩
