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:
KanitRegular.ttf
(PostScript name is Kanit-Regular
) here.Kanit-Regular
in the asset catalog.Kanit-Regular.ttf
and put it into the data asset.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
}
}
CTFontManager
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?
Got it working by making the following:
Kanit-Regular
data asset with fonts
On-Demand Resource Tag (tag name can be the name of your preference, fonts
is just the example).fonts
tag into Initial Install Tags
Prefetched Resource Tags sectionFonts
Capability in Signing & Capabilities
and tick all boxes in itlike 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.