Persistent font registration crashes when fonts are delivered via Apple-Hosted Background Assets

Hi everyone,

I’m trying to register fonts system-wide using CTFontManagerRegisterFontURLs with the .persistent scope. The fonts are delivered through Apple-Hosted Background Assets (since On-Demand Resources are deprecated).

Process-level registration works perfectly, but persistent registration triggers a system “Install Fonts” prompt, and tapping Install causes the app to crash immediately.

I’m wondering if anyone has successfully used Apple-Hosted Background Assets to provide persistent, system-wide installable fonts, or if this is a current OS limitation/bug.

What I Expect

  • Fonts delivered through Apple-Hosted Background Assets should be eligible for system-wide installation
  • Tap “Install” should install fonts into Settings → Fonts just like app-bundled or ODR fonts
  • App should not crash

Why This Matters

According to:

WWDC 2019: Font Management and Text Scaling

  • Developers can build font provider apps that install fonts system-wide, using bundled or On-Demand Resources.

WWDC 2025: Discover Apple-Hosted Background Assets

  • On-Demand Resources are deprecated, and AHBAs are the modern replacement.

Therefore, persistent font installation via Apple-Hosted Background Assets appears to be the intended path moving forward.

Question

Is this a known limitation or bug in iOS? Should .persistent font installation work with Apple-Hosted Background Assets? Do we need additional entitlement, manifest configuration, or packaging rules?

Any guidance or confirmation from Apple engineers would be greatly appreciated.

Additional Info

I submitted a Feedback including a minimal reproducible sample project:

FB21109320

Answered by DTS Engineer in 867153022
I submitted a Feedback including a minimal reproducible sample project:

I got your crash report from that. In that I see this:

Exception Type:  EXC_BREAKPOINT (SIGTRAP)

which strongly suggests that your app is crashing due to a trap, that is, some code has detected an inconsistency and deliberately crashed the process. The crashing thread looks like this:

Thread 3 name:   Dispatch queue: com.apple.NSXPCConnection.user.com.apple.FontServices.FontProviderLoader
Thread 3 Crashed:
0 libdispatch.dylib          … _dispatch_assert_queue_fail + 120
1 libdispatch.dylib          … dispatch_assert_queue$V2.cold.1 + 115
2 libdispatch.dylib          … dispatch_assert_queue + 107
3 libswift_Concurrency.dylib … _swift_task_checkIsolatedSwift + 47
4 libswift_Concurrency.dylib … swift_task_isCurrentExecutorWithFlagsImpl(swift::SerialExecutorRef, swift::swift_task_is_current_executor_flag) + 355
5 InstallFonts.debug.dylib   … closure #5 in ContentView.register(scope:) + 96
6 InstallFonts.debug.dylib   … thunk for @escaping @callee_guaranteed (@guaranteed CFArrayRef, @unowned Bool) -> (@unowned Bool) + 80
7 CoreText                   … invocation function for block in _CTFontManagerRegisterActionFontURLs(__CFArray const*, CTFontManagerScope, bool, Action, URLTrust, __CFArray const*, bool (__CFArray const*, bool) block_pointer) + 263
8 FontServices               … __FSFontProviderRegisterFonts_block_invoke + 67
9 FontServices               … __64-[FontProviderManager _registerFonts:enabled:completionHandler:]_block_invoke_2 + 99

Frame 7 is CTFont calling your callback, frames 6 and 5 are your code, and frames 4 through 0 are a Swift concurrency runtime check that your code is running on the expected actor. That check has failed, and hence the trap.

AFAICT this relates to this line of your code:

CTFontManagerRegisterFontURLs(cfArray, scope, true) { _, _ in true }

You have trailing closure that acts as a completion handler. Due to a lack of appropriate annotations in CoreText, Swift assumes that this closure will be called on the main actor. It is not, and hence the trap.

I’m not able to test this right now, but I suspect that the following will fix the crash:

CTFontManagerRegisterFontURLs(cfArray, scope, true) { @Sendable _, _ in true }

This tells Swift to form a sendable closure, which thus won’t include a main actor check.

Lemme know how this pans out because, if this does resolve the problem, we should repurpose FB21109320 to request a fix to the annotations on CTFontManagerRegisterFontURLs.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I submitted a Feedback including a minimal reproducible sample project:

I got your crash report from that. In that I see this:

Exception Type:  EXC_BREAKPOINT (SIGTRAP)

which strongly suggests that your app is crashing due to a trap, that is, some code has detected an inconsistency and deliberately crashed the process. The crashing thread looks like this:

Thread 3 name:   Dispatch queue: com.apple.NSXPCConnection.user.com.apple.FontServices.FontProviderLoader
Thread 3 Crashed:
0 libdispatch.dylib          … _dispatch_assert_queue_fail + 120
1 libdispatch.dylib          … dispatch_assert_queue$V2.cold.1 + 115
2 libdispatch.dylib          … dispatch_assert_queue + 107
3 libswift_Concurrency.dylib … _swift_task_checkIsolatedSwift + 47
4 libswift_Concurrency.dylib … swift_task_isCurrentExecutorWithFlagsImpl(swift::SerialExecutorRef, swift::swift_task_is_current_executor_flag) + 355
5 InstallFonts.debug.dylib   … closure #5 in ContentView.register(scope:) + 96
6 InstallFonts.debug.dylib   … thunk for @escaping @callee_guaranteed (@guaranteed CFArrayRef, @unowned Bool) -> (@unowned Bool) + 80
7 CoreText                   … invocation function for block in _CTFontManagerRegisterActionFontURLs(__CFArray const*, CTFontManagerScope, bool, Action, URLTrust, __CFArray const*, bool (__CFArray const*, bool) block_pointer) + 263
8 FontServices               … __FSFontProviderRegisterFonts_block_invoke + 67
9 FontServices               … __64-[FontProviderManager _registerFonts:enabled:completionHandler:]_block_invoke_2 + 99

Frame 7 is CTFont calling your callback, frames 6 and 5 are your code, and frames 4 through 0 are a Swift concurrency runtime check that your code is running on the expected actor. That check has failed, and hence the trap.

AFAICT this relates to this line of your code:

CTFontManagerRegisterFontURLs(cfArray, scope, true) { _, _ in true }

You have trailing closure that acts as a completion handler. Due to a lack of appropriate annotations in CoreText, Swift assumes that this closure will be called on the main actor. It is not, and hence the trap.

I’m not able to test this right now, but I suspect that the following will fix the crash:

CTFontManagerRegisterFontURLs(cfArray, scope, true) { @Sendable _, _ in true }

This tells Swift to form a sendable closure, which thus won’t include a main actor check.

Lemme know how this pans out because, if this does resolve the problem, we should repurpose FB21109320 to request a fix to the annotations on CTFontManagerRegisterFontURLs.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

Thank you for the detailed analysis — especially the breakdown of the crashing thread and explanation about the Swift concurrency actor expectation. That was extremely helpful.

I tested your suggestion:

CTFontManagerRegisterFontURLs(cfArray, scope, true) { @Sendable _, _ in true }

Result

This does prevent the crash.

The system “Install Fonts” sheet appears, and tapping Install no longer triggers a trap.

Remaining Issue

Although the crash is fixed, the fonts still do not install system-wide when using the .persistent scope with fonts delivered via Apple-Hosted Background Assets. After tapping Install, nothing appears under:

Settings → General → Fonts → My Fonts

So the concurrency crash is resolved, but the underlying issue remains:

  • .process registration works
  • .persistent registration does not install fonts
  • No errors are returned by CTFontManagerRegisterFontURLs

Next Steps

As you suggested, I am happy to have FB21109320 repurposed to track the missing concurrency annotation on CTFontManagerRegisterFontURLs, since adding @Sendable does avoid the trap.

Thanks again for the help — much appreciated.

Question: Is .persistent font registration intended to work with fonts delivered through Apple-Hosted Background Assets?

Historically, Apple supported this workflow using On-Demand Resources (per WWDC 2019 “Font Management and Text Scaling”).

However, ODR is deprecated and Apple-Hosted Background Assets are the modern replacement (per WWDC 2025 “Discover Apple-Hosted Background Assets”).

So my question is whether:

  1. .persistent font installation should work via AHBAs today, and there may be a bug preventing installation,

or

  1. .persistent installation is currently not supported for asset-pack-hosted fonts, and apps must instead ship those fonts in the app bundle until a future update adds support.

Having clarity on this expected behaviour will help a lot.

Thanks again for your guidance — it’s been extremely helpful.

Persistent font registration crashes when fonts are delivered via Apple-Hosted Background Assets
 
 
Q