MetricKit przebudowany: telemetria świadoma stanu w iOS 27
Demonstracyjna aplikacja Apple z WWDC 2026 raportowała współczynnik zacięć przewijania na poziomie 15 milisekund na sekundę, uśredniony dla całego dnia użytkowania. W rozbiciu na poszczególne karty te same dane opowiadały zupełnie inną historię: 1 ms/s na jednej karcie, 71 ms/s na drugiej.1 Jeden ekran był niemal bezbłędny; drugi, jak ujęto to w trakcie sesji, „doświadczał krytycznych zakłóceń”.1 Uśredniona liczba ukrywała oba te fakty. Sesja 222, „Meet the new MetricKit”, to opowieść o tym, jak iOS 27 likwiduje tę lukę: gruntowna przebudowa powierzchni API frameworka oraz nowy framework towarzyszący, StateReporting, który zamienia metryki polowe obejmujące całą aplikację w metryki dla poszczególnych stanów. Telemetria polowa może wreszcie odpowiedzieć na pytanie, które każdy inżynier wydajności zadaje jako pierwsze: który ekran jest wolny?
TL;DR
- W iOS 27 MetricKit „został przebudowany od podstaw z bogatym kontekstowo i ekspresyjnym, nowoczesnym API stawiającym na Swift”, a każda nowa funkcja omawiana w trakcie sesji jest dostępna wyłącznie w nowych API.1
- Punktem wejścia jest klasa
MetricManager. Aplikacje oczekują na asynchroniczne strumieniemetricReportsidiagnosticReportsprzy uruchomieniu, a oba typy raportów sąCodable, więcJSONEncoderwysyła je prosto na serwer analityczny.1 - Raporty mają strukturę:
intervalEntrieszawierają wpis obejmujący cały dzień wraz z drobniejszymi rozbiciami, zorganizowanymi w grupy metryk takie jak.cpu,.memory,.displayi.gpu, aż do pojedynczych wartości takich jakpeakMemory.1 - Nowe dane w iOS 27: metryka liczby klatek Metal dla wydajności renderowania, diagnostyka wyjątków pamięci dla zakończeń wynikających z przekroczenia limitu pamięci oraz
categoryawarii, która wiąże poszczególne diagnostyki awarii z trendami metryk.1 - Sztandarową funkcją jest framework
StateReporting: aplikacja raportuje stan, w jakim się znajduje (aktywna karta, wariant eksperymentu, konfiguracja widoku), a MetricKit agreguje metryki dla każdego stanu, zastępując jedną uśrednioną liczbę rozbiciem na poszczególne ekrany.1
Przebudowany od podstaw
Yonni, inżynier z zespołu MetricKit, przedstawia przebudowę w iOS 27, zaczynając od 1:23.
Zadanie MetricKit się nie zmieniło: jest on „elementem zbierającym dane” w przepływie pracy nad wydajnością, dostarczając dwa rodzaje danych. Metryki mówią, czy dany obszar wydajności ogólnie się poprawia, czy pogarsza; diagnostyka wskazuje, która ścieżka kodu spowodowała problem.1 Zmieniło się natomiast wszystko w sposobie, w jaki te dane otrzymujemy. Sesja stawia sprawę jasno: w iOS 27 framework „został przebudowany od podstaw z bogatym kontekstowo i ekspresyjnym, nowoczesnym API stawiającym na Swift”, a „wszystkie postępy, które dziś omówię, są dostępne wyłącznie w tym nowym zestawie API”.1
Nowym punktem wejścia jest klasa MetricManager. Zamiast rejestrować delegata i parsować ładunki danych, oczekuje się na raporty poprzez właściwość metricReports jako strumień asynchroniczny. Dwie zasady operacyjne pochodzą wprost z sesji: konfigurację należy wykonać przy uruchomieniu aplikacji, „aby uniknąć utraty danych spowodowanej opóźnioną subskrypcją”, oraz utrzymywać MetricManager przy życiu, „tak aby strumienie mogły nadal dostarczać raporty w miarę gotowości kolejnych danych”.1 Apple zaleca uruchomienie tej pracy w odłączonym zadaniu lub dedykowanej klasie usługowej, gdy tylko aplikacja zostanie uruchomiona.1
Sesja prezentuje kod na slajdach, więc poniższe fragmenty to poglądowe kształty wywołań odpowiadające jej opisowi; przed wdrożeniem należy zweryfikować dokładne sygnatury w dokumentacji Apple.
// 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.
}
}
Wysłanie raportu na serwer kiedyś oznaczało obsługę nieprzejrzystych danych ładunku. Teraz wartości MetricReport są Codable: „Wystarczy utworzyć JSONEncoder i zakodować cały raport”.1 Jeśli zamiast całego dokumentu potrzebna jest konkretna wartość, raport ma pełną strukturę. Iteruje się po intervalEntries, które „zawierają zagregowany wpis dla całego dnia oraz mniejsze okna rozbicia, gdy są dostępne”, zwykle po kilka godzin każde i obecne tylko wtedy, gdy istnieją dla nich metryki.1 Wewnątrz każdego interwału metryki są zorganizowane w grupy metryk, gdzie „każda grupa reprezentuje aspekt systemu, rzeczy takie jak .cpu, .memory, .display i .gpu”.1 Należy zawęzić do interesującej nas grupy (przykład z sesji wyciąga memoryMetrics), a następnie przełączać się między przypadkami metryk, aby dotrzeć do pojedynczej wartości takiej jak peakMemory.1
Katalog metryk również rośnie w iOS 27. Obok histogramów czasu uruchamiania (przykład z sesji pokazuje większość uruchomień mieszczących się między 510 a 540 milisekundami), zawieszeń, metryk animacji oraz zużycia zasobów takich jak CPU, GPU, zapisy na dysk i transfery sieciowe, MetricKit dodaje metrykę liczby klatek Metal. Sesja nazywa liczbę klatek „kluczową metryką dla twórców gier do zrozumienia wydajności renderowania” i odsyła do „Find and fix performance issues in your Metal game” po stronie optymalizacji.1
Warto opierać się na metryce uruchamiania z MetricKit, zamiast samodzielnie instrumentować uruchamianie. Apple mierzy czas uruchamiania od momentu dotknięcia ikony aplikacji przez użytkownika do narysowania pierwszej klatki, co następuje zanim proces w ogóle zaistnieje.3 Ręcznie napisany licznik może wystartować dopiero wtedy, gdy uruchomi się nasz kod, więc całkowicie pomija okno przed-main i zaniża rzeczywiście odczuwane przez użytkowników uruchomienie. Należy odczytywać histogram dostarczany przez MetricKit, zamiast budować licznik, który nie widzi najważniejszej części.
Diagnostyka: ślady wsteczne, wyjątki pamięci i kategorie awarii
Metryki mówią, że coś uległo regresji; diagnostyka mówi gdzie. Gdy coś pójdzie nie tak, na przykład awaria lub zawieszenie, „system przechwytuje diagnostykę na urządzeniu”, a raport diagnostyczny „pakuje szczegóły i natychmiast dostarcza je do aplikacji za pośrednictwem MetricKit”.1 Wiele diagnostyk zawiera ślady wsteczne pokazujące dokładny stos wywołań w momencie zdarzenia. W przejściu prezentowanym w sesji zsymbolizowany ślad wsteczny zaczyna się od startu wątku w kodzie systemowym, przechodzi do aplikacji i zatrzymuje się na funkcji submitReport() aplikacji, która wyznacza punkt awarii i miejsce, w które należy wymierzyć poprawkę.1
Diagnostyka awarii niesie ze sobą ślad wsteczny, przyczynę zakończenia oraz typ wyjątku. Nowość w iOS 27: category zakończenia „wskazuje, jak każda awaria została uwzględniona w metrykach”, więc „jeśli liczba nietypowych zakończeń rośnie, można je bezpośrednio skorelować z poszczególnymi diagnostykami”.1 Linia metryki na pulpicie i stojące za nią pojedyncze raporty awarii wreszcie dzielą wspólny klucz.
iOS 27 dodaje również diagnostykę wyjątków pamięci: „gdy aplikacja lub rozszerzenie zostaje zakończone z powodu przekroczenia limitu pamięci, otrzymuje się więcej wglądu w to, co się stało”.1 Rozszerzenia są wyraźnie objęte zakresem, co ma znaczenie dla każdego, kto debuguje zdalnie wymuszone zakończenia pamięci widżetów lub rozszerzeń.
Konsumpcja odzwierciedla stronę metryk. Oczekuje się na diagnosticReports w instancji MetricManager, ponownie od uruchomienia aplikacji w odłączonym zadaniu lub klasie usługowej, a wartości DiagnosticReport są Codable na potrzeby tego samego potoku kodowania i wysyłki.1 Ponieważ raporty mają strukturę, można przełączać się między przypadkami diagnostyki: przypadek awarii zwraca ślad wsteczny, przyczynę i category, podczas gdy przypadek zawieszenia może być kierowany do innego przetwarzania.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: od jednej uśrednionej liczby do prawdy o każdym ekranie
Wszystko powyżej wciąż opisuje telemetrię obejmującą całą aplikację, a taka telemetria ma swój pułap. Aplikacja do rozliczania wydatków z sesji konkretyzuje ten problem. Aplikacja organizuje swoje funkcje w kartę Reports i kartę Spending. W ciągu dnia MetricKit raportuje 4,5 sekundy łącznego czasu zacięć w ciągu 5 minut przewijania: współczynnik zacięć 15 ms/s. Ta liczba jest jednak „średnim współczynnikiem zacięć przewijania dla całego użytkowania aplikacji, nawet jeśli ktoś przechodzi tam i z powrotem między kartą Reports a kartą Spending”.1 Wiadomo, że aplikacja się zacina. Nie wiadomo gdzie.
Nowy framework StateReporting usuwa uśrednianie. Stany to „informacje, które definiujesz, opisujące konfigurację lub zachowanie aplikacji, tak aby MetricKit mógł agregować metryki jako funkcję tych cech”.1 Gdy ludzie przemieszczają się między kartami, aplikacja raportuje każde przejście, a MetricKit przecina te stany z danymi metryk i diagnostyki.1
Korzyść w demonstracji to moment, który uzasadnia przebudowę. Zamiast jednej uśrednionej wartości 15 ms/s, metryki przychodzą dla każdego stanu: karta Spending przewijała się „niewiarygodnie płynnie” na poziomie 1 ms/s, podczas gdy karta Reports „skoczyła do 71 ms/s”.1 Sesja wyciąga wniosek, którego uśredniona liczba nigdy nie mogłaby udźwignąć: „karta Spending działa świetnie! Ale karta Reports doświadcza krytycznych zakłóceń i to właśnie tam powinien skupić się Twój wysiłek optymalizacyjny”.1 Jedna liczba stała się werdyktem i zleceniem pracy.
Stany podążają za modelem przejść, a nie nawiasowania. „Nie ma par start/koniec — aplikacja raportuje stan, w którym się znajduje, w dowolnym momencie”, a MetricKit śledzi, jak długo aplikacja pozostaje w każdym stanie.1
Domeny, metadane i kodowanie według stanu
Każdy stan jest przypisany do domeny, która „opisuje funkcję lub obszar aplikacji”. Domena może przechowywać tylko jeden aktywny stan naraz, a oddzielne domeny pozwalają wielu stanom być aktywnymi jednocześnie.1 Przykładem z sesji jest eksperyment A/B: przy włączonej zmianie eksperymentalnej wydatki są pobierane z bazy danych w małych partiach; przy wyłączonej — w większych partiach. Umieszczenie stanu karty i stanu rozmiaru partii w oddzielnych domenach oznacza, że „MetricKit dostarczy oddzielne metryki dla każdej karty i każdego rozmiaru partii”.1 Telemetria dla każdego ekranu i odczyty eksperymentu z tego samego potoku, w warunkach polowych.
Adopcja składa się w sesji z trzech kroków: zaimportować framework StateReporting, utworzyć domenę („zwykle łańcuch w odwrotnej notacji DNS”) i zarejestrować ją przy konfiguracji instancji MetricManager, a następnie raportować przejścia, gdy aplikacja wchodzi w każdy stan, jak na przykład przejście do stanu identyfikowanego łańcuchem „Reports”.1 Dla większej szczegółowości definiuje się własną strukturę z makrem ReportableMetadata, tworzy się StateReporter z tym typem metadanych i raportuje przejścia zarówno z etykietą, jak i z własnym typem. Przykład ViewConfiguration z sesji niesie wartość listSize oraz informację o tym, czy lista jest posortowana.1 Ponownie: sesja pokazuje ten przepływ na slajdach bez pełnych sygnatur, więc kształt należy traktować jako coś do potwierdzenia w dokumentacji, a nie jako składnię do skopiowania.
Po stronie odbiorczej raport zyskuje drugą oś. Zanim jakiekolwiek stany zostaną zaraportowane, właściwość stateEntries w raporcie metryk jest pusta. Po adopcji raport niesie wartości StateEntry, z których każda przechowuje „wartości metryk zagregowane dla czasu spędzonego w tym pojedynczym stanie”.1 Na potrzeby potoku serwerowego można grupować zakodowane dane wyjściowe według domeny: ustawić klucz encodingFormatKey we właściwości userInfo obiektu JSONEncoder na byStateReportingDomain, a zakodowany raport zaprezentuje zarówno wpisy stanów, jak i wpisy interwałów „pogrupowane według każdej domeny i stanu obecnych w raporcie”.1
Najlepsze praktyki i od czego zacząć
Sesja kończy się wskazówkami, które brzmią jak okupiona trudem rada dotycząca projektowania schematu. Domeny powinny być wąsko ograniczone do jednego obszaru aplikacji. Przejścia stanów „powinny reprezentować stabilne, znaczące fazy, a nie ulotne zdarzenia UI”.1 Każdy stan należy zaprojektować tak, aby gdy pojawi się regresja, sam stan dawał wystarczająco dużo informacji do wymierzenia poprawki. I warto oprzeć się pokusie instrumentowania wszystkiego: „Zbyt wiele stanów może skutkować danymi, które są nadmiernie szczegółowe i w rzeczywistości mogą utrudnić interpretację ogólnego obrazu”, a górne limity liczby stanów istnieją po to, by zminimalizować narzut (sesja nie podaje konkretnej liczby).1 Przed wdrożeniem warto zweryfikować za pomocą instrumentu Points of Interest, że raportowane stany odpowiadają oczekiwaniom.1
Liczność to ta sama pułapka po stronie metadanych. Szybko zmieniające się wartości stanów należy grupować w grube kategorie (małe, średnie, duże), zamiast raportować dokładne liczby. W laboratorium wydajności Apple na WWDC 2026, gdzie zespół odpowiadał na żywo na pytania dotyczące adopcji MetricKit i szerszego przepływu pracy nad poborem energii (uchwycone w tym, co zespół wydajności Apple powiedział w laboratorium WWDC26), zwrócono uwagę, że zapisywanie „1000 kontra 1001 elementów” dodaje koszt bez wglądu: obie wartości lądują w tym samym reżimie wydajnościowym, więc osobny stan dla każdej z nich kupuje narzut i nic poza tym.4 Należy wybrać granice, które zmieniają zachowanie, i zwinąć wszystko pomiędzy nimi.
Strona zbierania to tylko połowa systemu. Sesja stawia sprawę wprost: „analizowanie metryk na wszystkich urządzeniach to problem z dziedziny data science”: stawia się serwer, który przyjmuje raporty, agreguje się je wzdłuż interesujących nas wymiarów, ustanawia się punkt odniesienia i monitoruje ruch w dowolnym kierunku.1 Raporty Codable i kodowanie byStateReportingDomain istnieją po to, by zasilać dokładnie taki potok.
Dla obecnych użytkowników końcowa instrukcja jest jednoznaczna: „jeśli używasz API MXMetricManager, przejdź na nowe API MetricManager, aby skorzystać ze wszystkich tych nowych możliwości”.1 Dokumentacja Apple formalizuje teraz tę migrację: MXMetricManager jest oznaczony jako przestarzały od wersji 27.0, ze wskazówką „Use MetricManager instead”.2 To samo stopniowe egzekwowanie granicy 27 pojawiło się w tym cyklu również gdzie indziej, w tym w wycofaniu ImageCreator w iOS 27 w Image Playground, gdzie ostrzeżenie o przestarzałości ustępuje miejsca twardemu przerwaniu w publicznym wydaniu. Nowe API to miejsce, w którym żyje każdy postęp z sesji, a sesja prezentuje je jako „przyszłość frameworka”.1
FAQ
Co właściwie zmieniło się w MetricKit w iOS 27?
Framework został przebudowany z nowoczesnym API stawiającym na Swift. Punktem wejścia jest nowa klasa MetricManager; raporty metryk i diagnostyki przychodzą jako oczekiwalne strumienie asynchroniczne (metricReports, diagnosticReports); raporty są Codable na potrzeby bezpośredniego kodowania JSON; a struktura jest nawigowalna w kodzie poprzez intervalEntries i grupy metryk. iOS 27 dodaje również metrykę liczby klatek Metal, diagnostykę wyjątków pamięci, category awarii, która wiąże diagnostykę awarii z rozliczeniem metryk, oraz framework StateReporting dla metryk dla poszczególnych stanów.1
Jak StateReporting decyduje, które metryki należą do którego stanu?
Aplikacja raportuje przejścia: stan, do którego się przemieszcza, w obrębie zdefiniowanej przez Ciebie domeny. MetricKit śledzi, jak długo aplikacja pozostaje w każdym stanie, i agreguje wartości metryk dla czasu tam spędzonego. Nie ma par start/koniec; aplikacja po prostu raportuje stan, w którym się znajduje w dowolnym momencie. Każdy stan otrzymuje następnie własny StateEntry w raporcie metryk.1
Czy mogę śledzić więcej niż jeden wymiar naraz, na przykład ekran i wariant eksperymentu?
Tak. Każda domena może przechowywać jeden aktywny stan naraz, ale oddzielne domeny działają równolegle. Aplikacja do wydatków z sesji umieszcza aktywną kartę w jednej domenie, a eksperyment z rozmiarem partii bazy danych w drugiej, a MetricKit dostarcza oddzielne metryki dla każdej karty i każdego rozmiaru partii.1
Czy powinienem raportować każde zdarzenie UI jako stan?
Nie. Sesja zaleca stany reprezentujące stabilne, znaczące fazy, a nie ulotne zdarzenia UI, domeny wąsko ograniczone do jednego obszaru aplikacji oraz ogólną powściągliwość: zbyt wiele stanów utrudnia interpretację danych, a system nakłada górne limity na liczbę stanów, aby zminimalizować narzut. Stany należy zweryfikować instrumentem Points of Interest przed wdrożeniem.1
Czy muszę przejść z MXMetricManager?
Wskazówka z sesji to migracja z MXMetricManager na nowe API MetricManager, ponieważ każda omówiona nowa możliwość (strumienie asynchroniczne, raporty Codable, metryki świadome stanu, nowe typy metryk i diagnostyki) jest dostępna wyłącznie w nowym zestawie API.1
MetricKit to polowa połowa dwuczęściowej historii tego roku: Instruments pokazuje zacięcie w laboratorium, a MetricKit świadomy stanu mówi, które ekrany zacinają się dla rzeczywistych użytkowników, co od strony laboratoryjnej zostało omówione w Instruments 27 a responsywność aplikacji. Praca nad renderowaniem, która faktycznie naprawia kartę z 71 ms/s, znajduje się w Wydajność SwiftUI i współdziałanie w iOS 27. A powód, dla którego uśrednione średnie wprowadzają w błąd już od samego początku, jest tematem martwego punktu wydajności. Pełnym centrum serii jest Apple Ecosystem Series.
Bibliografia
-
Apple, WWDC 2026 session 222, Meet the new MetricKit. Source for the iOS 27 ground-up rebuild and Swift-first API framing, the
MetricManagerentry point and themetricReports/diagnosticReportsasync streams,Codablereports andJSONEncoderusage,intervalEntriesand metric groups (.cpu,.memory,.display,.gpu,peakMemory), the Metal frame rate metric, memory exception diagnostics, the crash terminationcategory, thesubmitReport()backtrace walkthrough, theStateReportingframework (domains, transition model,StateReporter,ReportableMetadata,stateEntries,byStateReportingDomainviaencodingFormatKey), the expense-app demo numbers (15 ms/s blended; 1 ms/s Spending tab versus 71 ms/s Reports tab), the state best practices and Points of Interest validation, and the guidance to migrate fromMXMetricManagertoMetricManager. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation:
MXMetricManager. Marked deprecated as of 27.0, with the guidance “Use MetricManager instead.” ↩ -
Apple Developer Documentation: Reducing your app’s launch time. Launch time is measured from the moment the user taps the app icon to the first frame drawn, before the app’s process exists. ↩
-
Apple, WWDC 2026 performance group lab, session 8003. Paraphrased from a locally transcribed recording; no official transcript is published. The team advised bucketing fast-changing state metadata into coarse categories, noting that recording “1,000 versus 1,001 items” adds cost without insight. ↩