---
title: "Practical Dynamic Type, Part 2: Testing"
source: "/writing/practical-dynamic-type-part-2-testing/"
---

Article

# Practical Dynamic Type, Part 2: Testing

Keehun Nam

August 27, 2018

Last year, our software developer [Chris Sessions](https://medium.com/@chris_91497) wrote a [helpful article about practical usage of Dynamic Type](/writing/practical-dynamic-type) . This short follow-up addresses one primary concern: _Unit Testing._ Since `adjustsFontForContentSizeCategory` cannot be unit tested, let's make our own!

Here is an example of a view that incorporates Dynamic Type features using Chris's `FontMetrics` wrapper from [Part 1](/writing/practical-dynamic-type) :

### That is not unit-testable. What are we going to do about it?

The reason that we cannot unit-test whether or not the label responds to Dynamic Type is that it is not possible to write to the system's Dynamic Type setting: `UIScreen.main.traitCollection.preferredContentSizeCategory`. It is a _read-only_ source of the current system-wide setting. One possible solution (and the one we happen to use at Livefront) is to disregard the system's `preferredContentSizeCategory` and use our own "source of truth" for the `UIContentSizeCategory`. This works because of two key reasons:

1.  Our `FontMetrics` can observe the system's Dynamic Type size change and read the new size.
2.  Crucially, we can also ask `UIFont` to use our own `contentSize` instead of the system's when calling its `preferredFont()`

With those two key components, we can implement our own Dynamic Type but with our own writable `preferredContentSizeCategory`.

## So what does that actually look like?

Before explaining what the changes to `FontMetrics` are, here is the updated class:

Here is how I arrived at this new code:

First, `FontMetrics` needs to use its own source of `UIContentSizeCategory` and not the system's `preferredContentSizeCategory`. This is reflected in lines 41-45. Notice that assigning a new value posts a notification for all observers of FontMetric's `ContentSizeCategoryDidChange`.

Secondly, this is not demonstrated in this article, but I changed over any references in my app's code of the system-provided `UIContentSizeCategoryDidChange` notification to our own: `FontMetricsContentSizeCategoryDidChange`

Third, in case that the user changes his or her Dynamic Type setting on their device while the app is running, `FontMetrics` observes for those changes and then changes its own `sizeCategory` to match the new system setting for a seamless experience (which then posts a notification). _We never want the user to notice that anything is different than how they expect system settings to work._

Fourth, now that `sizeCategory` behaves exactly as we would like, we request `UIFontMetrics.default.scaledFont()` to use our ContentSizeCategory instead of the system's own. This can be seen in lines 59, 69, 87, and 100.

Fifth, although this wasn't strictly necessary, I created a static constant called `default` so that calling `FontMetrics.default` would always return the same instance. This is akin to `NotificationCenter.default` or `UIDevice.current`. It just helps make users of `FontMetrics` keep their code clean-looking and bug-free (by avoiding accidentally using a different instance of `FontMetrics`. This also meant that any references to `FontMetrics()` in my app was replaced with `FontMetrics.default`.

**Basically, we've recreated the mechanism behind** `**UIContentSizeCategoryDidChange**` **and made our own writable version of** `**preferredContentSizeCategory**`**.**

To take advantage of our new `FontMetrics` wrapper, we must update `Hal9000` as well!

Notice that we aren't using `adjustsFontForContentSizeCategory = true` anymore, but now it's unit testable!

### Trade Offs

There is an important trade off that was made here: we can't keep using the convenient property called `adjustsFontForContentSizeCategory = true` as there is no way to ask it to observe our own `sizeCategory`. Plus, we want to have more control over our text than `adjustsFontForContentSizeCategory` offers. At Livefront, we've rolled our own `UILabel` replacement that is even more convenient to use than setting `adjustsFontForContentSizeCategory` to `true`. As a side-effect, we rarely have to actually observe the `FontMetricsContentSizeCategoryDidChange` notification because our custom `UILabel` (and a plethora of other controls) is already doing that for us behind the scenes.

## Onto unit tests!

Finally, the part we've been waiting for. Let's test `Hal9000`'s Dynamic Type features!

Note that while we're measuring the same `subject.dialog.font.pointSize` three times, we'll get a different result as we're changing `FontMetric.default.sizeCategory` before each measurement!

With this power, there are many things we can choose to do with it. Here are just some ideas:

-   Unit test Auto Layout with many different font sizes, especially complex layouts involving `UIStackViews` that changes axis according to its contents
-   Write (and be able to test!) custom controls and views that implement Dynamic Type features
-   Design unit-testable interfaces which _reduce_ white spaces at higher levels of Dynamic Type size (e.g. a form with vertical white space between fields that get closer together as the text gets bigger instead of the fields getting further apart)
-   Sleep better at night knowing that any regression of Dynamic Type features will be caught! 🎉

Hal9000 is passing!

## So... What's next?

At Livefront, we've rolled unit-testable Dynamic Type and many other features into our own versions of `UILabel`, `UIButton`, and others. It has made building new interfaces so much faster and much more reliable, and we encourage you to have your own personalized toolbox that is right for you.

[Here, you can download the Xcode project and run the tests yourself!](https://www.dropbox.com/s/9ci2r3bbvca4jhp/PracticalDynamicTypePart2.zip?dl=0)

## More Resources

-   " [Delivering an Exceptional Accessibility Experience](https://developer.apple.com/videos/play/wwdc2018/230/) " (WWDC 2018)
-   " [UIKit: Apps for Every Size and Shape](https://developer.apple.com/videos/play/wwdc2018/235/) " (WWDC 2018)

-   Keehun Nam

    Software Engineer

    Keehun Nam

### Tags

[Accessibility](/insights/all/?topic=accessibility) [Digital Engineering](/insights/all/?topic=digital-engineering) [Mobile Apps](/insights/all/?topic=mobile-apps)

### Share

-   Copied

## View these next.

[See all](/insights/all/?type=writing)

-   [

    Article

    How To Sabotage Your Project Using Inconsistency

    Collin Flynn

    ](/writing/how-to-sabotage-your-project-using-inconsistency/)
-   [

    Article

    Unit Testing race conditions by creating chaos (Swift)

    Sean Berry

    ](/writing/unit-testing-race-conditions-by-creating-chaos-swift/)
-   [

    Article

    UIApplicationDelegate call sequence reference

    Sean Berry

    ](/writing/uiapplicationdelegate-call-sequence-reference/)
