We continue gets acquainted with new stuff from Apple presented at WWDC. At this time we consider MetricKit, this brand new framework, which serves as a tool for application performance monitoring.
Everyone knows that measuring application performance during development is easy. Xcode shows the amount of used RAM and CPU load, you can connect using the Instruments to the simulator or the device under test and even write their own tools (more on this see our article on custom tool packages: part 1/part 2). Only an understanding of the importance of the performance setting does not allow to measure almost anything that runs an application. But everything becomes more complicated when we’re talking about the AppStore, if the application is intended for real users. No matter how thoroughly you are testing your app in real conditions and will always be a bunch of surprises that will affect performance and user experience. Of course, there are many tools for collecting different parameters, but most of them are limited by the iOS SDK, as well as the influence of the actual monitoring behavior of applications.
This year, Apple decided to fill this gap and provide developers a tool that helps them gather and analyze application performance in the real environment. They announced MetricKit (a framework that provides access to the parameters offered OS) separate tab in the Xcode organizer 11, where can I find application settings. Pause in MetricKit because the display settings in XCode will only work with applications that are already published in the AppStore.
The architecture of the framework is quite simple and straightforward. The Central part is the class MXMetricManager, which is a one-element structure, which provides developers with a large set of APIs of the framework.
In General, a workflow consists of 3 main stages:
- MXMetricMnager you initialize and assign a observer.
- If you wish, you can implement your own metrics in your app using Signpost API
- And finally, now we are dealing with the received data in the method didReceivePayloads, i.e. send them to your backend for further analysis.
Options come in the form of an array of instances MXMetricPayload. The payload encapsulates the metadata sets and timestamps. Metric payload is a simple wrapper for a subclass MXMetric. For each type of setting, it is separate.
The types of metrics is quite well documented by Apple, so we will not dwell on this too long. However, we should stop to notice one interesting thing MXMetric provides a public API to serialize it to JSON or NSDictionary that, from my point of view, a bit unusual.
Internal components MetricKit.
Outside MetricKit looks pretty simple. But it’s always interesting to see how it works from the inside. Dive into something deeper is always intriguing, if you are faced with a specific task. So I decided that I want to pass parameters with labels MetricKit, and then get them to provide me with updated metrics at any time. Of course, you can use `Debug -> Simulate MetricKit Payloads` in Xcode, but id does not allow you to display your own data. However, this is not a very useful command, but it gives you direction in your research, and it looks quite funny 😉
To begin the task, we obviously need MetricKit. You might think that to a binary file for framework easy because Xcode shows it in the list of frameworks once you add it via the “link binary with libraries”. This is a very optimistic thought. Because if you open MetricKit.framework, you will see a file MetricKit.tbd. Its size is just 4KB. Obviously, that’s not what we’re looking for.
So what actually happens here?
TBD stands for “text-based stub dylib” and is actually a YAML file describing dylib exporting the symbols and the path of the binary file dylib. Linking to files tbd reduces the size of the binary file. Later, at run time, a real binary file dylib is loaded from the OS path in the tbd file. Here is the file when you open it in Xcode:
Using the path of file tbd, it’s easy to binary MetricKit for further research, but there is an even more simple method.
Our the application binary contains the path of each dynamically linked library in the header section of Mach-O. This information is easy to obtain using the tool using the-l flag.
Here is the output for the test project I created:
→ otool -l ./Metrics | grep-i metrickit name /System/Library/Frameworks/MetricKit.framework/MetricKit (offset 24)
You can see the same path that we saw earlier in the tbd file. Having a binary framework, you can look at the internal elements. For this I usually use Hopper Disassemble. It is simple to use but very powerful tool for a careful study of the binaries.
As soon as I open the binary file MetricKit — go to the tab ‘Proc.’ and deploy a list of ’Tags’. Here you can see all the exported symbols. Choosing one of them (for example, MXMetricManager), we can see all its methods and choosing the method that will see its contents in the right part:
When viewing the list of methods MXMetricManager [ https://gist.github.com/deszip/88a258ae21d33dc75d7cbac9569c6ec1 ] very easy to notice the method _checkAndDeliverMetricReports. It seems that you must call to make MetricKit to deliver updates to subscribers.
Unfortunately, trying to invoke it did not lead to a call of a subscriber, which probably means that these settings will not be delivered. Considering the implementation of the method, we note some interesting things: it iterates through the contents of the directory /Library/Caches/MetricKit/Reports.
He then tries to unzip the instance MXMetricPayload for each item on the disk. And finally, it iterates over registered subscribers and calls the method didReceive a list of data.
The problem is probably that in /Library/Caches/MetricKit/Reports no data, but we know that we need some archived copies MXMetricPayload. So, let’s create them and place them on disk before calling ‘_checkAndDeliverMetricReports’. Again, the plan is to create an instance MXMetricPayload, then create and add any type MXMetric and then archive the instance data on the disk. After all to call method ‘_checkAndDeliverMetricReports’, this should cause our subscriber stub as the argument.
Viewing documents on Apple, and payload metrics, you may notice that they don’t have a public initializer, and most of the properties are read-only. So how it is possible to create an instance of the class?
Again back to the Hopper to see the list of methods MXMetricPayload:
Here you can see its initializers and methods for assigning parameters. To call private methods is easy, using the class NSInvocation and methods ‘performSelector’ because of the dynamic nature of Objective-C.
As an example, we will create metrics for CPU and add them to the payload. Using this link you can find the complete code snippet: [ https://gist.github.com/deszip/a0cf877b07cc2877129e0aaef2fed1e4 ].
And finally archived everything we have created and the data written into the directory /Library/Caches/MetricKit/Reports.
Now it’s time to call the method ‘_checkAndDeliverMetricReports‘, which ultimately should lead to a call of the subscriber. This time in an argument with stubbed transmit the data payload as argument.
Where are the metrics
Reporting is quite simple to implement by MetricKit, but you’re probably interested to know how the reports appear in the directory app/Library. Here’s how.
Digging inside the binary file MetricKit, I noticed this method: ‘_createXPCConnection’. Verification of its implementation and clarify the situation — it builds NSXPCConnection to service named com.apple.metrickit.xpc and two interfaces MXXPCServer and MXXPCClient for client and server sides. If you look at the description of the Protocol:
MetricKit is a unique and indispensable tool for concern about the performance of your application in real conditions in production.
Unfortunately, it is not currently possible to look at the user interface ‘Metric’ in Xcode, except that it was shown during a demonstration at the session of WWDC.
It can be an invaluable tool to move user experience to a new level by eliminating performance issues in your code.
One drawback that I see now in this tool is the lack of details for each type: only splitting — this version of the app, and you can’t see any metrics for a particular group of devices/OS/regions, etc.
But, of course, there is always the possibility to send data to yourself for further processing together with the important information that you need. You can attach it to tasks in your bug tracker and much more. In AppSpector our team is working on expanding the functionality of the monitoring tools using performance data obtained from MetricKit.
Stay up to date!