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 (2)
I encountered this problem a few days ago, and as I have worked a bit with fonts before, I knew that there are a couple of common problems, but it was nice to find your article to confirm it was something relatively simple to fix or work around. Thanks! π₯°
I just want to point out, for anyone having problems with a font now showing, that you don't need to use the dynamic
useFonts
hook to debug this, just debug what is loaded through theexpo-font
config if you already have that in place.Also, if you're not able to actually modify the font you are using, you can use a workaround using the
Platform
fromreact-native
to use the correct name depending on the platform you are running on currently. But I wouldn't recommend this if you are able to modify the font, as it's a bit unruly to manage in the long run. π€Here's an slightly tweaked example showing what I mean
In app.json I load two different fonts
In my react component, I debug the actual names of the fonts using the
ExpoFont.getLoadedFonts()
as was shown in the original article, and then I use the correct name depending on the platform I'm running on currently. As you can see, the "SpaceMono-Regular" was the correct name for both iOS and Android, while the other font had a name that differed just slightly. π¬Oh, another thing I forgot to mention.
I had problems installing Fontforge on my M1 mac using
Homebrew
as it was complaining on a couple of things, so I searched for some other way to fix this. And I found this python script that can help with renaming the font "Full name" property, if you don't want or can't installFontforge
.I haven't tried the script my self just yet, but I hope this can help someone having this issue. π₯°
github.com/chrissimpkins/fontname....