Bundle Expo icon fonts

Hi all

This is for a managed-workflow app using Expo SDK 35, on Android and iOS.

Our app is unable, according to Sentry, to download the following assets:

Error: Could not download from https://d1wp6m56sqw74a.cloudfront.net/~assets/5a293a273bee8d740a045d
https://d1wp6m56sqw74a.cloudfront.net/~assets/b2e0fc821c6886fb3940f8
https://d1wp6m56sqw74a.cloudfront.net/~assets/a37b0c01c0baf1888ca812
etc…

They’re all true-type font links. Now, we’re following the documentation very closely for this, and are using icons in the way mentioned, like so. It’s a component that shows a loading screen while all async assets are loaded. Once all the promises resolve, it redirects to either login or main app.

class AuthLoadingScreen extends Component {
  componentDidMount() {
    this._handleLoading();
  }
  // other lifecycle methods
	
  _handleLoading = async() => {
    await this._loadResourcesAsync();
	// other async calls 
  }
​
  _loadResourcesAsync = async() => {
    const iconFonts = [
      Icon.MaterialIcons.font,
      Icon.Ionicons.font,
      Icon.MaterialCommunityIcons.font,
    ];
    const visuals = [
      require('../assets/images/icon.png'),
      // other images cut for brevity
    ];
​
    const cacheIconFonts = iconFonts.map(f => Font.loadAsync(f));
    const cacheVisuals = visuals.map(vis => Asset.fromModule(vis).downloadAsync());
​
    return Promise.all([...cacheVisuals, cacheIconFonts]);
  };
​
  render() {
    return (
      <View style={styles.container}>
        <ActivityIndicator size="large" color="#ffffff" />
      </View>
    );
  }
}
​
export default connect(mapStateToProps, mapDispatchToProps)(AuthLoadingScreen);

However, it appears that certain fonts, or certain parts of the fonts, aren’t being bundled with the App? They’re all Expo vector icons from @expo/vector-icons": "^10.0.0", and no other special fonts are being loaded.

Do Expo fonts have to be explicitly set to be bundled somehow with an assetBundlePattern in App.json? It seems strange to me to deep-link to a bundle pattern into node_modules. Please advise!

Thanks very much.

I thought I read at one point that they had to be bundled, e.g.,

    "assetBundlePatterns": [
      "node_modules/@expo/vector-icons/src/vendor/react-native-vector-icons/Fonts/Entypo.ttf",
      "node_modules/@expo/vector-icons/src/vendor/react-native-vector-icons/Fonts/MaterialIcons.ttf",
      "node_modules/@expo/vector-icons/src/vendor/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"
]

That always seemed odd to me, though, since I thought they would be already included by virtue of vector-icons already being natively linked into the binary Expo builds. Would also appreciate the clarification!

Is this in the documentation anywhere?

It doesn’t just seem odd… it seems totally wrong to include “hard-coded” node_module assets.

Hey all,

You don’t need to bundle the vector icons to use them, but doing so will result in them loading faster. @jdhorner can you share your app.json and username so we can investigate what’s going on with your asset cdn links? If the links were copied and pasted correctly, they should not throwing access denied errors.

Cheers,
Adam

Thanks Adam!

I’d definitely like to bundle them; to be honest I had assumed that was already done, since they’re being imported into components via the @expo/vector-icons package. Is that not actually the case? We use “amilia” as our username.

I’ve checked the logs, and for 52 separate events, it’s these three URLs

https://d1wp6m56sqw74a.cloudfront.net/~assets/5a293a273bee8d740a045d
https://d1wp6m56sqw74a.cloudfront.net/~assets/a37b0c01c0baf1888ca812
https://d1wp6m56sqw74a.cloudfront.net/~assets/b2e0fc821c6886fb3940f8

for a total of 18 unique devices,
all iOS running versions 11.4.1, 12.3.1, 12.4.1, 13.1.2, and 13.1.3

{
  "expo": {
    "name": "amilia",
    "icon": "./assets/images/icon.png",
    "version": "3.1.3",
    "description": "Easily Access your Amilia Account from your portable device.",
    "slug": "amilia-mobile",
    "sdkVersion": "35.0.0",
    "platforms": [
      "ios",
      "android"
    ],
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.amilia",
      "buildNumber": "3.1.3.0",
      "infoPlist": {
        "NSCameraUsageDescription": "This allows Amilia to use photos from your camera and library.",
        "NSPhotoLibraryAddUsageDescription": "This allows Amilia to use photos from your library.",
        "NSPhotoLibraryUsageDescription": "This allows Amilia to use photos from your library."
      }
    },
    "locales": {
      "fr": "./languages/french.app.json"
    },
    "android": {
      "package": "com.amilia",
      "versionCode": 1614313
    },
    "androidStatusBar": {
      "barStyle": "light-content",
      "backgroundColor": "#2B91E1"
    },
    "orientation": "portrait",
    "splash": {
      "image": "./assets/images/splash.png",
      "resizeMode": "cover",
      "backgroundColor": "#2B91E1"
    },
    "primaryColor": "#2B91E1",
    "updates": {
      "enabled": true,
      "checkAutomatically": "ON_LOAD",
      "fallbackToCacheTimeout": 10000
    },
    "assetBundlePatterns": [
      "assets/images/**/*"
    ],
    "privacy": "unlisted"
  }
}

To clarify, what I meant to say is that they “had to be bundled in order to be available offline”. Like, they behave just like any other asset, where we can either bundle or let them be downloaded from the server at runtime. Is that correct, or do these now somehow automatically end up in the build? I’ve seen Sentry errors where it fails to download one of the vector icon fonts, so I’m thinking it’s not.

OK, this is just so interesting to me.

Because they’re a node module, and the fonts are included there, and they’re imported into components after using Font.loadAsync, I 100% was not assuming that if I didn’t manually bundle them, they were being requested over the network still.

Put another way, I assumed this was the flow:

  1. package is listed as a project dependency in package.json

    "@expo/vector-icons": "^10.0.0"

  2. in our app’s “load all our assets” method, everything is loaded up and cached as per documentation:

import * as Icon from '@expo/vector-icons';
import * as Font from 'expo-font';
import { Asset } from 'expo-asset';

///

  _loadResourcesAsync = async() => {
    const iconFonts = [
      Icon.MaterialIcons.font,
      Icon.Ionicons.font,
      Icon.MaterialCommunityIcons.font,
    ];
    const visuals = [
      require('../assets/images/icon.png'),
      require('../assets/images/particles.png'),
      require('../assets/images/splash.png'),
      require('../assets/images/amilia-logo-white.png'),
    ];

    const cacheIconFonts = iconFonts.map(f => Font.loadAsync(f));
    const cacheVisuals = visuals.map(vis => Asset.fromModule(vis).downloadAsync());

    return Promise.all([...cacheVisuals, cacheIconFonts]);
  };
  1. Now later in our app, when we reference an icon, it should use the icon that’s been bundled and included and cached in our app, right? (Not make a network request to a CDN to fetch it??)
import { Icon } from 'react-native-elements';
/// 
  <Icon
    color="white"
    type="material-community"
    name="arrow-right"
/>

If the above assumptions aren’t correct, then I guess I just don’t understand how importing something into a component works. I had assumed @expo/vector-icons was actual, static resources and not links to a CDN.

assets in node_modules, whether they are in @expo/vector-icons or react-navigation or any other pkg, are treated exactly the same as assets in your project source code. if you bundle them with assetBundlePatterns then they are included in your standalone app directly. if you do not, then they are requested over the network at runtime.

1 Like

OK. I guess we’ll bundle them.

In the meantime, let me know if you guys have any insights as to why Sentry is reporting

Error value([native code])

errorCould not download from ‘https://d1wp6m56sqw74a.cloudfront.net/~assets/b2e0fc821c6886fb3940f85a3320003e

if you see anything obvious. Thanks!

there are a number of reasons you could see that error - maybe the user lost connection while downloading, for example. maybe dns problems. same sort of reasons why you might fail to download something when visiting a website

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.