'Caching images in expo is thowing Failed to load assets error in production

1

This is my code to cache all the images in my local folder. I'm prefetching all the images and after that resolving the promise. But this always results in "Failed to load all assets" error in Expo production mode.

 function cacheImages(images) {
  return images.map((image) => {
    if (typeof image === "string") {
      return Image.prefetch(image);
    } else {
      return Asset.fromModule(image).downloadAsync();
    }
  });
}
async function _loadAssetsAsync() {
    try {
      imageAssets = cacheImages([
        require("./ASSETS/Mariodimarko.jpg"),
        require("./ASSETS/SAMMY_WFX.jpg"),
      ]);

      await Promise.all([...imageAssets]);
    } catch (error) {
      console.warn(error);
      await Promise.all([...imageAssets]);
    }
  }

  return ready? (
    <NavigationContainer>
      <AuthProvider>
          <StackNavigator />
      </AuthProvider>
    </NavigationContainer>
  ) : (
    <AppLoading
      startAsync={_loadAssetsAsync}
      onFinish={() => setReady(true)}
      onError={(error) => console.warn(error)}
    />
  );


Solution 1:[1]

You are not defining your imageAssets or if you did out of the function, probably it was defined as const. This is generating an exception catched by your code.

Just add the keyword const before imageAssets

...
    try {
      const imageAssets = cacheImages([
        require("./ASSETS/Mariodimarko.jpg"),
        require("./ASSETS/SAMMY_WFX.jpg"),
      ]);

      await Promise.all([...imageAssets]);
    } catch (error) {
      console.warn(error);
      await Promise.all([...imageAssets]);
    }
...

Solution 2:[2]

If your paths to the images really exists, the only reason that i can imagine is due the dev enviroment. Let's try this:

const cacheImages = (images) => {
  return images.map(image => {
    if (typeof image === 'string') {
      return Image.prefetch(image);
    } else {
      return Asset.fromModule(image).downloadAsync();
    }
  });
};

const _loadAssetsAsync = async() => {

  const myImages = {
    a1: { asset: require("./ASSETS/Mariodimarko.jpg") },
    a2: { asset: require("./ASSETS/SAMMY_WFX.jpg") },
  };

  let arrayImages = [], assetInfo;

  for (const key in myImages) {

    assetInfo = await Asset.fromModule(myImages[key].asset);

    console.log(assetInfo); // just for you see the info

    if ( ( assetInfo.dowloaded ) && ( assetInfo.localUri !== null ) ) {
       /* this never happens in development mode even you 
       added the path to your app.json (assetBundlePatterns)
       and if it was previously downloaded in production mode, 
       you dont need to download it again,right?
       */
    }
    else {
       /* here we add the remote url to the arrayImages */
       arrayImages.push(assetInfo.uri);
    }

  };

  if ( arrayImages.length > 0 ){
    const imageAssets = cacheImages(arrayImages);
    try { await Promise.all([...imageAssets]);} 
    catch (error) { console.warn(error); }
  } 

};

    const [ready, setReady] = useState(false);

    if (ready) {
      return (
        <NavigationContainer>
          <AuthProvider>
            <StackNavigator />
          </AuthProvider>
        </NavigationContainer>
      );
    } else {
      return (
        <AppLoading
          startAsync={_loadAssetsAsync}
          onFinish={() => setReady(true)}
          onError={(error) => console.warn(error)}
        />
      );
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sylvio Ruiz Neto
Solution 2 Sylvio Ruiz Neto