Closed efkan closed 1 year ago
Additional info: on our app's release version, the images became visible after getting an OTA update.
Found another clue; I didn't run across the asset issue on SDK v47.0.14.
However, it persists on v48.
The example repo has a local update server as the URL, but an EAS project ID. Where are the updates served from?
Please add instructions to your README explaining how you set up the project before building the app. Did you use EAS build, or a local build?
The repo might be dysfunctional. But we encountered this issue while we’re implementing expo-updates module.
Yes I used “eas build” command to check it if it works as expected.
Please note my comment: https://github.com/expo/expo/issues/22656#issuecomment-1571311387
Hello, we encountered the same issue, while adding sentry into our project, in the docs theres an instruction to install also expo updates module. If we remove the expo-updates, images are shown on release build, and if we leave the expo-updates in package.json the images are not displayed. We are not using EAS build.
Same issue here. We are attempting to add expo-updates
in our bare react-native app. We can reproduce in release build on simulator. We don't even need to add and remove the package like @asphyxiar to reproduce it. Just enable and disable the flag in Expo.plist
can see the issue:
Disabled:
Enabled:
Note that the custom update server http://localhost:3000/api/manifest
does not exist yet. We are in the beginning of the process of integrating expo-updates
. Anyway, if the url call fails, I think expo should just use bundled javascript and assets, right? Now it seems that no image is bundled and found.
Libraries versions we are using:
"expo": "^48.0.19",
"expo-updates": "~0.16.4"
Thank you for filing this issue! This comment acknowledges we believe this may be a bug and there’s enough information to investigate it. However, we can’t promise any sort of timeline for resolution. We prioritize issues based on severity, breadth of impact, and alignment with our roadmap. If you’d like to help move it more quickly, you can continue to investigate it more deeply and/or you can open a pull request that fixes the cause.
I can reproduce the issue, and we will take a look.
ENG-9073
We're seeing a similar issue but only image assets from node_modules
like the flags in react-native-country-flag
I was not reproducing this issue in current main Expo code. Now that the SDK 49 beta is out, I will test to see if the issue still shows up there.
I forgot to mention we're using Expo Updates on a vanilla RN app.
"expo": "^48.0.0",
"expo-updates": "~0.16.4",
@sregg @efkan @asphyxiar I did some more investigation, and now this appears to be fixable with some changes to the way you are calling expo-asset
APIs. useAssets()
returns an array, so I import all the assets with a single call to that hook, and then each image component references the localUri
property of one of the assets in the array.
It may be that the vanilla React Native Image
component can be used too, but the API for expo-image
is very similar.
With the following changes to the example app, the images do show up in a release build as expected.
diff --git a/package.json b/package.json
index 67c9d98..7471922 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,10 @@
"test": "jest"
},
"dependencies": {
- "expo": "^48.0.0",
+ "expo": "~48.0.18",
"expo-asset": "~8.9.1",
"expo-constants": "~14.2.1",
+ "expo-image": "^1.3.1",
"expo-updates": "~0.16.4",
"react": "18.2.0",
"react-native": "0.71.8"
diff --git a/App.tsx b/App.tsx
index 29a3539..4090290 100644
--- a/App.tsx
+++ b/App.tsx
@@ -5,10 +5,10 @@
* @format
*/
+import { Image } from 'expo-image';
import React from 'react';
-import type {PropsWithChildren} from 'react';
+import type { PropsWithChildren } from 'react';
import {
- Image,
SafeAreaView,
ScrollView,
StatusBar,
@@ -18,7 +18,7 @@ import {
View,
} from 'react-native';
-import {Asset, useAssets} from 'expo-asset';
+import { Asset, useAssets } from 'expo-asset';
import {
Colors,
@@ -32,7 +32,7 @@ type SectionProps = PropsWithChildren<{
title: string;
}>;
-function Section({children, title}: SectionProps): JSX.Element {
+function Section({ children, title }: SectionProps): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
@@ -42,7 +42,8 @@ function Section({children, title}: SectionProps): JSX.Element {
{
color: isDarkMode ? Colors.white : Colors.black,
},
- ]}>
+ ]}
+ >
{title}
</Text>
<Text
@@ -51,7 +52,8 @@ function Section({children, title}: SectionProps): JSX.Element {
{
color: isDarkMode ? Colors.light : Colors.dark,
},
- ]}>
+ ]}
+ >
{children}
</Text>
</View>
@@ -61,8 +63,9 @@ function Section({children, title}: SectionProps): JSX.Element {
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
- const [img] = useAssets([
+ const [img, error] = useAssets([
require('./assets/images/category/_gamingNotebooks.png'),
+ require('./assets/GradientTop.png'),
]);
// console.log(img);
@@ -79,30 +82,30 @@ function App(): JSX.Element {
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
- style={backgroundStyle}>
+ style={backgroundStyle}
+ >
<Header />
- <Image
- style={$SLIDE_LIST_IMAGE}
- source={img || {}}
- alt={'Gaming Notebooks'}
- resizeMode="cover"
- />
- <Image
- style={$SLIDE_LIST_IMAGE}
- source={require('./assets/images/category/_gamingNotebooks.png')}
- alt={'Gaming Notebooks'}
- resizeMode="cover"
- />
- <Image
- style={$SLIDE_LIST_IMAGE}
- source={require('./GradientTop.png')}
- alt={'Gaming Notebooks'}
- resizeMode="cover"
- />
+ {img && img.length > 0 ? (
+ <Image
+ style={$SLIDE_LIST_IMAGE}
+ source={img[0].localUri}
+ alt={'Gaming Notebooks'}
+ contentFit="cover"
+ />
+ ) : null}
+ {img && img.length > 1 ? (
+ <Image
+ style={$SLIDE_LIST_IMAGE}
+ source={img[1].localUri}
+ alt={'GradientTop'}
+ contentFit="cover"
+ />
+ ) : null}
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
- }}>
+ }}
+ >
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
We're not using expo-image
nor expo-assets
.
Moreover, the issue comes from assets in node_modules
so we don't have access to that code.
Is there a way to disable assets in updates altogether?
I was also experiencing this issue when I added expo-updates to our project. Didn't realize that's what was causing it for about a week and a half, after trying every conceivable solution I could find to fix the problem. Turned out, once I uninstalled expo-updates, the issue went away and my images went back to rendering properly on IOS & Android.
I also did not use expo-assets, or expo-image in my project before (when this worked flawlessly) or after (when I fixed it).
Was occurring consistently on IOS builds, but occasionally would happen on Android builds when I tried pushing out an expo-update call. Seems to me that the expo-updates app is messing with the hashes of the images & causing them to not be found.
Thanks @sregg -- I'll take a look specifically at what is happening with images that are coming from node_modules
paths. Once I have a repro, I'll create a separate issue to track that problem.
@Bengejd is your issue also with images coming from node_modules
paths? If you could describe specifically what you did when you were "pushing out an expo-update call" (or even provide a sample app that shows the problem), that would be great!
My issue is coming from local assets, e.g: assets/logo-small.jpg
when I have the expo-updates
package installed, and then using the local assets like so:
assets/images.tsx
export const IMAGES: {
LOGO_SMALL: number;
...
} = {
LOGO_SMALL: require("./logo-small.jpg"),
...
}
components/logo-small.tsx
import {IMAGES} from "@assets";
import {Image} from "react-native-ui-lib";
...
export const LogoSmall = (...) => {
const [assets, error] = useAssets([IMAGES.LOGO_SMALL]);
const logo = !error ? assets?.[0] ?? null : null;
return (
<Image style={styles.imageLogoSmall} source={logo} />
);
};
This method works great when used during development (utilizing Expo-Go for example), the images show up and everything is peachy keen. However, once I have built the application using eas build --profile testing
, the application no longer loads the images. All that is shown is a blank grey square in their place.
This issue persisted for several builds & several OTA updates via expo-update
with the command: eas update --branch testing
. However, the issue was always present in these builds, whether I used the OTA update feature or not.
However, after stumbling across this thread, I decided to uninstall expo-updates & run a new build (with the build command above), and the issue completely resolved itself. Images show up, leading me to believe that OTA updates were causing the issue (as others have listed in the original thread).
At the moment I don't have time to replicate the environment while stripping away the functionality & confidential information (crunch week 😄), but I can attempt to sometime this weekend, maybe.
@Bengejd thanks very much, this is definitely enough to get started creating a repro.
Same for us. It's not just image assets from node_modules
but also from our own assets
folder that are imported with require
:
const heroImagePlaceholder = require('~/assets/images/imageBanner.jpg');
...
<Image source={heroImagePlaceholder} />
Hmm I did a simple test.
yarn create expo-app AssetTest --template blank-typescript@48
expo-updates
:cd AssetTest
npx expo install expo-updates
./assets
folderApp.tsx
to show the picture using just RN Image
and an ordinary require()
:import { StatusBar } from 'expo-status-bar';
import { Image, StyleSheet, Text, View } from 'react-native';
const image = require('./assets/dougheadshot.jpg');
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<Image source={image} />
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
npx expo run:ios --configuration Release
And the image appears.
I see the same thing after running eas init
and eas update:configure
.
@sregg should not your image import be
const heroImagePlaceholder = require('./assets/images/imageBanner.jpg');
???
Ok I finally was able to reproduce the bug, by copying the metro.config.js
from @efkan 's project into my test project.
The root cause seems to be that the affected projects are using the plain vanilla Metro config from RN, and not importing Expo's config.
Your metro.config.js
should look like this:
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');
module.exports = getDefaultConfig(__dirname);
@douglowder Interesting. I wonder why metro config would break with expo-updates specifically then, and not just be broken the whole time?
For reference, here is my metro.config, which does import expo's config to extend it:
const { withNxMetro } = require("@nrwl/expo");
const path = require('path');
const workspaceRoot = path.resolve(__dirname, '../../');
const projectRoot = __dirname;
const { getDefaultConfig } = require("@expo/metro-config");
module.exports = (() => {
const config = getDefaultConfig(projectRoot);
const {transformer, resolver} = config;
config.transformer = {
...transformer,
babelTransformerPath: require.resolve("react-native-svg-transformer"),
assetPlugins: ["expo-asset/tools/hashAssetFiles"],
}
config.resolver = {
...resolver,
assetExts: resolver.assetExts.filter((ext) => ext !== "svg"),
sourceExts: [...resolver.sourceExts, "svg"],
}
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(workspaceRoot, 'node_modules'),
];
return withNxMetro(config, {
debug: false,
extensions: [],
projectRoot: __dirname,
watchFolders: [],
})
})();
@Bengejd I think not having expo/metro-config
breaks expo-updates
specifically because it is adjusting asset paths. Ultimately the source for our Metro config is at
https://github.com/expo/expo/blob/main/packages/%40expo/metro-config/src/ExpoMetroConfig.ts
If you are on SDK 48, you should use
https://github.com/expo/expo/blob/sdk-48/packages/%40expo/metro-config/src/ExpoMetroConfig.ts
Thanks @douglowder I'll try this.
Maybe this should be explained in https://docs.expo.dev/bare/installing-updates/
Is there really no way to disable this asset thing and only use expo-updates
to update the JS bundle?
Using the expo metro config
const { getDefaultConfig } = require('expo/metro-config');
module.exports = getDefaultConfig(__dirname);
didn't fix the issue for me.
The following images don't show up:
in our code:
const heroImagePlaceholder = require('../../../../assets/images/imageBanner.jpg');
or
const heroImagePlaceholder = require('~/assets/images/imageBanner.jpg');
in react-native-country-flag
:
exports.ad = require("./ad.png");
in react-native-bouncy-checkbox
:
const defaultCheckImage = require("./check.png");
Here's our metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts, resolverMainFields },
} = await getDefaultConfig(__dirname);
return {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
// this is needed for Storybook (see https://github.com/storybookjs/react-native/issues/405#issuecomment-1436683333)
resolverMainFields: ['sbmodern', ...resolverMainFields],
},
};
})();
I also wanted to mention that we're using using expo-asset
at all. It's only referenced in our yarn.lock
as it's a dependency of expo
.
I updated my config file but it's still not working:
const { getDefaultConfig } = require('expo/metro-config');
module.exports = (async () => {
const config = await getDefaultConfig(__dirname);
return {
...config,
transformer: {
...config.transformer,
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
...config.resolver,
assetExts: config.resolver.assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...config.resolver.sourceExts, 'svg'],
// this is needed for Storybook (see https://github.com/storybookjs/react-native/issues/405#issuecomment-1436683333)
resolverMainFields: ['sbmodern', ...config.resolver.resolverMainFields],
},
};
})();
Should the assets be renamed inside the .app
?
Also here's the error in the log when loading the image
Task <AC8A67BB-3EAA-4541-81CC-D94C56BE0E77>.<3> finished with error [-1100] Error Domain=NSURLErrorDomain Code=-1100 "The requested URL was not found on this server." UserInfo={NSLocalizedDescription=The requested URL was not found on this server., NSErrorFailingURLStringKey=file:///Users/simonreggiani/Library/Developer/CoreSimulator/Devices/AFA69A09-627D-41A3-AEA4-3B7F6FD92C35/data/Containers/Data/Application/9A151CC4-2691-4225-9614-CC3442EB5F76/Library/Application<decode: mismatch for [%20S] got [OBJECT public sz:48]>upport/.expo-internal/assets/src/assets/images/banner-placeholder.png, NSErrorFailingURLKey=file:///Users/simonreggiani/Library/Developer/CoreSimulator/Devices/AFA69A09-627D-41A3-AEA4-3B7F6FD92C35/data/Containers/Data/Application/9A151CC4-2691-4225-9614-CC3442EB5F76/Library/Application<decode: mismatch for [%20S] got [SCALAR sz:8]>upport/.expo-internal/assets/src/assets/images/banner-placeholder.png, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <AC8A67BB-3EAA-4541-81CC-D94C56BE0E77>.<3>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <AC8A67BB-3EAA-4541-81CC-D94
Here's what's in /Users/simonreggiani/Library/Developer/CoreSimulator/Devices/AFA69A09-627D-41A3-AEA4-3B7F6FD92C35/data/Containers/Data/Application/9A151CC4-2691-4225-9614-CC3442EB5F76/Library/
No .expo-internal
.
Also here's the code in the JS bundle where the image is loaded
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
module.exports = _$$_REQUIRE(_dependencyMap[0]).registerAsset({
"__packager_asset": true,
"httpServerLocation": "/assets/src/assets/images",
"width": 375,
"height": 300,
"scales": [1],
"hash": "31f4b797520b24f058bfbdf1b9ce45aa",
"name": "banner-placeholder",
"type": "png",
"fileHashes": ["31f4b797520b24f058bfbdf1b9ce45aa"]
});
},3093,[1216]);
I finally resolve this problem. About my app:
expo-updates
expo-image
, expo-asset
etc. I just wanna add expo-updates
to my app.With above setting and expo-updates
enabled, I build the app with "Release" config on iOS simulator. And my images are missing.
Turns out you NEED to use expo-asset
but just need to add import 'expo-asset';
at the very beginning of your js bundle (probably index.js
at root). Inside expo-asset
, it would call setCustomSourceTransformer
from react-native's Image
to tell it how to resolve image assets from correct location (the hidden .expo-internal
). After adding the expo-asset
line, my images finally appear.
@douglowder Can you confirm if my finding is correct?
Thank you so much @chochihim this was it!
I was missing import 'expo-asset';
too.
This definitely should be explained in the Expo Updates docs.
@chochihim have u also added expo-asset into the package.json and actually installed it? or just adding the line? Its not working for me
@asphyxiar I didn't install it. It is one of the dependencies of expo
so I just import it.
still not seeing the images, could it be connected to the fact that iam running expo 47 not 48?
Minimal reproducible example
https://github.com/efkan/ExpoUpdatesExample.git
Summary
While images are displayed as expected on a newly created React-Native app, they could not be found after I implemented the Expo-Updates module.
On the other hand, the images are shown as expected on Android for both Dev and Prod environments.
We cannot continue testing & development due to bumping this issue. I've searched on the Internet but couldn't find any solution.
Ps: sorry if I'd overlooked something on the Expo documentation
Environment
expo-env-info 1.0.5 environment info: System: OS: macOS 13.3.1 Shell: 5.9 - /bin/zsh Binaries: Node: 16.16.0 - ~/.nvm/versions/node/v16.16.0/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 8.16.0 - ~/.nvm/versions/node/v16.16.0/bin/npm Managers: CocoaPods: 1.12.1 - /Users/efkan/.rvm/gems/ruby-3.0.0/bin/pod SDKs: iOS SDK: Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4 Android SDK: API Levels: 30, 31, 33 Build Tools: 30.0.2, 30.0.3, 31.0.0, 33.0.0, 33.0.1, 34.0.0 System Images: android-28 | Google ARM64-V8a Play ARM 64 v8a, android-30 | Google APIs ARM 64 v8a, android-30 | Google Play ARM 64 v8a, android-31 | ARM 64 v8a, android-31 | Intel x86 Atom_64, android-31 | Google APIs ARM 64 v8a, android-31 | Google APIs Intel x86 Atom_64, android-31 | Google Play ARM 64 v8a, android-33 | Google APIs ARM 64 v8a IDEs: Android Studio: 2022.1 AI-221.6008.13.2211.9619390 Xcode: 14.3/14E222b - /usr/bin/xcodebuild npmPackages: expo: ^48.0.0 => 48.0.17 react: 18.2.0 => 18.2.0 react-native: 0.71.8 => 0.71.8 npmGlobalPackages: eas-cli: 3.13.1 Expo Workflow: bare