Custom Fonts in Swift Package

I have been able to ship some image and asset catalogs in a Swift package with success using Xcode 12b1 and Swift 5.3. I am not having so much luck with using a custom .ttf file in a Swift Package.

I am loading a .ttf file in the manifest like so:
Code Block
.target(
name: "BestPackage",
dependencies: [],
resources: [
.copy("Resources/Fonts/CustomFont.ttf"),
.process("Resources/Colors.xcassets")
]
),


And I noticed that there's no initializer on the Font type in SwiftUI to include an asset from a module. For example, this works:

Code Block
static var PrimaryButtonBackgroundColor: SwiftUI.Color {
Color("Components/Button/Background", bundle: .module)
}


However, there's no way to specify where a font is coming from. I was hoping that loading it into the module would emit it into the target for use, but no such luck:

Code Block
static var PrimaryButtonFont: Font {
  Font.custom("CustomFont", size: 34)
}


This does not load the font as expected. I'm investigating using a CoreText api to try and trick it into loading, but I feel like there should be an easier way. Any advice?

Replies

Update: Still no success but I was able to prove that the font is indeed inside the module

I wrote a method to get available font URLs from the module like so:

Code Block
  static func fontNames() -> [URL] {
    let bundle = Bundle.module
    let filenames = ["CustomFont"]
    return filenames.map { bundle.url(forResource: $0, withExtension: "ttf")! }
  }


Calling this method at runtime and printing the result yields this:

Code Block
font names: [file:///Users/davidokun/Library/Developer/CoreSimulator/Devices/AFE4ADA0-83A7-46AE-9116-7870B883DBD3/data/Containers/Bundle/Application/800AE766-FB60-4AFD-B57A-0E9F3EACCDB2/BestPackageTesting.app/BestPackage_BestPackage.bundle/CustomFont.ttf]


I then tried to register the font for use in the runtime with the following method:

Code Block
extension UIFont {
  static func register(from url: URL) {
    guard let fontDataProvider = CGDataProvider(url: url as CFURL) else {
      print("could not get reference to font data provider")
      return
    }
    guard let font = CGFont(fontDataProvider) else {
      print("could not get font from coregraphics")
      return
    }
    var error: Unmanaged<CFError>?
    guard CTFontManagerRegisterGraphicsFont(font, &error) else {
      print("error registering font: \(error.debugDescription)")
      return
    }
  }
}


When I call it like so:

Code Block
fontNames().forEach { UIFont.register(from: $0) }


I get this error:

Code Block
error registering font: Optional(Swift.Unmanaged<__C.CFErrorRef>(_value: Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=105 "Could not register the CGFont '<CGFont (0x600000627a00): CustomFont>'" UserInfo={NSDescription=Could not register the CGFont '<CGFont (0x600000627a00): CustomFont>', CTFailedCGFont=<CGFont (0x600000627a00): CustomFont>}))


Any more ideas are welcome.
Add a Comment
You can do something like this:

I created a Fonts folder inside my project folder. Inside the Fonts folder I dragged and dropped my fonts file which should be in a .ttf format. Then I used this code to use that font:

Code Block SwiftUI
Text("Muskaan Agrawal")
          .font(Font.custom("Pacifico-Regular", size: 40))

Then you go to the info.plist file inside your project folder. It should say "information property list". If you hover next to that you can click the plus button. The placeholder should say "App Category". You click on that and change it "Fonts provided by Application". Under the value column corresponding to the "fonts provided by application" section you type in the name of the fonts file. In my case this was "Pacifico-Regular.ttf".

That should work if not you can go to the launch screen and add a label. Change the font to your chosen font and if it works that means that you have successfully imported the font and that you might need to check for spelling errors when you choose your font style in the Text box.
@muskaan21 I'm sorry, but I think you missed that I'm working in a Swift Package, and not an application. Your instructions would probably work for an application, but not for a package.
@dokun1 Oh yeah, sorry :\
I tested your code and it works!
Thanks for the excellent idea 👍

Did you remember to add the fonts to the info.plist of the target application inside the "Fonts provided by application"?
I have that and the fonts are loaded correctly after registering fonts with this example code.

@dokun1 I was able to get this working by using this tutorial, which seems to register the fonts via Core Graphics, as you've suggested:

Packaging your Custom Fonts in Swift Packages by Jacob Rakidzich https://jacobzivandesign.com/technology/custom-fonts-from-swift-package/

I would like to report that as of this comment's writing, this approach of registering fonts after the app launches works only for SwiftUI apps, but not for UIKit apps which still use UIApplicationDelegate.

For SwiftUI apps, it does suffice to define a register() function in your Swift Package, which you should then call in the SwiftUI app's init(), as follows:

import MyCustomPackage

@main
struct SwiftUIDemoApp: App {
    
    init() {
        CustomFont.register()
    }
    
}

The original poster @dokun1 already has a working implementation of the register() function.

However, UIKit apps seem to still require being configured as before, where font files must be in the application bundle and explicitly declared in the app target's Info.plist: https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app

  • EDIT: calling the register() function within UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:) works, too. Apparently, I forgot to actually call it in my UIKit-based demo app and I thought I did (was too sleepy to notice), which led me to believe that it does not work in UIKit. Calling the Swift package's register() function after app launch works, even without adding the font files in the app bundle and in the Info.plist file.

Add a Comment