8.5 C
London
Tuesday, November 21, 2023

ios – Fonts randomly cease rendering accurately when constructing with Xcode 15


After updating my/our gadgets to iOS 17 (or Xcode 15, not fully certain), the customized fonts in our software have began randomly not working. Normally it should occur after the app has been within the background for some time, I believe.

I am conscious that there is been issues with Xcode 15 and fonts in Interface Builder, however (fortunately) we do not use that in any respect.

What occurs is both:

  1. The fonts do not load in any respect. For icon fonts, this implies a whole lot of query marks.
  2. The fonts get swapped round, that means common textual content (Roboto, customized font) begins rendering utilizing the icon font (Font Superior). I can inform as a result of I acknowledge the font from when incorrectly placing textual content into an icon throughout improvement. I attempted the reminiscence warning on the simulator, however that does not set off it. It didn’t occur when constructing for iOS 16 and I didn’t change any font logic since then. It nonetheless doesn’t occur on gadgets working iOS 17 however on variations of the app constructed with Xcode 14.

These are doubtlessly the identical downside, as trying to load icons with a non-icon font would set off scenario #1. I’ve not been in a position to connect a debugger to examine the views as I can’t reproduce the error.

Some debug data:

Xcode: 15.0.1 (15A507)

iOS: 17.1.1 (actual gadget, I’ve not noticed it on Simulator)

We load the customized fonts from a proprietary SPM package deal:

fileprivate static func registerFont(fontName: String, ext: String = "otf") {
    
    guard let fontURL = Bundle.module.url(forResource: fontName, withExtension: ext),
        let fontDataProvider = CGDataProvider(url: fontURL as CFURL),
        let font = CGFont(fontDataProvider) else {
            fatalError("Could not create font from filename: (fontName).(ext)")
    }
    
    var error: Unmanaged<CFError>?

    CTFontManagerRegisterGraphicsFont(font, &error)
    
}

public static func registerFonts() {

    registerFont(fontName: "Font Superior 6 Manufacturers-Common-400")
    registerFont(fontName: "Font Superior 6 Duotone-Stable-900")
    registerFont(fontName: "Font Superior 6 Professional-Mild-300")
    registerFont(fontName: "Font Superior 6 Professional-Common-400")
    registerFont(fontName: "Font Superior 6 Professional-Stable-900")
    registerFont(fontName: "Font Superior 6 Professional-Skinny-100")
    registerFont(fontName: "Font Superior 6 Sharp-Mild-300")
    registerFont(fontName: "Font Superior 6 Sharp-Common-400")
    registerFont(fontName: "Font Superior 6 Sharp-Stable-900")
    registerFont(fontName: "Roboto-Medium", ext: "ttf")
    registerFont(fontName: "Roboto-Common", ext: "ttf")
    registerFont(fontName: "RobotoMono-Common", ext: "ttf")
    
}

// Comparable strategies for all font varieties and icons:
@objc public static func getDefaultFont(measurement: CGFloat) -> UIFont {

    return UIFont.init(title: "Roboto-Common", measurement: measurement) ?? UIFont.systemFont(ofSize: measurement);

}

This code is then known as in didFinishLaunching, and it might crash the app if it failed:

FontHandler.registerFonts()

To reiterate, all of the fonts work when launching the app. Restarting the app all the time fixes it, which, mixed with the truth that they get swapped round, leads me to consider that it is some sort of font caching concern/bug.

We use the fonts in code-only (no storyboards or xibs ever). A font can be loaded like this:

let label = UILabel()
label.font = FontHandler.getDefaultFont(measurement: 15)

The font recordsdata themselves ship with the SPM package deal (in a folder known as Fonts), which incorporates the FontHandler class used above.

import PackageDescription

let package deal = Package deal(
    title: "MyPackageName",
    defaultLocalization: "en",
    platforms: [
        .iOS(.v11)
    ],
    merchandise: [
        .library(
            name: "MyPackageName",
            targets: ["MyPackageName"]),
    ],
    targets: [
        .target(
            name: "MyPackageName",
            resources: [
                .process("Fonts") // Here!
            ]),
        .testTarget(
            title: "MyPackageNameTests",
            dependencies: ["MyPackageName"]),
    ]
)

Latest news
Related news

LEAVE A REPLY

Please enter your comment!
Please enter your name here