Skip to content

Constant-driven MTFontManager API + caller migration#210

Merged
kostub merged 8 commits into
masterfrom
feature/add-math-fonts-pr2
May 31, 2026
Merged

Constant-driven MTFontManager API + caller migration#210
kostub merged 8 commits into
masterfrom
feature/add-math-fonts-pr2

Conversation

@kostub
Copy link
Copy Markdown
Owner

@kostub kostub commented May 30, 2026

Goal

Replace the three per-font convenience methods with public MTFontName* constants + the generic fontWithName:size: (keeping defaultFont), and migrate every in-repo caller. Breaking public API change.

Plan: docs/plans/2026-05-30-add-math-fonts.md (items 9–14)
LLD: docs/lld/2026-05-30-add-math-fonts.md

Stack

Commits

  • [item 9] Add public MTFontName* font-name constants
  • [item 10] Remove per-font convenience methods; route defaultFont via constant
  • [item 11] Migrate iOS example to fontWithName: + applyFontWithName:
  • [item 12] Migrate SwiftMathExample MathFont to fontWithName: + constants
  • [item 13] Update README usage snippet to fontWithName: + constants
  • [item 14] Note breaking font-API change and new fonts in CHANGELOG

Breaking change

latinModernFontWithSize:, xitsFontWithSize:, termesFontWithSize: removed. Callers migrate to fontWithName:size: + MTFontName* constants. defaultFont is unchanged.

Testing

265 tests, 0 failures (swift test). iOS example builds (xcodebuild BUILD SUCCEEDED). SwiftMathExample builds against the new enum.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors font loading in MTFontManager by removing the three per-font convenience methods (latinModernFontWithSize:, xitsFontWithSize:, and termesFontWithSize:) in favor of a generic fontWithName:size: accessor that uses newly introduced public font name constants. It also adds five new OpenType MATH fonts, updates the documentation, and adapts the example projects and tests. The review feedback highlights that MTFontManager.fontManager() should be used instead of instantiating new MTFontManager objects in Swift code and documentation to ensure caching is not bypassed. Additionally, hardcoded string literals in ViewController.m should be replaced with the new public font name constants.

Comment on lines +57 to +59
func font(size: CGFloat) -> MTFont? {
MTFontManager().font(withName: fontName, size: size)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using MTFontManager() creates a new instance of MTFontManager every time font(size:) is called. This bypasses the singleton instance and defeats the caching mechanism of nameToFontMap, leading to redundant font loading and parsing overhead. Use the shared fontManager() singleton instead.

Suggested change
func font(size: CGFloat) -> MTFont? {
MTFontManager().font(withName: fontName, size: size)
}
func font(size: CGFloat) -> MTFont? {
MTFontManager.fontManager().font(withName: fontName, size: size)
}
References
  1. Use the shared singleton instance MTFontManager.fontManager() instead of creating a new MTFontManager instance to leverage the built-in caching mechanism and avoid performance degradation from re-parsing font files.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 73e3fc4. Switched to MTFontManager.fontManager() so the shared nameToFontMap cache is reused instead of re-parsing the font on every font(size:) call.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to the previous reply: MTFontManager.fontManager() does not compile in Swift. Because the Obj-C + (instancetype) fontManager is a factory method whose name matches the class, Swift's Clang importer collapses it into init() and marks fontManager() as unavailable ('fontManager()' has been replaced by 'init()'). So MTFontManager() was actually the only spelling Swift offered — and it resolved to -init, producing a fresh, cache-bypassing instance. The original report was correct on both counts.

Properly fixed in 1956418: fontManager is now a class property (@property (class, readonly, strong) MTFontManager *fontManager;) so Swift imports it as MTFontManager.fontManager (no parens, no init() collapse), and init/new are NS_UNAVAILABLE so the bypass can't be written at all. This call site now uses MTFontManager.fontManager.font(withName:size:).

Verified: swift test (265 tests), the iOS Obj-C example, and the SwiftMathExample app all build.

Comment thread README.md Outdated

```swift
label.font = MTFontManager().termesFont(withSize: 20)
label.font = MTFontManager().font(withName: MTFontNameTermes, size: 20)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In Swift, calling MTFontManager() instantiates a new font manager every time, bypassing the caching mechanism of the singleton. It is highly recommended to use the shared fontManager() instance instead to ensure optimal performance.

Suggested change
label.font = MTFontManager().font(withName: MTFontNameTermes, size: 20)
label.font = MTFontManager.fontManager().font(withName: MTFontNameTermes, size: 20)
References
  1. Use the shared singleton instance MTFontManager.fontManager() instead of creating a new MTFontManager instance to leverage the built-in caching mechanism and avoid performance degradation from re-parsing font files.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 73e3fc4. Updated the Swift snippet to MTFontManager.fontManager() (the Obj-C snippet below already used the singleton).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to the previous reply: the MTFontManager.fontManager() snippet does not compile in Swift — the Obj-C +fontManager factory method is imported as init(), making fontManager() unavailable. Fixed properly in 1956418 by exposing the singleton as a class property; the README Swift snippet now reads MTFontManager.fontManager.font(withName:size:) (no parens), and init/new are NS_UNAVAILABLE so a non-singleton instance can't be constructed.

Comment thread iosMathExample/example/ViewController.m Outdated
Comment on lines +327 to +329
static NSString *const kFontKeys[] = {
@"latinmodern-math", @"texgyretermes-math", @"xits-math",
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since this PR introduces public constants for the font names (MTFontNameLatinModern, MTFontNameTermes, MTFontNameXITS), we should use them here instead of hardcoded string literals to prevent potential mismatch bugs and improve maintainability. Note that because these are extern constants, we should remove the static keyword from the array definition to avoid compiler errors regarding non-constant initializers.

    NSString *const kFontKeys[] = {
        MTFontNameLatinModern, MTFontNameTermes, MTFontNameXITS,
    };

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 73e3fc4. kFontKeys now uses MTFontNameLatinModern/MTFontNameTermes/MTFontNameXITS, and dropped static since extern const NSString* aren't compile-time-constant initializers. Verified the iOS example target still builds.

@kostub kostub force-pushed the feature/add-math-fonts-pr2 branch from 6e6fb29 to 8b36f91 Compare May 30, 2026 18:49
@kostub kostub changed the base branch from feature/add-math-fonts-pr1 to master May 30, 2026 18:49
kostub and others added 2 commits May 31, 2026 00:29
Address PR #210 review:
- MathLabel.swift / README: call MTFontManager.fontManager() instead of
  MTFontManager(), so the shared nameToFontMap cache is reused rather than
  re-parsing the font file on every font(size:) call.
- iOS example: key the picker off the public MTFontName* constants instead
  of hardcoded loader-key literals. Drops `static` since extern const
  NSString* are not compile-time-constant array initializers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MTFontManager() in Swift bypassed the singleton: the ObjC factory method
+fontManager was imported as init(), so MTFontManager() called -init and
produced a fresh, cache-bypassing instance, while the explicit fontManager()
spelling was marked unavailable.

Expose the shared instance as a class property instead of a +fontManager
factory method so Swift imports it as MTFontManager.fontManager (no parens,
no init() collapse), and mark init/new NS_UNAVAILABLE so the bypass can't be
written. The getter now builds the instance via dispatch_once + a private
-initPrivate. ObjC callers using [MTFontManager fontManager] are unaffected.

Update the Swift callers (MathLabel, README, Swift API tests) to the property
form and assert singleton identity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kostub kostub merged commit 254de59 into master May 31, 2026
1 check passed
@kostub kostub deleted the feature/add-math-fonts-pr2 branch May 31, 2026 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant