iosswiftobjective-ccore-textasset-catalog

What's the definition of Font Asset in the Asset Catalog?


Starting from iOS 13, CTFontManager has the following function:

@discussion Font assets are extracted from the asset catalog and registered. This call must be made after the completion handler of either NSBundleResourceRequest beginAccessingResourcesWithCompletionHandler: or conditionallyBeginAccessingResourcesWithCompletionHandler: is called successfully.
Name the assets using Postscript names for individual faces, or family names for variable/collection fonts. The same names can be used to unregister the fonts with CTFontManagerUnregisterFontDescriptors. In iOS, fonts registered with the persistent scope are not automatically available to other processes. Other process may call CTFontManagerRequestFonts to get access to these fonts.

@param      fontAssetNames
            Array of font name assets in asset catalog.

...

CTFontManagerRegisterFontsWithAssetNames(_ fontAssetNames: CFArray, _ bundle: CFBundle?, _ scope: CTFontManagerScope, _ enabled: Bool, _ registrationHandler: ((CFArray, Bool) -> Bool)?)

However, Asset Catalog does not have any way to add "Font Assets".

What I've tried:

Data Asset's Contents.json now looks like this:

{
   "data" : [
    {
      "filename" : "Kanit-Regular.ttf",
      "idiom": "universal",
      "universal-type-identifier" : "public.truetype-ttf-font"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

like this:

func registerFont() {
    var cfBundle: CFBundle?
    if let bundle = Bundle(for: type(of: self)) {
        cfBundle = CFBundleCreate(kCFAllocatorDefault, bundle.bundleURL as CFURL)
    }
    CTFontManagerRegisterFontsWithAssetNames(["Kanit-Regular"] as CFArray, cfBundle, .persistent, true) { (errors, done) -> Bool in
        print(errors)
        return done
    }
}

After this, getting errors printed:

▿ 1 element
  - 0 : Error Domain=NSPOSIXErrorDomain Code=22 "Invalid argument" UserInfo={CTFontManagerErrorFontAssetNameKey=(
    "Kanit-Regular"
)}

Is there any way to make it work?


Solution

  • Got it working by making the following:

    Initial Install Tags Resource Tags section

    enter image description here

    like this:

    func registerFont() {
        var cfBundle: CFBundle?
        var resourceRequest: NSBundleResourceRequest?
        if let bundle = Bundle(for: type(of: self)) {
            resourceRequest = NSBundleResourceRequest(tags: Set(arrayLiteral: "fonts"), bundle: bundle)
            cfBundle = CFBundleCreate(kCFAllocatorDefault, bundle.bundleURL as CFURL)
        }
        resourceRequest?.beginAccessingResources() { error in
            if let error = error {
                print(error)
            } else {
                CTFontManagerRegisterFontsWithAssetNames(["Kanit-Regular"] as CFArray, cfBundle, .persistent, true) { (errors, done) -> Bool in
                    print(errors)
                    return done
                }
            }
        }
    }
    

    Outcome

    When scope is passed as .persistent or .user, on initial call of CTFontManagerRegisterFontsWithAssetNames function the user will be asked to install fonts into the system, which is not what I really need. In case the scope is .process or .none, the same errors output is returned as provided at the end of the question.

    Although this function does not fit my needs, I at least validated that it is working. Maybe someone finds it useful.