Monitoring for iOS with MetricKit: Getting Started

Learn how to use MetricKit to monitor power, performance, and diagnostics in your iOS apps. By Adam Rush.

Leave a rating/review
Download materials
Save for later
Share

For a long time, iOS apps were considered too small and lightweight to worry about monitoring their performance, unlike web apps. In modern times, however, iOS apps are getting bigger, more complex and doing more behind the scenes than ever before. Being able to remotely monitor how your app is running on many different devices is now vital.

Some iOS monitoring tools have been available for a long time. For example, you could query the device to see what OS is running or whether it’s using a Wi-Fi or cellular connection. Another common practice is using a third-party analytics tool, like Firebase or New Relic, to help capture what a user’s experience is really like.

However, there’s so much more diagnostic information that could help iOS developers! Apple listened and, in iOS 13, introduced MetricKit. This tool makes it much easier to retrieve diagnostic data from the device and make it available for remote monitoring for iOS. In this tutorial, you’ll explore MetricKit APIs by using Xcode to simulate receiving diagnostics.

You’ll do this by:

  • Adding MetricKit to the starter app.
  • Loading sample data into Xcode.
  • Using MetricKit to view app use statistics.

Looking Inside MetricKit

With MetricKit, you can receive diagnostic data from the OS about the physical device. It sends a report containing data for the last 24 hours in JSON format. This is helpful, for example, if you want to relay this data to your own server. You can then visualize and analyze that data and use it to improve the performance of your app.

New APIs in iOS 14

MetricKit has been around since iOS 13, but it received some significant improvements in iOS 14.

First, it introduced a brand new payload called MXDiagnosticPayload. Previously, you only received the MXMetricPayload. The new MXDiagnosticPayload gives much more information, such as crashes and exceptions. This is a great example of how Apple is continuously exploring ways of expanding frameworks.

Furthermore, Apple has introduced a brand-new performance metric called MXAppExitMetric. This object represents the types of exits made by the app in the foreground and background. This information can help you discover why users are leaving your app and in what state.

Getting Started

Download the sample project using the Download Materials button at the top or bottom of this tutorial.

For this tutorial, you’ll work with Shopping Trolley, an app that displays a simple shopping list for different types of fruit — because the world clearly needs more fruit-listing apps. :]

Note: Before you begin, you should know that MetricKit only works on a real iOS device and isn’t compatible with the simulator. So, you’ll need to plug in a real device to get this working end-to-end.

Testing MetricKit with a real iOS device

Start by opening ShoppingListTableViewController.swift. Add this code to the top of the file.

import MetricKit

Now, you can access the MetricKit framework.

Next, add this code into viewDidLoad():

let metricManager = MXMetricManager.shared
metricManager.add(self)

This accesses MetricManager provided by the framework. Then add self, the ShoppingListTableViewController, as a subscriber to metricManager, which enables it to listen for a metrics payload from the OS.

Next, you just need to add one final piece of code to the bottom of ShoppingListTableViewController.swift.

extension ShoppingListTableViewController: MXMetricManagerSubscriber {
  func didReceive(_ payloads: [MXMetricPayload]) {
    guard let firstPayload = payloads.first else { return }
    print(firstPayload.dictionaryRepresentation())
  }

  func didReceive(_ payloads: [MXDiagnosticPayload]) {
    guard let firstPayload = payloads.first else { return }
    print(firstPayload.dictionaryRepresentation())
  }
}

This is an extension on your view controller that conforms to MXMetricManagerSubscriber, which contains two methods that can receive payloads from MetricKit. In this case, when receiving the payload, you simply print it to the console. However, in your production version, this is a good place to record the metrics, such as making that API call to your server.

Congratulations! You’ve integrated MetricKit. :]

Understanding MetricKit APIs

The framework’s interface is straightforward, and you can see how much thought Apple put into it during development. The framework includes:

  • A manager class with a subscriber protocol.
  • Classes for each category of metrics and diagnostics.
  • Payload classes for the reported data.
  • Classes for measurement units, such as cellular bars. This is amazing!
  • Classes for representing accumulated data such as histograms. They’ve done the hard work again!

Understanding MXMetricManager

MXMetricManager is the beating heart of the MetricKit framework. This is the shared object that manages your subscription for receiving on-device daily metrics.

MetricKit starts accumulating reports for your app after calling shared for the first time. To start receiving metrics reports, you first call add(_ :) with a class that conforms to the MXMetricManagerSubscriber protocol.

The system then delivers these reports once per day at most. Each report contains the metrics from the past 24 hours along with any previously undelivered reports.

The Manager also has remove(_ :), which allows you to remove a subscriber at any point.

happy Swifty riding a rocket

Implementing MXSignpostMetric

A huge benefit of using the MetricKit framework is that you can now incorporate your very own metrics inside the ones Apple provides “out of the box”. This is incredibly powerful because it means you can add custom metrics to the reports you send to your server, allowing you to dig even further.

Start by adding a custom metric to log when a user loads ShoppingListTableViewController. Add fruitsLogHandle directly below the existing fruit.

let fruitsLogHandle = MXMetricManager.makeLogHandle(category: "Fruits")

This creates a handle, which is a bit like a bucket, to hold your custom metrics.

Add this code to the end of viewDidLoad().

mxSignpost(
  .event,
  log: fruitsLogHandle,
  name: "Loading Fruits TableViewController")

mxSignpost logs a custom metric within the fruits bucket when the view controller has finished loading for the user. This is a simple example, and you could really expand on it further. For example, if your app has a video player, logging when the stream starts and finishes could be useful.

Implementing MXMetricPayload

You can receive two different payloads from MetricKit. Begin with MXMetricPayload — this is an object that encapsulates the daily metrics report. Remember: To trigger the MetricKit daily report, you must have a real device plugged in! If you don’t have a device, you can still read through the tutorial, as a sample JSON payload is provided.

Build and run on the device.

Shopping Trolley app showing shopping list with apples, bananas and strawberries

Navigate back to Xcode and select DebugSimulate MetricKit Payloads.

selecting Simulate MetricKit Payloads in Xcode

This triggers a sample daily report with the same shape as the reports you’ll receive organically. You should see both of the payloads printed on the console.

 AnyHashable("cellularConditionMetrics"): {
    cellConditionTime =     {
        histogramNumBuckets = 3;
        histogramValue =         {
            0 =             {
                bucketCount = 20;
                bucketEnd = "1 bars";
                bucketStart = "1 bars";
            };
            1 =             {
                bucketCount = 30;
                bucketEnd = "2 bars";
                bucketStart = "2 bars";
            };
            2 =             {
                bucketCount = 50;
                bucketEnd = "3 bars";
                bucketStart = "3 bars";
            };
        };
    };

The code sample above reproduces a small snippet of the full payload showing cellularConditionMetrics. This aspect of the payload provides rich data about the cellular conditions your user experienced during the last 24 hours of using your app. It also delves further by telling you how many times they were on one bar, two bars or three bars of service. You can use bucketCount to create a histogram. Imagine how helpful it could be to know the average amount of time your users spend on certain bars of signal!