DEV Community

Aymeric M
Aymeric M

Posted on

1

The Mystery of Fonts on iOS: Why Was My App Not Displaying Custom Fonts?

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>
Enter fullscreen mode Exit fullscreen mode

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());
}, []);
Enter fullscreen mode Exit fullscreen mode

The result was surprising:

Fonts loaded on iOS: [
  "BasierCircle-Regular",
  "font000000002d6f7cbc",
  "BasierCircle-SemiBold",
  "SwearDisplay-Bold"
]
Enter fullscreen mode Exit fullscreen mode

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:

  1. Open FontForge and load the font (.ttf or .otf).
  2. Go to Element β†’ Font Info.
  3. Modify the "Full Name" field to match the exact desired name (e.g., BasierCircle-Regular).
  4. 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',
        ],
      },
    ],
  ],
});
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
munchyydl profile image
Marcus Christensen β€’ β€’ Edited

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 the expo-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 from react-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

    "plugins": [
      [
        "expo-font",
        {
          "fonts": [
            "./assets/fonts/SpaceMono-Regular.ttf",
            "./assets/fonts/Cocogoose-Pro-Bold.otf"
          ]
        }
      ]
    ],
Enter fullscreen mode Exit fullscreen mode

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. 😬

import * as ExpoFont from "expo-font";
import { useEffect } from "react";
import { Platform, StyleSheet, Text, View } from "react-native";

export default function Test() {
  useEffect(() => {
    console.log(`Fonts loaded on ${Platform.OS}:`, ExpoFont.getLoadedFonts());
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.header1}>Header1</Text>
      <Text style={styles.header2}>Header2</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#5638d1",
    alignItems: "center",
    justifyContent: "center",
  },
  header1: {
    fontFamily: "SpaceMono-Regular",
    fontSize: 48,
    color: "#efefef",
  },
  header2: {
    fontFamily:
      Platform.OS === "ios" ? "CocogoosePro-Bold" : "Cocogoose-Pro-Bold",
    fontSize: 48,
    color: "#efefef",
  }
});
Enter fullscreen mode Exit fullscreen mode
Collapse
 
munchyydl profile image
Marcus Christensen β€’

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 install Fontforge.

I haven't tried the script my self just yet, but I hope this can help someone having this issue. πŸ₯°

github.com/chrissimpkins/fontname....