A Frustrating Bug on iOS
Recently, while working on a mobile application with React Native and Expo, I encountered an unexpected issue: my custom fonts were not displaying on iOS. Yet, everything worked perfectly on Android.
After hours of debugging and investigation, I discovered that iOS does not recognize fonts by their file name but rather by their "Full Name", a subtlety that does not exist on Android.
In this article, I'll explain the cause of this issue, how to identify it, and most importantly, how to fix it.
Understanding Font Behavior on iOS
On Android, using a custom font is relatively straightforward. You define the font in expo-font
using the file path, and it can be referenced directly by its filename:
import { useFonts } from 'expo-font';
import { Text } from 'react-native';
const [fontsLoaded] = useFonts({
'BasierCircle-Regular': require('./assets/fonts/BasierCircle-Regular.ttf'),
});
if (!fontsLoaded) {
return null;
}
<Text style={{ fontFamily: 'BasierCircle-Regular' }}>Hello, world!</Text>
Problem: This code does not always work on iOS. The reason is that iOS does not identify fonts by their file name (BasierCircle-Regular.ttf
), but rather by their internal font name.
Identifying the Issue
To check which fonts were actually loaded by iOS, I added a simple console.log()
in my code:
useEffect(() => {
console.log('Fonts loaded on iOS:', ExpoFont.getLoadedFonts());
}, []);
The result was surprising:
Fonts loaded on iOS: [
"BasierCircle-Regular",
"font000000002d6f7cbc",
"BasierCircle-SemiBold",
"SwearDisplay-Bold"
]
Some font names appeared as hexadecimal identifiers (font000000002d6f7cbc
) instead of the expected names!
What was happening was that iOS was not using the correct font name but instead generating an identifier.
The Solution: Editing the "Full Name" of the Font
To fix this, I used FontForge, an open-source software that allows you to edit font metadata.
Steps to follow:
-
Open FontForge and load the font (
.ttf
or.otf
). - Go to Element → Font Info.
- Modify the "Full Name" field to match the exact desired name (e.g.,
BasierCircle-Regular
). - Save the font and re-import it into the application.
Proper Integration in Expo
Once the name was corrected, I declared my fonts in app.config.ts
(instead of using require()
in my code):
export default ({ config }) => ({
...config,
plugins: [
[
'expo-font',
{
fonts: [
'./assets/fonts/BasierCircle-Regular.ttf',
'./assets/fonts/BasierCircle-Medium.ttf',
'./assets/fonts/BasierCircle-SemiBold.ttf',
'./assets/fonts/BasierCircle-Bold.ttf',
],
},
],
],
});
This avoids issues related to manually loading fonts and ensures cleaner management via expo-font.
Conclusion
This bug highlights a crucial difference between iOS and Android in handling custom fonts. To avoid this type of issue, here are some best practices to keep in mind:
✅ Always check a font's "Full Name" on iOS using FontForge.
✅ Declare fonts in app.config.ts
rather than loading them with require()
.
✅ Use ExpoFont.getLoadedFonts()
to quickly debug font loading.
🚀 With these adjustments, my app now displays fonts correctly on all platforms!
Useful Resources
If you've encountered a similar issue, share your experience in the comments!
Top comments (0)