MetricKit 全面重建:iOS 27 中具狀態感知的遙測
Apple 的 WWDC 2026 示範 app 回報的滑動卡頓率為每秒 15 毫秒,這是整天使用情況的平均值。若依分頁拆開來看,同一份資料卻訴說著截然不同的故事:一個分頁是 1 ms/s,另一個則是 71 ms/s。1一個畫面幾近完美無瑕;另一個則用議程的話來說,正「經歷嚴重的中斷」。1混合後的數字同時掩蓋了這兩項事實。議程 222「Meet the new MetricKit」講述的,正是 iOS 27 如何弭平這道落差:從頭重新打造這套框架的 API 介面,並推出一套全新的搭配框架 StateReporting,把整個 app 的現場指標轉化為依狀態區分的指標。現場遙測終於能回答每位效能工程師最先想問的問題:到底是哪個畫面慢?
TL;DR
- 在 iOS 27 中,MetricKit「已從頭重新打造,提供脈絡豐富且富表達力的現代 Swift 優先 API」,而議程中介紹的每一項新功能都是新 API 獨有的。1
- 進入點是
MetricManager類別。App 在啟動時 awaitmetricReports與diagnosticReports這兩個非同步串流,而兩種報告型別都是Codable,因此用一個JSONEncoder就能直接把它們送往您的分析伺服器。1 - 報告是結構化的:
intervalEntries包含一筆全天的條目外加數筆較小的細分區段,並組織成.cpu、.memory、.display、.gpu等 metric 群組,最終可深入到peakMemory這類個別數值。1 - iOS 27 的新資料:用於了解算繪效能的 Metal 影格率 metric、用於記憶體上限終止情況的記憶體例外 diagnostic,以及把個別當機 diagnostic 連回您 metric 趨勢的當機
category。1 - 最受矚目的功能是
StateReporting框架:回報您的 app 所處的狀態(作用中的分頁、實驗組別、檢視設定),MetricKit 便會依狀態彙整指標,把單一的混合數字替換成依畫面區分的細目。1
從頭重新打造
MetricKit 團隊的工程師 Yonni 從 1:23 起介紹 iOS 27 的這次重建。
MetricKit 的任務並未改變:它是效能工作流程中的「收集環節」,提供兩種資料。Metric 告訴您某個效能面向整體上是在改善還是惡化;diagnostic 則告訴您是哪段程式碼路徑造成了問題。1改變的是您接收這些資料的一切方式。議程說得很直白:在 iOS 27 中,這套框架「已從頭重新打造,提供脈絡豐富且富表達力的現代 Swift 優先 API」,而且「我今天要談的所有進展,都是這套全新 API 獨有的」。1
新的進入點是 MetricManager 類別。您不再需要註冊一個 delegate 並剖析 payload,而是透過 metricReports 屬性以非同步串流的方式 await 報告。兩條操作原則直接出自議程:在 app 啟動時就完成設定,「以避免因延遲訂閱而造成任何資料遺失」,並讓 MetricManager 保持存活,「如此串流才能在後續資料就緒時持續傳遞報告」。1Apple 建議在 app 一啟動時,就在一個 detached task 或專屬的 service 類別中執行這項工作。1
議程是以投影片呈現程式碼,因此以下片段是符合其描述的示意呼叫形式;正式出貨前,請對照 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.
}
}
過去要把報告送往您的伺服器,意味著得處理不透明的 payload 資料。如今 MetricReport 值都是 Codable:「只要建立一個 JSONEncoder 並編碼整份報告即可。」1如果您想要的是某個特定數值而非整份文件,這份報告也是完全結構化的。您可以走訪 intervalEntries,它「包含一筆全天彙整的條目,以及在可取得時提供的較小細分區段」,通常每段約數小時,且僅在該區段存在指標時才會出現。1在每個 interval 之內,指標會組織成 metric 群組,其中「每個群組代表系統的一個面向,例如 .cpu、.memory、.display 與 .gpu」。1篩選到您關注的群組(議程的範例取出的是 memoryMetrics),再對 metric 的各個 case 進行 switch,即可取得 peakMemory 這樣的個別數值。1
iOS 27 的 metric 目錄也有所擴充。除了啟動時間直方圖(議程的範例顯示多數啟動落在 510 到 540 毫秒之間)、卡頓、動畫指標,以及 CPU、GPU、磁碟寫入與網路傳輸等資源消耗外,MetricKit 還新增了一個 Metal 影格率 metric。議程稱影格率是「遊戲開發者了解算繪效能的關鍵 metric」,並指引讀者參閱「Find and fix performance issues in your Metal game」以了解最佳化的一面。1
請善用 MetricKit 的啟動 metric,而非自行對啟動進行檢測。Apple 測量啟動時間的起點,是使用者點按 app 圖示的那一刻,直到第一個影格繪製出來為止,而這甚至發生在您的行程還不存在之前。3手刻的計時器只能在您的程式碼開始執行後才啟動,因此它完全錯過了 main 之前的那段時間,進而低估了使用者實際感受到的啟動時間。請改為閱讀 MetricKit 提供的直方圖,而不要打造一個看不見關鍵環節的計時器。
Diagnostic:回溯追蹤、記憶體例外與當機分類
Metric 告訴您某處退步了;diagnostic 則告訴您是在哪裡。當發生問題時,例如當機或卡頓,「系統會在裝置上擷取一份 diagnostic」,而一份 diagnostic 報告會「把細節打包,並透過 MetricKit 立即傳遞給您的 app」。1許多 diagnostic 都包含 backtrace,顯示事件發生當下的確切呼叫堆疊。在議程的逐步演示中,符號化後的 backtrace 從系統程式碼中的執行緒起點開始,跨入 app,並停在 app 的 submitReport() 函式,而這正標示出失敗的位置,也是修正應鎖定之處。1
當機 diagnostic 帶有 backtrace、終止原因與例外型別。iOS 27 新增了終止 category,它「指出每次當機在指標中是如何被計入的」,因此「如果異常終止呈上升趨勢,您就能把它們直接與個別 diagnostic 相互對照」。1您儀表板上的那條 metric 線,與其背後的個別當機報告,終於共享了一把鑰匙。
iOS 27 也新增了記憶體例外 diagnostic:「當您的 app 或 extension 因超出記憶體上限而被終止時,您可以對發生了什麼事獲得更多洞察。」1Extension 明確被納入範圍,這對任何需要遠端除錯 widget 或 extension 記憶體終止情況的人來說都很重要。
消費端與 metric 端如出一轍。您在 MetricManager 實例上 await diagnosticReports,同樣在 app 啟動時於 detached task 或 service 類別中進行,而 DiagnosticReport 值也是 Codable,適用同一條編碼並送出的管線。1由於報告是結構化的,您可以對 diagnostic 的各個 case 進行 switch:當機 case 會產生 backtrace、原因與 category,而卡頓 case 則可導向不同的處理流程。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:從單一混合數字到依畫面區分的真相
以上所述仍是在描述整個 app 的遙測,而整個 app 的遙測有其天花板。議程中那款費用報銷 app 把問題具體化了。這款 app 把功能分成一個 Reports 分頁與一個 Spending 分頁。一整天下來,MetricKit 在 5 分鐘的滑動中回報了總計 4.5 秒的卡頓時間:卡頓率為 15 ms/s。但這個數字是「涵蓋所有 app 使用情況的平均滑動卡頓率,即便有人正在 Reports 分頁與 Spending 分頁之間來回切換」。1您知道 app 會卡頓,卻不知道卡在哪裡。
全新的 StateReporting 框架消除了這種混合。狀態是「您所定義、用以描述 app 設定或行為的資訊,讓 MetricKit 能依這些特性彙整指標」。1當人們在分頁之間移動時,app 會回報每一次轉換,而 MetricKit 則把這些狀態與 metric 及 diagnostic 資料交集起來。1
示範中的回報成果,正是足以證明這場重新打造價值的時刻。指標不再是單一的混合 15 ms/s 數字,而是依狀態送達:Spending 分頁滑動起來「順暢得不可思議」,僅 1 ms/s,而 Reports 分頁則「飆升至 71 ms/s」。1議程得出了那個混合數字永遠無法支撐的結論:「Spending 分頁表現得很棒!但 Reports 分頁正經歷嚴重的中斷,而這正是您的最佳化努力應當聚焦之處。」1一個數字化為一份判決,也化為一張工單。
狀態遵循的是轉換模型,而非成對括住。「沒有起點或終點的配對——app 回報的是它在任一時間點所處的狀態」,而 MetricKit 會追蹤 app 停留在每個狀態的時間長度。1
領域、metadata 與依狀態編碼
每個狀態都歸屬於某個 domain,而 domain「描述 app 的某項功能或某個區域」。一個 domain 在同一時間只能持有一個作用中的狀態,而不同的 domain 則讓多個狀態能同時並行。1議程的範例是一項 A/B 實驗:當實驗性的變更開啟時,費用會以小批次從資料庫擷取;關閉時,則為較大的批次。把分頁狀態與批次大小狀態放在不同的 domain 中,意味著「MetricKit 會為每個分頁與每種批次大小分別傳遞指標」。1依畫面區分的遙測,以及來自同一條管線的實驗讀數,皆在現場取得。
議程中的採用分為三個步驟:匯入 StateReporting 框架,建立一個 domain(「通常是一段反向 DNS 字串」)並在設定 MetricManager 實例時加以註冊,再於 app 進入每個狀態時回報轉換,例如轉換到一個以字串「Reports」標識的狀態。1若需更細的粒度,您可以用 ReportableMetadata 巨集定義自己的 struct,以該 metadata 型別建立一個 StateReporter,並在回報轉換時同時帶上標籤與您的自訂型別。議程的 ViewConfiguration 範例帶有一個 listSize 值,以及該清單是否已排序的資訊。1同樣地:議程是在投影片上展示這個流程而未附上完整簽章,因此請把這個形式視為需要在文件中確認的東西,而非可直接照抄的語法。
在接收端,報告增添了第二個軸向。在回報任何狀態之前,您 metric 報告上的 stateEntries 屬性是空的。採用之後,報告會帶有 StateEntry 值,每一筆都持有「橫跨停留在該個別狀態之時間所彙整的指標數值」。1對於伺服器管線,您可以把編碼後的輸出依 domain 分組:在您 JSONEncoder 的 userInfo 屬性上把 encodingFormatKey 鍵設為 byStateReportingDomain,編碼後的報告便會同時呈現 state entries 與 interval entries,且「依報告中存在的每個 domain 與狀態分組」。1
最佳實務,以及該從何處著手
議程在收尾時給出的指引,讀來宛如得來不易的 schema 設計建言。Domain 應緊密地限縮在單一 app 區域。狀態轉換「應代表穩定、有意義的階段,而非短暫的 UI 事件」。1請把每個狀態設計成:當退步出現時,光憑該狀態就能給您足夠的資訊來鎖定修正。並請克制把一切都拿來檢測的衝動:「過多的狀態可能導致資料過於細碎,反而讓人更難解讀整體樣貌」,而狀態數量設有上限,正是為了將額外負擔降到最低(議程並未給出確切數字)。1出貨前,請用 Points of Interest instrument 驗證所回報的狀態符合您的預期。1
基數(cardinality)是 metadata 端同樣的陷阱。請把快速變動的狀態數值歸入較粗的類別(小、中、大),而非回報精確的計數。在 Apple 的 WWDC 2026 效能實驗室,團隊現場回答了關於 MetricKit 採用以及更廣泛功耗工作流程的提問(收錄於Apple 效能團隊在 WWDC26 實驗室所說的內容),他們指出記錄「1,000 對比 1,001 個項目」只會增加成本卻無洞察可言:這兩個數值落在同一個效能區間,因此為每一個都設立一個獨立狀態,買到的只有額外負擔而別無其他。4請挑出會改變行為的邊界,並把其間的一切都收攏起來。
收集端只占了系統的一半。議程直言不諱地指出「分析所有裝置上的指標是一個資料科學問題」:您要架起一台伺服器來吸收報告,沿著您關注的維度進行彙整,建立基準,並監看任一方向的變動。1Codable 報告與 byStateReportingDomain 編碼的存在,正是為了餵養這樣一條管線。
對於既有的採用者,收尾的指示十分明確:「如果您正在使用 MXMetricManager API,請遷移到新的 MetricManager API,以善用所有這些新功能。」1Apple 的文件如今已將這項遷移正式化:MXMetricManager 自 27.0 起被標示為 deprecated,並附上「Use MetricManager instead」的指引。2本週期同樣分階段、以 27 為界線的強制做法也出現在其他地方,包括 Image Playground 的 ImageCreator 在 iOS 27 中被棄用,在那裡,棄用警告會在正式公開發佈時轉為硬性中斷。新的 API 是議程中每一項進展的所在,而議程把它們呈現為「這套框架的未來」。1
FAQ
MetricKit 在 iOS 27 中究竟有何改變?
這套框架已用現代 Swift 優先的 API 重新打造。進入點是全新的 MetricManager 類別;metric 與 diagnostic 報告以可 await 的非同步串流(metricReports、diagnosticReports)送達;報告都是 Codable,可直接進行 JSON 編碼;而其結構也可透過 intervalEntries 與 metric 群組在程式碼中走訪。iOS 27 還新增了 Metal 影格率 metric、記憶體例外 diagnostic、把當機 diagnostic 連結到指標計入的當機 category,以及用於依狀態區分指標的 StateReporting 框架。1
StateReporting 如何決定哪些指標歸屬於哪個狀態?
您的 app 回報轉換:它正轉移到的狀態,以及這發生在您所定義的某個 domain 之內。MetricKit 會追蹤 app 停留在每個狀態的時間長度,並把該段時間內的指標數值彙整起來。沒有起點/終點的配對;app 只是回報它在任一時間點所處的狀態。隨後,每個狀態都會在 metric 報告中獲得自己的 StateEntry。1
我能同時追蹤不只一個維度嗎,例如畫面與實驗組別?
可以。每個 domain 在同一時間只能持有一個作用中的狀態,但不同的 domain 可以並行運作。議程的費用 app 把作用中的分頁放在一個 domain,把資料庫批次大小實驗放在另一個,而 MetricKit 便為每個分頁與每種批次大小分別傳遞指標。1
我該把每個 UI 事件都回報為一個狀態嗎?
不該。議程建議採用代表穩定、有意義階段的狀態,而非短暫的 UI 事件;domain 應緊密限縮在單一 app 區域;整體上更要有所節制:過多的狀態會讓資料更難解讀,而系統也對狀態數量設有上限以將額外負擔降到最低。出貨前,請用 Points of Interest instrument 驗證您的狀態。1
我必須捨棄 MXMetricManager 嗎?
議程的指引是從 MXMetricManager 遷移到新的 MetricManager API,因為所涵蓋的每一項新功能(非同步串流、Codable 報告、具狀態感知的指標、新的 metric 與 diagnostic 型別)都是新 API 組合所獨有的。1
MetricKit 是今年這個兩部曲故事中的現場那一半:Instruments 在實驗室裡讓您看見卡頓,而具狀態感知的 MetricKit 則告訴您真實使用者究竟在哪些畫面遇到卡頓,實驗室端的內容請參閱 Instruments 27 與 app 回應性。真正能修好一個 71 ms/s 分頁的算繪工作,則在 iOS 27 的 SwiftUI 效能與互通。而混合平均之所以一開始就會誤導人的原因,正是 效能盲點 所探討的主題。完整的系列專區是 Apple 生態系系列。
參考資料
-
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. ↩