Supporting Dynamic Type at Airbnb

An overview of process, tips, and pitfalls we learned while bringing Dynamic Type to Airbnb. For you to reference when supporting Dynamic Type in your apps too!

Noah Martin
Jul 24, 2019 · 5 min read

By Noah Martin & Hugo Ahlberg

Background

30% of people using the app had a preferred font size that was not the default.

It turns out, supporting this preference creates a consistent experience across the OS that users will notice. Experimenting with Dynamic Type on individual features in the Airbnb app resulted in a significant increase in engagement, helping move our bottom line metrics. If you spend the time to support Dynamic Type in your app too, users will surely thank you for it!

Font size selection in iOS

Why is it important?

Design

Second, we had to make sure fonts scale the correct amount. This is done by assigning every text a corresponding UIFont.TextStyle. Using a larger TextStyle indicates your font is already big, so it doesn’t need to increase in size as much.

Third, we had to fix some labels that were changing size even though they shouldn’t be eligible to scale. Our recommendation is everything part of the scrollable area on the screen should scale, and everything else should be left static. However, anyone with large Dynamic Type enabled still needs a way to view text in smaller containers such as tab bars. If you use all standard UIKit elements this is handled by the Large Content Viewer:

We filed a bug report with Apple, requesting a new API for presenting these popups from custom views. This capability has since been included in the iOS 13 beta, so you’ll be able to see it in the Airbnb app soon.

Engineering

Each of these attributes exist in two forms:

  1. The unscaled units which are shown to users with the default font size, and are the values we set when creating font attributes.
  2. The scaled units which fit the preferredContentSizeCategory and are what we read at runtime.

Internally, features will request a font using attributes expressed as unscaled units, and will receive an object containing various functions we use to display text which always returns values in scaled units. This ensures any calculation, such as bounding box of text, will use the scaled units. Some of the most common bugs we saw were caused by using unscaled units for layout calculations instead of scaled units.

There are two ways to use UIFontMetrics to convert from unscaled to scaled units.

Method #1:

func scaledFont(for font: UIFont, compatibleWith traitCollection: UITraitCollection?) -> UIFont

Method #2:

func scaledValue(for value: CGFloat, compatibleWith traitCollection: UITraitCollection?) -> CGFloat

There are subtle differences in these approaches. Consider the following examples, each using a different UIFontMetrics method:

Depending on the device you run on, we observed results like this:

Scaled pointSize and lineHeight vary between method 1 and method 2
Scaled pointSize and lineHeight vary between method 1 and method 2

The results aren’t quite consistent, but since we customize line height with NSParagraphStyle we need to use the CGFloat scale function. The UIFont with unscaled point size is scaled directly to get an adjusted UIFont. Here’s a full example to scale an NSAttributedString:

The last step to fully supporting Dynamic Type is to encourage validation across all features, including ones in development. We know developer time is limited, so we automated support for Dynamic Type as much as possible. Happo, the tool we use for UI regression detection, already snapshots existing components. We added an additional step to render with the accessibilityExtraExtraExtraLarge size.

There are no APIs available to programmatically change the simulator’s Dynamic Type settings, so avoid using UIApplication.shared.preferredContentSize. A more testable approach is to query the trait collection of a UIView. In the UIWindow for our Happo Tests, the traitCollection is configured to include a custom content size. The end result is snapshots like these:

Default (large) preferred content size
AccessibilityExtraExtraExtraLarge preferredContentSize

With snapshots generated on every code change to the iOS app, developers have a hassle-free way to know new features support Dynamic Type, and easily detect regressions.

Thanks to Amie Kweon, Dylan Harris, Bryn Bodayle, Tyler Hedrick, and Kieraj Mumick for their support on this project!

To try out these features for yourself, download the Airbnb app on the App Store. If you’re passionate about projects like this and iOS development, we encourage you to apply for the roles we have open!

The Airbnb Tech Blog

Creative engineers and data scientists building a world…