PerformanceSuite 是一个 iOS Swift 库,旨在测量和收集 iOS 应用程序的性能和质量指标。
与其他解决方案(如 MetricKit、Firebase Performance、Instabug、Sentry 等)相比,它提供了额外的灵活性。 但是,它专注于性能监控的原生部分。 对于存储和可视化您的指标、构建监控图表以及设置警报,您需要有自己的后端。
此库用于Booking.com 主要 iOS 应用程序,该应用程序每天被数百万用户使用。 我们在这篇文章中描述了我们在 Booking.com 如何测量性能。
我们还开源了类似的 Android PerformanceSuite。
请注意,PerformanceSuite 目前不支持跟踪标准崩溃。 您将需要一个额外的工具来收集崩溃的堆栈跟踪(例如,Firebase Crashlytics)。
应该在您的应用程序启动时激活 PerformanceSuite
监控,方法是提供一个设置为处理性能指标的对象。 随着您的应用程序继续运行,您将收到传递这些指标的回调。
func startupTimeReceived(_ data: StartupTimeData) { ... }
func fatalHangReceived(info: HangInfo) { ... }
func nonFatalHangReceived(info: HangInfo) { ... }
func viewControllerLeakReceived(viewController: UIViewController) { ... }
func watchdogTerminationReceived(_ data: WatchdogTerminationData) { ... }
func appRenderingMetricsReceived(metrics: RenderingMetrics) { ... }
对于屏幕级别的指标,您应该从 screenIdentifier(for:)
返回 ScreenIdentifier
,如果此视图控制器不应被跟踪,则返回 nil。 查看 [Screen identifiers] 获取示例。
func screenIdentifier(for viewController: UIViewController) -> ScreenIdentifier? { ... }
func ttiMetricsReceived(metrics: TTIMetrics, screen: ScreenIdentifier) { ... }
func renderingMetricsReceived(metrics: RenderingMetrics, screen: ScreenIdentifier) { ... }
PerformanceSuite 屏幕跟踪在很大程度上依赖于 UIKit UIViewController 的生命周期。
对于纯 SwiftUI 应用程序,iOS 仍然在底层创建 UINavigationController
来执行导航,PerformanceSuite 支持这些情况。
但是,不创建任何 UIHostingController
的自定义 SwiftUI 过渡当前未实现自动化。 目前,您可以为此类情况使用 Fragment TTI tracking。 如果有需求,我们可能会在以后引入一些语法糖。
但是,对于大多数应用程序而言,当前设置足以自动跟踪 UIHostingController
中的 SwiftUI 视图的屏幕打开。 查看 Usage 部分了解更多详情。
PerformanceSuite
库并将其添加到您的目标。要使用 CocoaPods 将 PerformanceSuite
集成到您的 Xcode 项目中,请在您的 Podfile 中指定它
pod 'PerformanceSuite'
目前 CocoaPods 仓库 has problems 在索引新添加的 pods 时存在问题,这就是为什么如果它不起作用,您可以指定源 url 和 tag
pod 'PerformanceSuite', :git => 'https://github.com/bookingcom/perfsuite-ios.git', :tag => '0.0.4' # use the last released version here
要接收性能事件,您必须有一个类实现以下一些协议
TTIMetricsReceiver
RenderingMetricsReceiver
AppRenderingMetricsReceiver
WatchDogTerminationsReceiver
HangsReceiver
ViewControllerLeaksReceiver
StartupTimeReceiver
ViewControllerLoggingReceiver
FragmentTTIMetricsReceiver
或者,您可以使用 PerformanceSuiteMetricsReceiver
来接收所有事件。
性能监控应在您的应用程序中尽早启动。 例如,您可以在 application(application:didFinishLaunchingWithOptions:)
方法的开头开始。
let metricsConsumer = MetricsConsumer()
try PerformanceMonitoring.enable(config: .all(receiver: metricsConsumer))
// or with more flexibility
let metricsConsumer = MetricsConsumer()
let config: Config = [
.screenLevelTTI(metricsConsumer),
.screenLevelRendering(metricsConsumer),
.appLevelRendering(metricsConsumer),
.hangs(metricsConsumer),
]
try PerformanceMonitoring.enable(
config: config,
// you may pass your own key-value storage
storage: KeyValueStorage.default,
// you may pass a flag if app did crash from Crashlytics
didCrashPreviously: didCrashPreviously
)
所有屏幕级别的指标都通过 UIViewController
对象从 PerformanceSuite 传递到您的代码。 要将视图控制器对象转换为 ScreenIdentifier
,您可以使用以下方法
PerformanceScreen
枚举PerformanceTrackableScreen
,每个屏幕都应返回此枚举UIHostingController
添加 SwiftUI 支持// We define enum with all our possible screens
// If you have too many screens, there can be several enums,
// or just a string identifier.
enum PerformanceScreen: String {
case search
case details
case checkout
}
// We define a protocol for screens to conform
protocol PerformanceTrackableScreen {
var performanceScreen: PerformanceScreen? { get }
}
// For view controllers it is easy, we just return which screen is this
extension SearchViewController: PerformanceTrackableScreen {
var performanceScreen: PerformanceScreen? { .search }
}
// If you have SwiftUI screens without corresponding custom `UIHostingController`,
// you will need to add introspection logic to find root views
// in any `UIHostingController` in the app.
//
// We should conform to this protocol in the topmost view of the screen.
//
// NB: if possible, better to use your own subclass for `UIHostingController`
// and implement `PerformanceTrackableScreen` only in your subclass.
// Otherwise it may be additional performance overhead to introspect
// all hosting controllers in the app
extension CheckoutScreenSwiftUIView: PerformanceTrackableScreen {
var performanceScreen: PerformanceScreen? { .checkout }
}
// We also need to implement the protocol in UIHostingController,
// So we can determine which is the SwiftUI view inside this controller.
extension UIHostingController: PerformanceTrackableScreen {
var performanceScreen: PerformanceScreen? {
return (introspectRootView() as? PerformanceTrackableScreen)?.performanceScreen
}
}
// In our metrics consumer we will receive UIViewController
// and should determine which screen is this.
class MetricsConsumer: TTIMetricsReceiver {
func screenIdentifier(for viewController: UIViewController) -> PerformanceScreen? {
(viewController as? PerformanceTrackableScreen)?.performanceScreen
}
func ttiMetricsReceived(metrics: TTIMetrics, screen: PerformanceScreen) {
// send the event to your backend with this identifier
send(metric: "tti", value: metrics.tti.seconds, screen: performanceScreen.rawValue)
}
}
在存储库中,我们有示例应用程序 PerformanceApp
,在第一个屏幕上,有用于生成所有可能指标的选项
PerformanceApp
启动时生成的我们在集成 UI 测试中使用此 PerformanceApp
来验证是否正确生成了所有指标。
要在本地启动项目
gem install cocoapods
安装 CocoaPodspod install
生成 Pods
文件夹Project.xcworkspace
以启动示例 PerformanceApp
或运行测试PerformanceApp
scheme 启动应用程序UnitTests
启动单元测试UITests
启动集成 UI 测试。 请注意,此 scheme 以 Release 模式编译。此软件最初由 Booking.com 开发。 经 Booking.com 批准,此软件已作为开源发布,对此作者们表示感谢。