Load fonts from a Swift Package

Viewed 1725

I wonder if there is a way to load fonts from a Swift Package at the moment? The font files are there in the package but the compiled program can't locate them.

2 Answers

Mojtaba Hosseini's answer is correct but in order to use the fonts of your package you need to register them also. You could do it with a little supporting function in a struct... I use this in my projects:

public struct Appearance {

    /// Configures all the UI of the package
    public static  func configurePackageUI() {
        loadPackageFonts()
    }

    static func loadPackageFonts() {
    
        // All the filenames of your custom fonts here
        let fontNames = ["Latinotype - Texta-Black.otf",
                         "Latinotype - Texta-BlackIt.otf",
                         "Latinotype - Texta-Bold.otf",
                         "Latinotype - Texta-BoldIt.otf",
                         "Latinotype - Texta-Book.otf",
                         "Latinotype - Texta-BookIt.otf",
                         "Latinotype - Texta-Heavy.otf",
                         "Latinotype - Texta-HeavyIt.otf",
                         "Latinotype - Texta-It.otf",
                         "Latinotype - Texta-Light.otf",
                         "Latinotype - Texta-LightIt.otf",
                         "Latinotype - Texta-Medium.otf",
                         "Latinotype - Texta-MediumIt.otf",
                         "Latinotype - Texta-Regular.otf",
                         "Latinotype - Texta-Thin.otf",
                         "Latinotype - Texta-ThintIt.otf",
        ]
    
        fontNames.forEach{registerFont(fileName: $0)}
    }

    static func registerFont(fileName: String) {
        guard let gFontRef = getFont(named: fileName) else {
            print("*** ERROR: ***")
            return
    }
    
    var errorRef: Unmanaged<CFError>? = nil
        if !CTFontManagerRegisterGraphicsFont(gFontRef, &errorRef) {
            print("*** ERROR: \(errorRef.debugDescription) ***")
        }
    }

    static func getFont(named fileName: String) -> CGFont? {
        let url = Bundle.module.url(forResource: fileName, withExtension: nil)
        guard let gUrl = url,
              let gFontData = NSData(contentsOf: gUrl),
              let gDataProvider = CGDataProvider(data: gFontData),
              let gFontRef = CGFont(gDataProvider) else {
                print("*** ERROR: ***")
                return nil
    }
    
        return gFontRef
    }
}

Just remember to add the font filename in the array.

EDIT: I've updated my answer.

✅ It is supported now

From Swift 5.3, you can add any resources to the target, including images, assets, fonts, zip, etc. Using the directory name will include all subfiles of the directory:

    .target(
        name: "ABUIKit",
        dependencies: [],
        resources: [.process("Resources") // <- this will add Resource directory to the target
        ]
    ),

Note that you should put the Resources folder under sources/packageName to make it recognizable.

⚠️ Also, Don't forget to register them!

You need to register fonts to make it work. So you can use frameworks like FontBlaster.

NOTE: You can always write your own code, but I prefer to use an existing framework and contribute my extra needs to them

So you call this somewhere early in the module's code (like some init method):

FontBlaster.blast(bundle: .module)

Then you can use the font inside or even outside of the module!


There is no need to name the font manually!

It will register all included fonts automatically. You can double-check the loaded fonts right after calling the blast method:

FontBlaster.loadedFonts.forEach { print("", $0) }
Related