Using Property Wrappers in Swift with XCTMetric.

Posted by

Now before we get started on this one, I’m gonna put a massive Disclaimer on this article… I’ve only just started to test the water with XCTMetric, so whether this actually works as expected is still up for discussion as I’m get to test this in a real world environment – but non the less, the theory behind this is what I find most interesting (and cool IMO) 😎.

Overview – Property Wrappers

Property wrappers first burst onto the scene back in 2019 as part of the Swift 5 release. In this article I’m going to assume you’ve had some prior exposure to Property Wrappers before, but if you’ve not… it’s a way of using annotations to remove repetitive boilerplate code and stop you from adding tons of logic in Computed Properties (well, it stops me from doing this anyway).

Below is a very quick example of how you might use a Property Wrapper to prepend a currency symbol to a String;

@propertyWrapper struct Currency {
    var wrappedValue = ""
    init(wrappedValue: String, currency: CurrencySymbol) {
        switch currency {
        case .GBP:
            self.wrappedValue = "£\(wrappedValue)"
        case .USD:
            self.wrappedValue = "$\(wrappedValue)"

Taking a quick look at the preceding code, wrappedValue within our initialiser, is the content of our Property ‘Wrapped’ code.

With the above set, lets take a look at how we would use this;

@Currency(currency: .GBP)
var test = "99.99"

Our Currency Property Wrapper allows us to use a neat little annotation against our ‘test’ variable (note, the above can all be done inline too).

Could this be done in a String Extension… yes – but this is just a very basic example on how we can use property wrappers in Swift 5

Overview – XCTMetric

I stumbled upon XCTMetric very recently as I’ve been deep-diving into performance related tools for various projects.

In a nutshell, XCTMetric allows you to test various performance metrics against a baseline you set for functions or logic within your XCTests.

The output for this is logged directly into Xcode’s test results window for you to consume however you see fit. Let’s start by taking a look at how we would use XCTMetric in one of our Unit Tests.

func testPerformanceMetric() {
    // Test tools
    let obj = TestTools()
    // This is XCTMetric
    measure(metrics: [XCTClockMetric(),
                        XCTMemoryMetric()]) {
        // Function that calls DateFormatter 100000 times 😝
        obj.testLeak(cycles: 100000)

As you can see from the preceding code, we pass in an array of XCTMetric classes based on the type of metrics we want to report on, for more details on what each one of these does, see the following docs on Apple Developer site here.

In order for your XCTest to fail (or pass) a Unit Test against XCTMetric, you first need to set a baseline based on the result of the first test you run. When running XCTMetric for the first time you’ll be presented with the following in Xcode;

XCTest – XCTMetric showing ‘no baseline average‘ set in Xcode

Selecting into this, you’ll then be presented with the following screen, which in turn will allow you to set your benchmark baseline;

UI of Performance Result, allowing you to set a baseline for your XCTMetric test

Clicking on Set Baseline, allows you to drill further into the tool – setting baselines for each of the XCYMetrics that you defined for your test (see the Array of XCTMetric mentioned previously).

UI of Baselines set for your XCTMetric test

When all your XCTestCase’s have ran, if any of your tests have been successfully measured as part of XCTMetric, you’ll be able to see further details in your test report.

XCTestCase Report including results of XCTMetric

Like I said earlier, I’m not going to deep dive into the ins and outs of XCTMetric, I’ll save that for another day (and to give myself more time to understand it too 😝) –

However I get and like the concept behind it – but I also don’t like re-using code over and over again, so wanted a neat way to simply drop, let’s say an ‘annotation‘ onto a function or an object to measure my code against – so this is where my genius idea came from…

The “Proposed” Solution

So if I could use annotations more I would… I’d love one to exclude Xcode from including certain functions in code coverage (ohh, the TDD & App Architecture police are coming 🚨) – but seriously, I’ve thought of a few examples over the years where I’ve love to drop a custom annotation in for one reason or another – so how cool would it be to do one for XCTMetric…

I went back and fourth a few times on this solution, trying to get it to work, so trying to write out my thought process would be confusing and probably boring to read, so I’ll start by dropping in the solution and going through it almost line by line.

@propertyWrapper struct MeasureableObject<Type> {
    var wrappedValue = ""
    init(wrappedValue: Type, testInstance: XCTestCase) {
        testInstance.measure(metrics: [XCTClockMetric(),
                                        XCTMemoryMetric()]) {
            _ = wrappedValue

First of all, I want this to be a generic as possible, initially I played around with creating a Property Wrappers for specific types, Strings, Objects etc.. but in typical me fashion I wasn’t happy with that approach.

So I start by accepting my wrappedValue as Type allowing swift to determine the type at runtime (and hopefully keeping the compiler happy too) – worth a shot!

@propertyWrapper struct MeasureableObject<Type>

But my next problem became apparent when running XCTMetric.measure(). This inherits from XCTestCase (basically the running class/instance of your XCTest) and having this sit in a Property Wrapper that you might want to use across various tests and file, simply won’t work.

So my next step was to use a custom init() in my Property Wrapper, which will allow me to pass in an instance of my UITestCase;

init(wrappedValue: Type, testInstance: XCTestCase)

So with this all in place… the compiler was happy but would my property wrapper sit nicely when annotated against various objects or even functions… let’s try it out;

func testPerformance_String() {
    @MeasureableObject<String>(testInstance: self)
    var test = "Test String" 

func testPerformance_Function() {
    @MeasureableObject<Void>(testInstance: self)
    var test = TestTools().testLeak(cycles: 100000) 

func testPerformance_viewController() {
    @MeasureableObject<UIViewController>(testInstance: self)
    var test = UIViewController()

In the preceding code, the compiler has allowed me to annotate against a String, Function & a UIViewController.

As a small trade off for my one single property wrapper I did need to set the Type on the annotation – allowing Generics to work some of its magic – but again, the compiler seemed happy, but would it actually work???


When running the tests, I was presented with the option from my Property Wrapper to ‘Set Baseline‘ – so for me that’s a great start and the XCTMetric output showed on my XCTestCase report.

Looking at my Tools.testLeak() function, changing the amount of cycles passed in did have and affect on the values in the report, increasing as expected, so passing in Void as a my Generic type still ran the function and measured the metrics in my Property Wrapper (well, as far as I’m concerned it did anyway).

Final thoughts

This all might go out of the window when I dig deeper into XCTMetric and the XCTestCase’s reporting tool, but I really like the theory behind it and taking something like Property Wrappers to help out in an unusual situation like this kinda made me smile a little.

As always, I’d love to hear your thoughts, feedback and suggestions around this topic or anything XCTMetric related.

Sample code for this post can be found on GitHub here.


C. 🥃