What can go wrong when using custom fonts in SwiftUI

What can go wrong when using custom fonts in SwiftUI

Ensure that font weight gets correctly applied even if a custom font was not registered by the app developer

Β·

2 min read

This blog post is targeting SDK developers who bundle custom fonts in their package/library/framework and these fonts shall be used in reusable SwiftUI views.

I will explain an issue observable when using font weight related view modifiers (like bold) on a non-registered custom font.

Let's assume you added the Open Sans true type font files to your bundle

  • OpenSans-Regular.ttf
  • OpenSans-Bold.ttf
  • ...

You also provide reusable SwiftUI views which shall automatically leverage the custom font. To do so you can pass the result of Font.custom(:size) to the font view modifier.

struct ReusableView: View {
    var body: some View {
        Text("Use Font.custom directly")
            .font(.custom("OpenSans-Regular", size: 17))
            .bold()
    }
}

If you use the bold view modifier then SwiftUI is smart enough to load OpenSans-Bold.

Correct bold font when registered

This all works well as long as the app developer did not forget to register the font in the app for use.

Font.registerOpenSans()

extension Font {
    static func registerOpenSans() {
        self.register(name: "OpenSans-Regular", withExtension: "ttf")
        self.register(name: "OpenSans-Bold", withExtension: "ttf")
        // ...
    }

    static func register(name: String, withExtension: String) {
        let fontURL = Bundle.main.url(forResource: name, withExtension: withExtension)!
        var error: Unmanaged<CFError>?
        CTFontManagerRegisterFontsForURL(fontURL as CFURL, .process, &error)
        if let error = error {
            print(error.takeUnretainedValue())
        }
    }
}

So what if the app developer forgot to register the font?

Fallback without bold

You might expected that the bold font weight would still gets applied. But this is not the case. Apple Bug ?

To ensure that the font weight gets applied, no matter what, I recommend to use an extension. This extension either loads the font or fallbacks to the system font.

extension Font {
    /// get "Open Sans" font or fallbacks to the system font in case the custom font was not registered
    /// - Parameter size: size of font
    /// - Returns: "Open Sans" font (or sytem font as fallback)
    static func openSans(size: CGFloat) -> Font {
        guard UIFont.familyNames.contains("Open Sans") else {
            return Font.system(size: size)
        }
        return .custom("OpenSans-Regular", size: size)
    }
}

This will ensure that SwiftUI will be able to apply font weight even to a fallback font.

Fallback honors bold font weight

struct ReusableView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Use extension with fallback handling")
                .font(.openSans(size: 17))
                .bold()

            Text("Use Font.custom directly")
                .font(.custom("OpenSans-Regular", size: 17))
                .bold()
        }
    }
}

Of course the result is the same in case the font was registered.

Works well when font is registered

Did you find this article valuable?

Support Swifty Tech by Marco Eidinger by becoming a sponsor. Any amount is appreciated!