← 所有文章

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 在啟動時 await metricReportsdiagnosticReports 兩個非同步串流,而這兩種報告型別都符合 Codable,因此用一個 JSONEncoder 就能直接把它們送到您的分析伺服器。1
  • 報告是結構化的:intervalEntries 包含一筆整日的項目,以及較小的細分區間,並依 .cpu.memory.display.gpu 等指標群組來組織,最終可深入到 peakMemory 這類個別數值。1
  • iOS 27 的新資料:用於量測算繪效能的 Metal 影格率指標、針對超出記憶體上限而終止的記憶體例外診斷,以及一個把個別當機診斷與您的指標趨勢串連起來的當機 category1
  • 最受矚目的功能是 StateReporting 框架:回報您 App 所處的狀態(作用中的分頁、實驗組別、檢視配置),MetricKit 便會依狀態彙整指標,把單一的混合數字換成依畫面區分的細目。1

從頭重建

觀看:Meet the new MetricKit (WWDC26)

MetricKit 團隊的工程師 Yonni 從 1:23 開始介紹 iOS 27 的重建。

MetricKit 的任務並未改變:它是效能工作流程中「負責蒐集的部分」,提供兩類資料。指標告訴您某個效能面向整體上是在改善還是惡化;診斷則告訴您是哪條程式碼路徑造成了問題。1改變的是您接收這些資料的整套方式。該場次說得很直接:在 iOS 27 中,這個框架「已從頭重建,採用情境豐富、表達力強的現代 Swift 優先 API」,而且「我今天要談的所有進展,都是這套全新 API 獨有的」。1

新的進入點是 MetricManager 類別。您不再需要註冊 delegate 並解析 payload,而是透過 metricReports 屬性以非同步串流的形式 await 報告。有兩條操作原則直接出自該場次:在 App 啟動時就完成設定,「以避免因延遲訂閱而造成任何資料遺失」,並讓 MetricManager 保持存活,「如此串流才能在後續資料就緒時持續傳遞報告」。1Apple 建議在 App 一啟動時,就用一個 detached task 或專屬的服務類別來執行這些工作。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在每個區間內,指標都依指標群組組織,其中「每個群組代表系統的一個面向,例如 .cpu.memory.display.gpu」。1篩選到您關心的群組(該場次的範例取出 memoryMetrics),再對各指標 case 進行 switch,便能取得 peakMemory 這類個別數值。1

iOS 27 的指標目錄也在擴充。除了啟動時間直方圖(該場次範例顯示大多數啟動落在 510 到 540 毫秒之間)、卡頓、動畫指標,以及 CPU、GPU、磁碟寫入、網路傳輸等資源耗用之外,MetricKit 還新增了 Metal 影格率指標。該場次稱影格率是「遊戲開發者理解算繪效能的關鍵指標」,並在最佳化方面指向「Find and fix performance issues in your Metal game」。1

診斷:回溯追蹤、記憶體例外與當機分類

指標告訴您有東西退步了,診斷則告訴您退步在哪裡。當出狀況時,例如當機或卡頓,「系統會在裝置上擷取一筆診斷」,而一份診斷報告會「把細節打包好,並透過 MetricKit 立即送到您的 App」。1許多診斷都包含回溯追蹤,呈現事件發生當下的確切呼叫堆疊。在該場次的逐步示範中,符號化後的回溯追蹤從系統程式碼中的執行緒起點開始,跨入 App,最後停在 App 的 submitReport() 函式,標示出失敗點,也就是修正應該瞄準的地方。1

當機診斷會帶有回溯追蹤、終止原因與例外型別。iOS 27 新增的終止 category「標示出每次當機在指標中是如何計入的」,因此「若異常終止呈現上升趨勢,您可以把它們直接與個別診斷做關聯」。1您儀表板上的指標折線,以及其背後的個別當機報告,終於共用同一把鑰匙。

iOS 27 也新增了記憶體例外診斷:「當您的 App 或擴充功能因超出記憶體上限而被終止時,您能對發生了什麼事獲得更多洞察。」1擴充功能明確涵蓋在範圍內,這對任何要遠端除錯小工具或擴充功能記憶體遭終止的人而言都很重要。

接收端與指標端如出一轍。您在自己的 MetricManager 實例上 await diagnosticReports,同樣在 App 啟動時於 detached task 或服務類別中進行,而 DiagnosticReport 值也符合 Codable,可走同一條編碼後送出的流程。1由於報告是結構化的,您可以對各診斷 case 進行 switch:當機 case 會給出回溯追蹤、原因與 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 會把這些狀態與指標及診斷資料相交。1

示範中的成果,正是讓這場重建顯得值得的時刻。指標不再是單一的混合 15 ms/s 數字,而是依狀態送達:Spending 分頁捲動起來「順得不可思議」,為 1 ms/s,而 Reports 分頁則「飆升到 71 ms/s」。1該場次下了混合數字永遠無從支撐的結論:「Spending 分頁表現得很棒!但 Reports 分頁正經歷嚴重中斷,而那正是您的最佳化心力該聚焦之處。」1一個數字變成了一項判斷與一份工作清單。

狀態遵循的是轉換模型,而非成對括起。「沒有起訖配對——App 在任一時刻回報它當下所處的狀況」,而 MetricKit 會追蹤 App 停留在每個狀態的時間長短。1

領域、中繼資料與依狀態編碼

每個狀態都歸屬於一個領域(domain),它「描述 App 的某項功能或某個區域」。一個領域同一時間只能持有一個作用中狀態,而不同的領域則能讓多個狀態同時進行中。1該場次的範例是一項 A/B 實驗:開啟實驗性變更時,支出資料以小批次從資料庫取出;關閉時則用較大批次。把分頁狀態與批次大小狀態放在不同的領域,意味著「MetricKit 會為每個分頁與每種批次大小各自交付指標」。1依畫面的遙測與實驗讀數,出自同一條流程,且來自現場。

該場次的導入有三個步驟:匯入 StateReporting 框架,建立一個領域(「通常是一個反向 DNS 字串」)並在設定 MetricManager 實例時註冊它,接著在 App 進入每個狀態時回報轉換,例如轉換到以字串「Reports」識別的狀態。1若需更細的粒度,您可用 ReportableMetadata 巨集定義自己的 struct,以該中繼資料型別建立一個 StateReporter,並在回報轉換時同時帶上標籤與您的自訂型別。該場次的 ViewConfiguration 範例帶有一個 listSize 值,以及清單是否已排序的資訊。1再次提醒:該場次是在投影片上展示這套流程,並未提供完整簽章,因此請把這個形式當成需在文件中確認的東西,而非可直接複製的語法。

在接收端,報告多出了第二個維度。在回報任何狀態之前,您指標報告上的 stateEntries 屬性是空的。導入之後,報告會帶有 StateEntry 值,每一個都持有「在該個別狀態所花時間內彙整的指標數值」。1對於伺服器流程,您可以把編碼輸出依領域分組:在您 JSONEncoderuserInfo 屬性上把 encodingFormatKey 鍵設為 byStateReportingDomain,編碼後的報告便會把 state entries 與 interval entries 都「依報告中存在的每個領域與狀態分組」呈現。1

最佳實務,以及從何著手

該場次以一段讀來像是得來不易的結構設計建議作結。領域應狹隘地限定在 App 的單一區域。狀態轉換「應代表穩定且有意義的階段,而非短暫的 UI 事件」。1請把每個狀態設計成:當退步出現時,單憑狀態本身就能給您足夠的資訊去瞄準修正。同時,請克制把每件事都加上量測的衝動:「狀態太多可能導致資料過於細碎,反而更難解讀整體樣貌」,而且狀態數量設有上限,以將額外負擔降到最低(該場次並未給出數字)。1在出貨前,請用 Points of Interest 工具驗證所回報的狀態是否符合您的預期。1

蒐集端只是整個系統的一半。該場次直言「跨所有裝置分析指標是一個資料科學問題」:您要架起一台伺服器來吸收報告,沿著您關心的維度彙整,建立基準線,並監看兩個方向上的變動。1Codable 報告與 byStateReportingDomain 編碼的存在,正是為了餵養這條流程。

對於既有的採用者,結尾的指示很明確:「如果您正在使用 MXMetricManager API,請遷移到新的 MetricManager API,以善用所有這些新功能。」1該場次的每一項進展都活在這套新 API 中,而該場次把它們呈現為「框架的未來」。1

FAQ

iOS 27 的 MetricKit 究竟改了什麼?

這個框架以現代 Swift 優先的 API 重建。進入點是新的 MetricManager 類別;指標與診斷報告以可 await 的非同步串流送達(metricReportsdiagnosticReports);報告符合 Codable,可直接做 JSON 編碼;而其結構可在程式碼中透過 intervalEntries 與指標群組來巡覽。iOS 27 還新增了 Metal 影格率指標、記憶體例外診斷、一個把當機診斷與指標計入串連起來的當機 category,以及用於依狀態指標的 StateReporting 框架。1

StateReporting 如何判定哪些指標屬於哪個狀態?

您的 App 會回報轉換:它正要進入的狀態,落在您所定義的某個領域內。MetricKit 會追蹤 App 停留在每個狀態的時間長短,並把該段時間內的指標數值彙整起來。沒有起訖配對;App 只是在任一時刻回報它當下所處的狀況。每個狀態接著會在指標報告中各自取得一筆 StateEntry1

我能不能同時追蹤多個維度,例如畫面與實驗組別?

可以。每個領域同一時間能持有一個作用中狀態,但不同的領域是並行運作的。該場次的報帳 App 把作用中的分頁放在一個領域,把資料庫批次大小實驗放在另一個領域,而 MetricKit 會為每個分頁與每種批次大小各自交付指標。1

我該把每個 UI 事件都當成狀態回報嗎?

不該。該場次建議採用代表穩定、有意義階段的狀態,而非短暫的 UI 事件;領域應狹隘地限定在 App 的單一區域;整體上也要有所節制:狀態太多會讓資料更難解讀,而系統也對狀態數量設有上限以將額外負擔降到最低。出貨前,請用 Points of Interest 工具驗證您的狀態。1

我一定得離開 MXMetricManager 嗎?

該場次的建議是從 MXMetricManager 遷移到新的 MetricManager API,因為其中涵蓋的每一項新功能(非同步串流、Codable 報告、狀態感知指標、新的指標與診斷型別)都是這套新 API 獨有的。1


今年 MetricKit 是一則由兩部分組成的故事中屬於現場的那一半:Instruments 在實驗室裡讓您看見卡頓,而狀態感知的 MetricKit 則告訴您對真實使用者而言是哪些畫面在卡——實驗室那一側的內容收錄於 Instruments 27 與 App 反應靈敏度。真正能修好一個 71 ms/s 分頁的算繪工作,則談於 iOS 27 的 SwiftUI 效能與互通。至於為何混合後的平均值一開始就會誤導人,正是 效能盲點 的主題。完整的系列專頁為 Apple Ecosystem Series

References


  1. Apple, WWDC 2026 session 222, Meet the new MetricKit. Source for the iOS 27 ground-up rebuild and Swift-first API framing, the MetricManager entry point and the metricReports / diagnosticReports async streams, Codable reports and JSONEncoder usage, intervalEntries and metric groups (.cpu, .memory, .display, .gpu, peakMemory), the Metal frame rate metric, memory exception diagnostics, the crash termination category, the submitReport() backtrace walkthrough, the StateReporting framework (domains, transition model, StateReporter, ReportableMetadata, stateEntries, byStateReportingDomain via encodingFormatKey), 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 from MXMetricManager to MetricManager

相關文章

Instruments 27 在 App 反應性方面的新功能

Instruments 27 新增了 Top Functions、Run Comparisons、Swift executors 工具以及全新的 Inspector 面板,用以診斷 CPU、actor 與系統呼叫造成的卡頓。

4 分鐘閱讀

iOS 27 中的 SwiftUI 效能與互通

iOS 27 的 SwiftUI 如何處理 lazy stack 捲動、GPU 著色器效果以及與 AppKit/UIKit 的互通,內容取自 UI Frameworks 團隊的三場 WWDC26 官方議程。

4 分鐘閱讀

從76到100:達成完美的Lighthouse分數

一個個人作品集網站如何從行動裝置Lighthouse效能分數76分、CLS 0.493,進步到全類別完美的100/100/100/100。

3 分鐘閱讀