Updates.checkForUpdateAsync never returns

I’ll use the OTA feature to perform a manual update. I wrote code like this, but no return.
I ran it on a real phone and MacBook simulator. I ran it in componentDidMount(), and I proceeded by subtracting it as a button event.

console.log("000");                                  // print 000
const update = await Updates.checkForUpdateAsync();  // never returned...
if (update.isAvailable) {                            // waiting.....
  console.log("11!");
  await Updates.fetchUpdateAsync();
  console.log("22!");
  Updates.reloadFromCache();
}

also Updates.fetchUpdateAsync() is never returned as well. here is my app.json.

 "updates": {
      "fallbackToCacheTimeout": 0,
      "enabled": false,
      "checkAutomatically": "ON_ERROR_RECOVERY"
    },
2 Likes

I’m not sure what is going wrong in your case. It could be that the app is having problems connecting to the server to check for, or download the updates. In case it helps, I’ve included below how I do it.

I’m not sure if this is the best way to do it, but I do it like this:

I keep track of the last time the app checked for an update in a tick variable. Then every time the user changes to a different screen I check if enough time has passed since the last check. If so, I set the tick variable to the current time.

I have a useEffect hook that depends on the tick variable, so when tick changes the effect is called.

  useEffect(() => {
    if (!__DEV__) {
      Updates.checkForUpdateAsync().then(({ isAvailable, manifest }) => {
        if (isAvailable) setUpdateAvailable(true);
      });
    }
  }, [tick]);

Then I have a button that I show if updateAvailable is true.

      <UpdateButton visible={updateAvailable} top={insets.top} />
const UpdateButton = props => {
  const { visible, top } = props;
  return (
    <FAB
      style={{
        position: "absolute",
        margin: 16,
        right: 0,
        top,
        backgroundColor: "#36454f"
      }}
      icon="cloud-download"
      label="Update"
      color="white"
      visible={visible}
      onPress={() => Updates.reload()}
    />
  );
};

This works for me.

1 Like

I’m running into the same problem. I have the following code, as per this website under manual updates.

The app idles indefinitely after Loader.js run Updates.checkForUpdateAsync(). The app will mount <AppNavigator/> because of this problem and users will never see the app.

AppEntry.js

import React from "react";
import { StyleSheet, View } from "react-native";
import { Provider } from "react-redux";
import store from "./redux/store";
import AppNavigator from "./navigation/AppNavigator";
import Loader from "./shared/Loader"; // see file definition further down comment

export default class App extends React.Component {
	state = {
		isLoadingComplete: false,
	};

	render() {
		return (
			<Provider store={store}>
				<React.Fragment>
					{!this.state.isLoadingComplete && (
						<Loader
							onFinish={this._handleFinishLoading}
							onError={this._handleLoadingError}
						/>
					)}
					{this.state.isLoadingComplete && (
						<AppNavigator />
					)}
				</React.Fragment>
			</Provider>
		);
	}

	_handleLoadingError = (error) => {
		console.warn(error);
	};

	_handleFinishLoading = () => {
		this.setState({ isLoadingComplete: true });
	};
}

Loading.js

import Expo, { Updates } from "expo";
import React from "react";
import { connect } from "react-redux";
import {
	StyleSheet,
	View,
	Dimensions,
	ActivityIndicator,
	Text,
} from "react-native";
import Dispatchers from "../redux/dispatchers";

var { width, height } = Dimensions.get("window");

const styles = StyleSheet.create({
	container: {
		width,
		height,
		flex: 1,
		backgroundColor: "#000",
		paddingTop: 100,
	},
	text: {
		color: "#fff",
		textAlign: "center",
		marginBottom: 10,
	},
});
class Loader extends React.Component {
	componentDidMount = () => {
		try {
            const update = await Updates.checkForUpdateAsync();
            if (update.isAvailable) {
                await Updates.fetchUpdateAsync();
                // ... notify user of update ...
                Updates.reloadFromCache();
            }
            // continues back to AppEntry.js since at this point the latest version has been downloaded
            this.props.onFinish();
        } catch (e) {
            // handle or log error
        }
	};

	render() {
		// if (!this.props.initialLoadComplete ){
		return (
			<View style={styles.container}>
				<Text style={styles.text}>Loading</Text>
				<ActivityIndicator color={"#fff"} size='large' />
			</View>
		);
		// }
	}
}

export default connect((state) => ({}), Dispatchers)(Loader);

app.json

{
	"expo": {
		"name": "____",
		"description": "Adriaan Balt",
		"slug": "snack-___",
		"privacy": "unlisted",
		"sdkVersion": "35.0.0",
		"version": "1.0.0",
		"orientation": "portrait",
		"primaryColor": "#000000",
		"icon": "./assets/expo-icon.png",
		"packagerOpts": {
			"assetExts": ["ttf", "mp4", "otf", "xml", "json", "png"]
		},
		"ios": {
			"buildNumber": "1.0.0",
			"supportsTablet": true,
			"bundleIdentifier": "com.__.__",
		},
		"platforms": ["ios"],
		"updates": {
			"enabled": true,
			"checkAutomatically": "ON_ERROR_RECOVERY",
			"fallbackToCacheTimeout": 0
		}
	}
}

I’m also getting on return for Update.checkForUpdateAsync. It never returns or even gives status change messages.

the Updates.fetchUpdateAsync works as expected and returns the proper values. The only problem is that this forces an update next restart. My code is pretty much the above code.

As a workaround I would just fetch the latest manifest directly but I’m not sure what the path to that would be.

1 Like

I created a button that triggers this function

import { Updates } from 'expo'
const load => () => {
    try {
        const update = await Updates.checkForUpdateAsync();
        if (update.isAvailable) {
            await Updates.fetchUpdateAsync();
            // ... notify user of update ...
            Updates.reloadFromCache();
        }
        // continues back to AppEntry.js since at this point the latest version has been downloaded
        this.props.onFinish();
    } catch (e) {
        // handle or log error
    }
}

and Updates.checkForUpdateAsync() still doesn’t return anything.

Could it be because I’m in devToolsEnabledWithManifest? Although I would then expect an error to come back from the response instead of nothing

https://github.com/expo/expo/blob/master/ios/Exponent/Versioned/Core/Api/EXUpdates.m#L58

Really not sure what’s causing it not to return anything for you. It works fine for me:

Are you able to see if any network requests are made when you click the button?
Does the behaviour depend on whether you try it on a physical device or simulator/emulator or on the OS? Or in the Expo app vs. a standalone app?
What happens if you run the above snack on your device/simulator in the Expo app?

Thank you @wodin for this concise example. I adjusted my code a bit and removed the async/await in exchange for a .then().catch() and I was able to get a response but unfortunately not the response I was hoping for. I got the following error:

{
    "framesToPop":1,
    "code":"E_CHECK_UPDATE_FAILED",
    "domain":"ABI35_0_0RCTErrorDomain",
    "userInfo":null,
    "nativeStackIOS":[
        "0   Exponent                            0x000000010512ed4c _ZN8facebook14ReactABI35_0_011JSIExecutor21defaultTimeoutInvokerERKNSt3__18functionIFvvEEENS3_IFNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEvEEE + 777708",
        "1   Exponent                            0x00000001050ddd30 _ZN8facebook14ReactABI35_0_011JSIExecutor21defaultTimeoutInvokerERKNSt3__18functionIFvvEEENS3_IFNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEvEEE + 445904",
        "2   Exponent                            0x0000000104ad35d8 Exponent + 3945944",
        "3   CoreFoundation                      0x00000001c07425a4 046F17DF-03A8-3D03-99F5-BD4CFF8123C4 + 1283492",
        "4   CoreFoundation                      0x00000001c060b1d0 046F17DF-03A8-3D03-99F5-BD4CFF8123C4 + 8656",
        "5   CoreFoundation                      0x00000001c060bde0 046F17DF-03A8-3D03-99F5-BD4CFF8123C4 + 11744",
        "6   Exponent                            0x00000001050de518 _ZN8facebook14ReactABI35_0_011JSIExecutor21defaultTimeoutInvokerERKNSt3__18functionIFvvEEENS3_IFNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEvEEE + 447928",
        "7   Exponent                            0x00000001050e4d84 _ZN8facebook14ReactABI35_0_011JSIExecutor21defaultTimeoutInvokerERKNSt3__18functionIFvvEEENS3_IFNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEvEEE + 474660",
        "8   Exponent                            0x00000001050e4ae4 _ZN8facebook14ReactABI35_0_011JSIExecutor21defaultTimeoutInvokerERKNSt3__18functionIFvvEEENS3_IFNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEvEEE + 473988",
        "9   libdispatch.dylib                   0x00000001c03e0b7c D174E751-B542-3A69-A593-7AA69CF30F23 + 7036",
        "10  libdispatch.dylib                   0x00000001c03e1fd8 D174E751-B542-3A69-A593-7AA69CF30F23 + 12248",
        "11  libdispatch.dylib                   0x00000001c03e8450 D174E751-B542-3A69-A593-7AA69CF30F23 + 37968",
        "12  libdispatch.dylib                   0x00000001c03e8e7c D174E751-B542-3A69-A593-7AA69CF30F23 + 40572",
        "13  libdispatch.dylib                   0x00000001c03f1f20 D174E751-B542-3A69-A593-7AA69CF30F23 + 77600",
        "14  libsystem_pthread.dylib             0x00000001c0447a94 _pthread_wqthread + 280",
        "15  libsystem_pthread.dylib             0x00000001c044dc7c start_wqthread + 8"
    ],
    "line":2750,
    "column":26,
    "sourceURL":"http://192.168.86.59:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&minify=false&hot=false"
}

I’m testing inside the Expo App on my device. No Network Requests that I can tell. I haven’t tried the simulator/emulator. I haven’t uploaded the APK to TestFlight.

If I run your snack I get this error:
Screen Shot 2019-12-11 at 9.46.49 AM

I searched the Github repo for the error code I am receiving E_CHECK_UPDATE_FAILED and came across this file. But I cant tell based on the above error what is actually wrong.

https://github.com/expo/expo/blob/master/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/UpdatesModule.java#L61

Any insight?

After a while I get a warning Unhandled promise rejection: Error: Cannot check for updates in dev mode. So maybe Updates only works while in TestFlight?

Progress! :slight_smile:

I am afraid I have not yet got the hang of async/await, so I always use .then().catch() :laughing:

I see from the the stack trace that you’re running it on iOS. So the code you found yesterday is the relevant code. The code you found today is for Android.

Updates are definitely not supported in dev mode and I wouldn’t be surprised if they didn’t work in the Expo app, but strangely, as you can see from my screenshot, it worked for me in the appetize.io Android preview, which as far as I understand it is just the Expo app running in an Android emulator. I’ve just tried the snack on my Android phone and it works there too. Strange. I’ve now tried it on the iOS preview in snack and that also works!

It’s possible that the snack behaves differently to an app being served from a local development machine, though.

Yes, that seems to be the case. I’ve just tried this on an app served from my development machine and it fails in a similar way to yours. The returned object has less info than yours, but it does have a code with value E_CHECK_UPDATE_FAILED and a message with value Cannot check for updates in dev mode.

The error seems to imply that you could get it to work by building the app in production mode, but this is not the case. Strangely I get lots more info in the error when in production mode vs. dev mode.

I think the error is trying to say that update checks can’t work in the Expo app. I have some vague ideas about why that might be (apps running within the Expo app are more like plugins than full apps, so there are definitely differences between running within the Expo app vs. running a standalone app. I suspect it’s possible for someone to implement something that works in dev mode/in the Expo app, but presumably nobody has considered it worth their time.

The only way for this function to work is once it is in TextFlight. It does not work with local development.

Note: Be careful when doing this because if you make it impossible for the app to check for OTA then you will never get your updated bundle; you will then have to re-submit to Testflight/App Store with code that makes it possible to download and cache your new JS bundle.

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