Custom Fonts and Typography for Mobile Applications
Connecting a custom font to a mobile app sounds simple. But the path to production goes through licensing, correct file formats, loading without delays, Dynamic Type, fallback on load failure, and readability on small screens. Each point is a separate task.
Licensing: Most Common Problem Source
A font for a desktop site and a font for a mobile app have different licenses from most foundries. Google Fonts is free for mobile apps (OFL, Apache 2.0). Adobe Fonts via Creative Cloud requires license verification for app embedding. Commercial fonts like Helvetica Neue, Gotham, Brandon Grotesque require separate license for embedded use.
License violation is not just a legal risk. App Store Review Team sometimes rejects apps with unlicensed fonts if discovered. Check the license before starting work.
Formats and File Preparation
iOS accepts OTF and TTF. Android — TTF. React Native and Flutter — TTF/OTF.
If font comes in only one weight, but design uses Regular, Medium, SemiBold, Bold — need separate files for each weight. Fake bold (system synthetic bold) on mobile looks bad and can cause iOS rejection.
Variable Fonts (.ttf with fvar table) — progressive approach: one file covers entire weight and width range. iOS supports variable fonts from iOS 14, Android from API 26. React Native — partial support via fontVariationSettings. Flutter — via FontVariation in TextStyle.
Size optimization: if using only Latin and Cyrillic — subsetting via pyftsubset (fonttools) removes unused glyphs. Typical Latin+Cyrillic subset from full font: from 800KB to 150KB.
iOS Connection
Font is added to Bundle, registered in Info.plist under UIAppFonts key:
<key>UIAppFonts</key>
<array>
<string>CustomFont-Regular.otf</string>
<string>CustomFont-Bold.otf</string>
</array>
In SwiftUI used via .font(Font.custom("CustomFont-Regular", size: 16)). In UIKit — UIFont(name: "CustomFont-Regular", size: 16).
Common mistake: font name in code and Postscript Name of file don't match. Verify via CTFontManagerCopyAvailableFontFamilyNames() or Font Book on macOS.
Dynamic Type on custom font: UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont) — this scales custom font proportionally to system size settings. Without this, app won't pass accessibility audit.
Android Connection
Font goes to res/font/, used via XML or code:
val typeface = ResourcesCompat.getFont(context, R.font.custom_regular)
textView.typeface = typeface
In Compose:
val customFontFamily = FontFamily(
Font(R.font.custom_regular, FontWeight.Normal),
Font(R.font.custom_bold, FontWeight.Bold)
)
Text(text = "Hello", fontFamily = customFontFamily)
Downloadable Fonts via Google Fonts API — saves APK size, but requires internet on first launch and fallback font on network unavailability.
Typographic Scale
Custom font must be properly "tuned" in scale: match line height, letter spacing, optimal size for each text style. Not something done in an hour — need to test on real screens of different sizes and densities (iPhone SE vs iPhone 15 Pro Max, ldpi vs xxxhdpi on Android).
Minimum readable size on mobile: 12pt/sp for Caption, 14pt/sp for Body Small. Below — only in very specific cases.
For design handoff: all text styles are set up in Figma as Text Styles with full specification, passed to developer via Figma Dev Mode with specific values.
| Task | Timeline |
|---|---|
| Connect font + basic scale (one platform) | 1 day |
| Two platforms + Dynamic Type + variable font | 2–3 days |
| Full design system with typography | 3–5 days |
Cost is calculated individually after project analysis.







