Assets not caching in production [CRITICAL]

SDK Version: 42
Platforms(Android/iOS/web/all): Android & iOS

I’m using EAS and SDK 42. And I got this problem with my Assets not loading when not online. On Android Vector Icons do not show when offline. And on iPhone it just hangs on AppLoading.

Is there any changes in AppLoading or any caching that has changed?

It seems like assets or some assets are not cached anymore and makes this thing happen.

And being able to work offline is pretty crucial for the app.

App.js goes here:

import AppLoading from 'expo-app-loading';
import * as SplashScreen from 'expo-splash-screen';
import { Asset } from 'expo-asset';
import * as Font from 'expo-font';
import React, { useState } from 'react';
import { StyleSheet, View, Text, Platform, } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { Ionicons, MaterialIcons, FontAwesome, MaterialCommunityIcons, Foundation } from '@expo/vector-icons';
import AppNavigator from './navigation/AppNavigator';
import Routes from './assets/data/data.json';
import { AppearanceProvider, Appearance } from 'react-native-appearance';
import * as Localization from 'expo-localization';
import i18n from 'i18n-js';
import Translations from './assets/data/translations.json';

export default function App(props) {
	Text.defaultProps = Text.defaultProps || {};
	Text.defaultProps.allowFontScaling = false;
	
	Text.defaultProps.style = {fontFamily: 'montserrat-regular'};
	
	const [isLoadingComplete, setLoadingComplete] = useState(false);

	i18n.translations = Translations;
	i18n.locale = Localization.locale;
	i18n.fallbacks = true;

	if (!isLoadingComplete && !props.skipLoadingScreen) {
		return (
			<AppearanceProvider>
				<AppLoading
					startAsync={loadResourcesAsync}
					onError={handleLoadingError}
					onFinish={() => handleFinishLoading(setLoadingComplete)}
				/>
			</AppearanceProvider>
		);
	} else {
		return (
			<AppearanceProvider>
				<View style={styles.container}>
					<StatusBar 
//						style= {'light'}
						style= {Appearance.getColorScheme() === 'dark' ? 'dark' : 'light'}
						hidden={false} 
						animated={false} 
						networkActivityIndicatorVisible={true} 
//						backgroundColor = {Appearance.getColorScheme() === 'dark' ? '#333' : 'black'}
						translucent={true}
					/>
					<AppNavigator />
				</View>
			</AppearanceProvider>
		);
	}
}

async function loadResourcesAsync() {
	await Promise.all([
		Asset.loadAsync([
			require('./assets/images/splash.png'),
			require('./assets/images/splash-dark.png'),
			require('./assets/images/icon.png'),
			require('./assets/images/icon-adaptive.png'),
			require('./assets/images/vegan.png'),
			require('./assets/images/vegetarian.png'),
			require('./assets/images/vegetarian-dark.png'),
			require('./assets/images/cauldron.png'),
			require('./assets/images/cauldron-dark.png'),
			require('./assets/images/tabacos.png'),
			require('./assets/images/tabacos-lightgray.png'),
			require('./assets/images/qr-400.png'),
//			require('./assets/images/yellow.png'),
//			require('./assets/images/loading.gif'),
		]),
		Font.loadAsync({
			...Ionicons.font,
			...MaterialIcons.font,
			...FontAwesome.font,
			...MaterialCommunityIcons.font,
			...Foundation.font,
			'montserrat-regular': require('./assets/fonts/Montserrat-Regular.ttf'),
			'montserrat-bold': require('./assets/fonts/Montserrat-Bold.ttf'),
			'montserrat-black': require('./assets/fonts/Montserrat-Black.ttf'),
		}),
	]);
}

function handleLoadingError(error) {
  console.warn(error);
}

function handleFinishLoading(setLoadingComplete) {
  setLoadingComplete(true);
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		backgroundColor: Appearance.getColorScheme() === 'dark' ? 'black' : 'white',
	},
});

Both Asset.loadAsync and Font.loadAsync hangs - even if they have been loaded before if there is no internet connectivity.

They should be cached if already loaded, and should not even try to download the assets.

But it seems they do or just hangs for some other reason if no internet available.

My package.json uses:
“expo-asset”: “~8.3.3”
“expo-font”: “~9.2.1”

Tried all of the statements individually and they all hang:

		await Asset.loadAsync([
			require('./assets/images/splash.png'),
			require('./assets/images/splash-dark.png'),
			require('./assets/images/icon.png'),
			require('./assets/images/icon-adaptive.png'),
			require('./assets/images/vegan.png'),
			require('./assets/images/vegetarian.png'),
			require('./assets/images/vegetarian-dark.png'),
			require('./assets/images/cauldron.png'),
			require('./assets/images/cauldron-dark.png'),
			require('./assets/images/tabacos.png'),
			require('./assets/images/tabacos-lightgray.png'),
			require('./assets/images/qr-400.png'),
		]);

		await Font.loadAsync({
			...Ionicons.font,
			...MaterialIcons.font,
			...FontAwesome.font,
			...MaterialCommunityIcons.font,
			...Foundation.font,
		});

		await Font.loadAsync({
			'montserrat-regular': {uri: require('./assets/fonts/Montserrat-Regular.ttf')},
			'montserrat-bold': {uri: require('./assets/fonts/Montserrat-Bold.ttf')},
			'montserrat-black': {uri: require('./assets/fonts/Montserrat-Black.ttf')},
		});

It is critical because there is currently 10,000 active users hiking with this app. And it is supposted to work in areas like mountains without cell phone coverage.

But on iPhone it hangs without cell pohne coverage, and on Android it loads without assets with no cell phone coverage.

The problem started when I upgraded to SDK 42 and EAS. The problem has not been resoved even though I posted previously.

@notbrent and @wodin you guys seems pretty sharp on theese issues usually?

Hi @caminoninja

You posted this at 03:16 this morning, my time :wink:
Also, bear in mind that I am just another Expo user and I don’t always hang out here.

I took your code and slimmed it down a bit to get this:

import AppLoading from 'expo-app-loading';
import { Asset } from 'expo-asset';
import * as Font from 'expo-font';
import React, { useState } from 'react';
import { Image, Platform, StyleSheet, Text, View } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { Ionicons } from '@expo/vector-icons';

export default function App() {
  const [isLoadingComplete, setLoadingComplete] = useState(false);

  if (!isLoadingComplete) {
    return (
      <AppLoading
        startAsync={loadResourcesAsync}
        onError={(error) => console.log(error)}
        onFinish={() => setLoadingComplete(true)}
      />
    );
  } else {
    return (
      <View style={styles.container}>
        <StatusBar
          style={'light'}
          hidden={false}
          animated={false}
          networkActivityIndicatorVisible={true}
          translucent={true}
        />
        <Image
          style={{ width: 50, height: 50 }}
          source={require('./assets/images/icon.png')}
        />
        <View>
          <Text style={{ fontFamily: 'montserrat-regular' }}>Regular</Text>
          <Text style={{ fontFamily: 'montserrat-bold' }}>Bold</Text>
          <Text style={{ fontFamily: 'montserrat-black' }}>Black</Text>
        </View>
        <View
          style={{
            flexDirection: 'row',
            justifyContent: 'space-around',
            alignItems: 'center',
          }}
        >
          <Ionicons name="md-checkmark-circle" size={32} color="black" />
          <Ionicons name="add-circle" size={32} color="black" />
          <Ionicons name="airplane" size={32} color="black" />
          <Ionicons name="alarm-outline" size={32} color="black" />
          <Ionicons name="alert-circle-outline" size={32} color="black" />
          <Ionicons name="arrow-back-circle" size={32} color="black" />
        </View>
      </View>
    );
  }
}

async function loadResourcesAsync() {
  await Promise.all([
    Asset.loadAsync([
      require('./assets/images/splash.png'),
      require('./assets/images/icon.png'),
      require('./assets/images/icon-adaptive.png'),
    ]),
    Font.loadAsync({
      ...Ionicons.font,
      'montserrat-regular': require('./assets/fonts/Montserrat-Regular.ttf'),
      'montserrat-bold': require('./assets/fonts/Montserrat-Bold.ttf'),
      'montserrat-black': require('./assets/fonts/Montserrat-Black.ttf'),
    }),
  ]);
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'space-around',
    alignItems: 'center',
    backgroundColor: 'white',
  },
});

I built it with eas build --platform=android and installed the resulting aab on my Samsung Galaxy S8 phone.

If I enable airplane mode, clear the cache and data for the app and then start it, it still shows the image, fonts and icons.

I wonder if your problem is related to OTA updates?

@caminoninja Does my example work for you? If you can create a minimal example that demonstrates the problem you should probably create an issue on github in the expo/expo repository.

Hmm …yeah OTA might …I have my app.json set up like this:

{
    "expo": {
      "updates": {
        "enabled": true,
        "checkAutomatically": "ON_ERROR_RECOVERY",
        "fallbackToCacheTimeout": 0
      }
    }
}

Might be outdated.

I’ll try your sample tonight. I’m in Spain but still I think it was pretty late :slight_smile:

Sorry for taking you for a staff pro. You are just alwas pretty sharp on the debugging.

Maybe it fails on shutdown and tries to do an ON_ERROR_RECOVERY on startup