MetricKit reconstruído: telemetria sensível ao estado no iOS 27
O app de demonstração da WWDC 2026 da Apple relatou uma taxa de hitches de rolagem de 15 milissegundos por segundo, com média ao longo de um dia inteiro de uso. Dividido por aba, os mesmos dados contaram uma história completamente diferente: 1 ms/s em uma aba, 71 ms/s na outra.1 Uma tela estava quase impecável; a outra estava, nas palavras da sessão, “sofrendo interrupções críticas”.1 O número combinado escondia os dois fatos. A sessão 222, “Meet the new MetricKit”, é a história de como o iOS 27 fecha essa lacuna: uma reconstrução do zero da superfície de API do framework e um novo framework complementar, StateReporting, que transforma as métricas de campo de todo o app em métricas por estado. A telemetria de campo finalmente consegue responder à pergunta que todo engenheiro de performance faz primeiro: qual tela está lenta?
TL;DR
- No iOS 27, o MetricKit “foi reconstruído do zero com uma API moderna, expressiva, contextualmente rica e Swift-first”, e cada novo recurso da sessão é exclusivo das novas APIs.1
- O ponto de entrada é a classe
MetricManager. Os apps aguardam os streams assíncronosmetricReportsediagnosticReportsna inicialização, e ambos os tipos de relatório sãoCodable, então umJSONEncoderos envia diretamente para o seu servidor de analytics.1 - Os relatórios são estruturados:
intervalEntriescontém uma entrada do dia inteiro mais subdivisões menores, organizadas em grupos de métricas como.cpu,.memory,.displaye.gpu, até valores individuais comopeakMemory.1 - Novos dados no iOS 27: uma métrica de taxa de quadros do Metal para performance de renderização, diagnósticos de exceção de memória para encerramentos por limite de memória e uma
categoryde crash que liga diagnósticos individuais de crash de volta às suas tendências de métricas.1 - O recurso de destaque é o framework
StateReporting: relate o estado em que o seu app está (aba ativa, braço do experimento, configuração da view) e o MetricKit agrega métricas por estado, substituindo um único número combinado por uma subdivisão por tela.1
Reconstruído do zero
Yonni, engenheiro da equipe do MetricKit, apresenta a reconstrução do iOS 27 a partir de 1:23.
O trabalho do MetricKit não mudou: ele é “a peça de coleta” do fluxo de performance, fornecendo dois tipos de dados. As métricas indicam se uma área de performance está melhorando ou piorando no geral; os diagnósticos indicam qual caminho de código causou um problema.1 O que mudou foi tudo sobre como você recebe esses dados. A sessão afirma com clareza: no iOS 27 o framework “foi reconstruído do zero com uma API moderna, expressiva, contextualmente rica e Swift-first”, e “todos os avanços que discutirei hoje são exclusivos deste novo conjunto de APIs”.1
O novo ponto de entrada é a classe MetricManager. Em vez de registrar um delegate e fazer o parsing de payloads, você aguarda os relatórios através da propriedade metricReports como um stream assíncrono. Duas regras operacionais vêm direto da sessão: faça a configuração na inicialização do app “para evitar qualquer perda de dados por assinatura tardia” e mantenha o MetricManager vivo “para que os streams possam continuar entregando relatórios à medida que dados subsequentes ficam prontos”.1 A Apple recomenda executar o trabalho em uma tarefa detached ou em uma classe de serviço dedicada assim que o app for iniciado.1
A sessão apresenta o código em slides, então os trechos abaixo são formatos ilustrativos de chamada que correspondem à sua descrição; confirme as assinaturas exatas na documentação da Apple antes de enviar para produção.
// 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.
}
}
Enviar um relatório para o seu servidor costumava significar lidar com dados de payload opacos. Agora os valores de MetricReport são Codable: “Basta criar um JSONEncoder e codificar o relatório inteiro”.1 Se você quiser um valor específico em vez do documento inteiro, o relatório é totalmente estruturado. Você itera pelos intervalEntries, que “incluem uma entrada agregada do dia inteiro e janelas de subdivisão menores quando disponíveis”, normalmente de algumas horas cada e presentes apenas quando existem métricas para elas.1 Dentro de cada intervalo, as métricas são organizadas em grupos de métricas, onde “cada grupo representa um aspecto do sistema, coisas como .cpu, .memory, .display e .gpu”.1 Filtre até o grupo que interessa a você (o exemplo da sessão extrai memoryMetrics), depois faça um switch sobre os casos de métrica para chegar a um valor individual como peakMemory.1
O catálogo de métricas também cresce no iOS 27. Além dos histogramas de tempo de inicialização (o exemplo da sessão mostra a maioria das inicializações entre 510 e 540 milissegundos), hangs, métricas de animação e consumo de recursos como CPU, GPU, escritas em disco e transferências de rede, o MetricKit adiciona uma métrica de taxa de quadros do Metal. A sessão chama a taxa de quadros de “uma métrica fundamental para desenvolvedores de jogos entenderem a performance de renderização” e indica “Find and fix performance issues in your Metal game” para o lado da otimização.1
Apoie-se na métrica de inicialização do MetricKit em vez de instrumentar a inicialização por conta própria. A Apple mede o tempo de inicialização desde o momento em que o usuário toca no ícone do app até o primeiro quadro desenhado, o que acontece antes mesmo de o seu processo existir.3 Um timer feito à mão só pode começar quando o seu código roda, então ele perde completamente a janela pré-main e subestima a inicialização que os seus usuários realmente sentem. Leia o histograma que o MetricKit fornece em vez de construir um timer que não consegue enxergar a parte que importa.
Diagnósticos: backtraces, exceções de memória e categorias de crash
As métricas indicam que algo regrediu; os diagnósticos indicam onde. Quando algo dá errado, como um crash ou um hang, “o sistema captura um diagnóstico no dispositivo” e um relatório de diagnóstico “empacota os detalhes e os entrega imediatamente ao seu app através do MetricKit”.1 Muitos diagnósticos incluem backtraces que mostram a pilha de chamadas exata no momento do evento. No passo a passo da sessão, o backtrace simbolizado começa no início da thread em código do sistema, atravessa para o app e para na função submitReport() do app, que marca o ponto da falha e o local a ser corrigido.1
Os diagnósticos de crash carregam um backtrace, o motivo do encerramento e um tipo de exceção. Novidade no iOS 27, uma category de encerramento “indica como cada crash foi contabilizado nas métricas”, de modo que “se os encerramentos anormais estão em tendência de alta, você pode correlacioná-los diretamente com diagnósticos individuais”.1 A linha de métrica no seu dashboard e os relatórios individuais de crash por trás dela finalmente compartilham uma chave.
O iOS 27 também adiciona diagnósticos de exceção de memória: “quando o seu app ou extensão é encerrado por exceder o seu limite de memória, você obtém mais informações sobre o que aconteceu”.1 As extensões estão explicitamente no escopo, o que importa para quem depura kills de memória de widget ou de extensão à distância.
O consumo espelha o lado das métricas. Você aguarda diagnosticReports na sua instância de MetricManager, novamente a partir da inicialização do app em uma tarefa detached ou classe de serviço, e os valores de DiagnosticReport são Codable para o mesmo pipeline de codificar e enviar.1 Como os relatórios são estruturados, você pode fazer um switch sobre os casos de diagnóstico: o caso de crash entrega o backtrace, o motivo e a category, enquanto um caso de hang pode ser direcionado a um processamento diferente.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 um único número combinado à verdade por tela
Tudo acima ainda descreve telemetria de todo o app, e a telemetria de todo o app tem um teto. O app de relatório de despesas da sessão torna o problema concreto. O app organiza seus recursos em uma aba Reports e uma aba Spending. Ao longo de um dia, o MetricKit relata 4,5 segundos de tempo total de hitch em 5 minutos de rolagem: uma taxa de hitches de 15 ms/s. Mas esse número é “uma taxa média de hitches de rolagem em todo o uso do app, mesmo que alguém esteja indo e voltando entre a aba Reports e a aba Spending”.1 Você sabe que o app tem hitches. Você não sabe onde.
O novo framework StateReporting remove a combinação. Os estados são “informações que você define e que descrevem a configuração ou o comportamento do seu app, para que o MetricKit possa agregar métricas em função dessas características”.1 À medida que as pessoas se movem entre as abas, o app relata cada transição, e o MetricKit cruza esses estados com os dados de métricas e diagnósticos.1
A recompensa na demonstração é o momento que justifica a reconstrução. Em vez de um único valor combinado de 15 ms/s, as métricas chegam por estado: a aba Spending rolou “incrivelmente suave” a 1 ms/s, enquanto a aba Reports “disparou para 71 ms/s”.1 A sessão chega à conclusão que o número combinado nunca poderia sustentar: “a aba Spending está com performance ótima! Mas a aba Reports está sofrendo interrupções críticas, e é exatamente aí que o seu esforço de otimização deve se concentrar”.1 Um número virou um veredito e uma ordem de serviço.
Os estados seguem um modelo de transição, não de delimitação por pares. “Não há pares de início ou fim - o app relata a condição em que está, em qualquer momento dado”, e o MetricKit acompanha por quanto tempo o app permanece em cada estado.1
Domínios, metadados e codificação por estado
Cada estado é escopado a um domínio, que “descreve uma função ou área de um app”. Um domínio pode conter apenas um estado ativo por vez, e domínios separados permitem que vários estados estejam em andamento simultaneamente.1 O exemplo da sessão é um experimento A/B: com uma alteração experimental ativada, as despesas são buscadas no banco de dados em pequenos lotes; desativada, em lotes maiores. Colocar o estado da aba e o estado do tamanho do lote em domínios separados significa que “o MetricKit entregará métricas separadas para cada aba e cada tamanho de lote”.1 Telemetria por tela e leituras de experimentos do mesmo pipeline, em campo.
A adoção tem três passos na sessão: importe o framework StateReporting, crie um domínio (“normalmente uma string de DNS reverso”) e registre-o quando você configurar a sua instância de MetricManager, depois relate as transições à medida que o app entra em cada estado, como transicionar para um estado identificado pela string “Reports”.1 Para um grão mais fino, você define a sua própria struct com a macro ReportableMetadata, cria um StateReporter com esse tipo de metadados e relata as transições tanto com o rótulo quanto com o seu tipo personalizado. O exemplo ViewConfiguration da sessão carrega um valor listSize e se a lista está ordenada.1 Novamente: a sessão mostra esse fluxo em slides sem assinaturas completas, então trate o formato como algo a confirmar na documentação, e não como sintaxe a copiar.
No lado do recebimento, o relatório ganha um segundo eixo. Antes de qualquer estado ser relatado, a propriedade stateEntries no seu relatório de métricas fica vazia. Após a adoção, o relatório carrega valores StateEntry, cada um contendo “valores de métricas agregados ao longo do tempo gasto naquele estado individual”.1 Para o pipeline do servidor, você pode agrupar a saída codificada por domínio: defina a chave encodingFormatKey na propriedade userInfo do seu JSONEncoder como byStateReportingDomain, e o relatório codificado apresenta tanto as entradas de estado quanto as entradas de intervalo “agrupadas por cada domínio e estado que existe no relatório”.1
Boas práticas e por onde começar
A sessão encerra com orientações que se leem como conselhos de design de schema conquistados a duras penas. Os domínios devem ser escopados de forma estreita a uma área do app. As transições de estado “devem representar fases estáveis e significativas, não eventos transitórios de UI”.1 Projete cada estado de modo que, quando uma regressão aparecer, o estado sozinho lhe dê informação suficiente para direcionar a correção. E resista ao impulso de instrumentar tudo: “Estados demais podem resultar em dados granulares demais e podem, na verdade, dificultar a interpretação do quadro geral”, e existem limites superiores para o número de estados para minimizar a sobrecarga (a sessão não dá um número).1 Antes de enviar para produção, valide que os estados relatados correspondem às suas expectativas com o instrumento Points of Interest.1
A cardinalidade é a mesma armadilha do lado dos metadados. Agrupe valores de estado que mudam rápido em categorias amplas (pequeno, médio, grande) em vez de relatar contagens exatas. No lab de performance da WWDC 2026 da Apple, onde a equipe respondeu perguntas ao vivo sobre a adoção do MetricKit e o fluxo de energia mais amplo (registrado em o que a equipe de performance da Apple disse no lab da WWDC26), eles observaram que registrar “1.000 versus 1.001 itens” adiciona custo sem trazer insight: os dois valores caem no mesmo regime de performance, então um estado distinto para cada um compra sobrecarga e nada mais.4 Escolha os limites que mudam o comportamento e colapse tudo que está entre eles.
O lado da coleta é apenas metade do sistema. A sessão é direta ao dizer que “analisar métricas em todos os dispositivos é um problema de ciência de dados”: você monta um servidor que ingere relatórios, agrega ao longo das dimensões que interessam a você, estabelece uma baseline e monitora movimentos em qualquer direção.1 Os relatórios Codable e a codificação byStateReportingDomain existem para alimentar exatamente esse pipeline.
Para quem já adota o framework, a instrução final é explícita: “se você estiver usando a API MXMetricManager, migre para a nova API MetricManager para aproveitar todos esses novos recursos”.1 A documentação da Apple agora formaliza essa migração: o MXMetricManager está marcado como descontinuado a partir do 27.0, com a orientação “Use MetricManager instead”.2 A mesma imposição escalonada na fronteira do 27 apareceu em outros lugares neste ciclo, incluindo a descontinuação de ImageCreator no iOS 27 do Image Playground, onde um aviso de descontinuação dá lugar a uma quebra definitiva no lançamento público. As novas APIs são onde vive cada avanço da sessão, e a sessão as apresenta como “o futuro do framework”.1
FAQ
O que de fato mudou no MetricKit no iOS 27?
O framework foi reconstruído com uma API moderna e Swift-first. O ponto de entrada é a nova classe MetricManager; os relatórios de métricas e diagnósticos chegam como streams assíncronos aguardáveis (metricReports, diagnosticReports); os relatórios são Codable para codificação direta em JSON; e a estrutura é navegável em código via intervalEntries e grupos de métricas. O iOS 27 também adiciona uma métrica de taxa de quadros do Metal, diagnósticos de exceção de memória, uma category de crash que liga os diagnósticos de crash à contabilização das métricas e o framework StateReporting para métricas por estado.1
Como o StateReporting decide quais métricas pertencem a qual estado?
O seu app relata transições: o estado para o qual está se movendo, dentro de um domínio que você define. O MetricKit acompanha por quanto tempo o app permanece em cada estado e agrega os valores de métricas ao longo do tempo gasto ali. Não há pares de início/fim; o app simplesmente relata a condição em que está em qualquer momento dado. Cada estado então recebe o seu próprio StateEntry no relatório de métricas.1
Posso acompanhar mais de uma dimensão ao mesmo tempo, como tela e braço do experimento?
Sim. Cada domínio pode conter um estado ativo por vez, mas domínios separados rodam simultaneamente. O app de despesas da sessão coloca a aba ativa em um domínio e um experimento de tamanho de lote do banco de dados em outro, e o MetricKit entrega métricas separadas para cada aba e cada tamanho de lote.1
Devo relatar cada evento de UI como um estado?
Não. A sessão recomenda estados que representem fases estáveis e significativas em vez de eventos transitórios de UI, domínios escopados de forma estreita a uma área do app e moderação no geral: estados demais tornam os dados mais difíceis de interpretar, e o sistema impõe limites superiores ao número de estados para minimizar a sobrecarga. Valide os seus estados com o instrumento Points of Interest antes de enviar para produção.1
Preciso abandonar o MXMetricManager?
A orientação da sessão é migrar do MXMetricManager para a nova API MetricManager, porque cada novo recurso abordado (streams assíncronos, relatórios Codable, métricas sensíveis ao estado, os novos tipos de métricas e diagnósticos) é exclusivo do novo conjunto de APIs.1
O MetricKit é a metade de campo de uma história em duas partes neste ano: o Instruments mostra o hitch no laboratório, e o MetricKit sensível ao estado indica quais telas têm hitches para usuários reais, abordado pelo lado do laboratório em Instruments 27 e a responsividade do app. O trabalho de renderização que de fato corrige uma aba de 71 ms/s vive em Performance e interoperabilidade do SwiftUI no iOS 27. E o motivo pelo qual médias combinadas enganam em primeiro lugar é o tema de o ponto cego da performance. O hub completo da série é a Série Apple Ecosystem.
Referências
-
Apple, WWDC 2026 session 222, Meet the new MetricKit. Fonte para a reconstrução do zero no iOS 27 e o enquadramento de API Swift-first, o ponto de entrada
MetricManagere os streams assíncronosmetricReports/diagnosticReports, os relatóriosCodablee o uso deJSONEncoder,intervalEntriese grupos de métricas (.cpu,.memory,.display,.gpu,peakMemory), a métrica de taxa de quadros do Metal, os diagnósticos de exceção de memória, acategoryde encerramento de crash, o passo a passo do backtrace desubmitReport(), o frameworkStateReporting(domínios, modelo de transição,StateReporter,ReportableMetadata,stateEntries,byStateReportingDomainviaencodingFormatKey), os números da demonstração do app de despesas (15 ms/s combinado; 1 ms/s na aba Spending versus 71 ms/s na aba Reports), as boas práticas de estado e a validação com Points of Interest, e a orientação para migrar doMXMetricManagerpara oMetricManager. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Documentação para Desenvolvedores da Apple:
MXMetricManager. Marcado como descontinuado a partir do 27.0, com a orientação “Use MetricManager instead”. ↩ -
Documentação para Desenvolvedores da Apple: Reducing your app’s launch time. O tempo de inicialização é medido desde o momento em que o usuário toca no ícone do app até o primeiro quadro desenhado, antes de o processo do app existir. ↩
-
Apple, WWDC 2026 performance group lab, session 8003. Parafraseado a partir de uma gravação transcrita localmente; nenhuma transcrição oficial foi publicada. A equipe aconselhou agrupar metadados de estado que mudam rápido em categorias amplas, observando que registrar “1.000 versus 1.001 itens” adiciona custo sem trazer insight. ↩