0
  1. Expo React Native SDK Version: 46
  2. Platforms: Android/iOS
  3. Package concerned : Expo.Updates

Hello everyone, I want to programmatically check for new updates, without using the fallbackToCacheTimeout in app.json that will trigger the check of the new updates when the application is launched because like that I can't put a custom loading page. So by doing this all by code as follow :

try{
  const update = await Updates.checkForUpdateAsync();
  if(update.isAvailable){
      await Updates.fetchUpdateAsync();
      await Updates.reloadAsync();
  }else{}
}catch(err){}

But I want to be able to abort all those calls after a certain time (thus, the user that have a bad connection can use the app without waiting a very long time). I check the documentation and I cannot found any method that allow this.

I dont't think it's possible to cancel a Promise for now in Javascript, or maybe any connection ?

Or does the "fallbackToCacheTimeout" value in the app.json will automatically apply to the fetch updates call of the Expo API?

Do someone have any idea how to do it ? :(

Julien Pepe
  • 142
  • 8
  • Nop, but what it is for ? I don't think there are errors thrown by the FetchAsync function. It's just blocked, waiting for the connection or download. To be able to avoid long waiting, I'm using a fetch call to my own server on an image and try to determine the network speed, and then I allow the download – Julien Pepe Jan 16 '23 at 11:21

1 Answers1

1

First of all I am assuming you have set updates.checkautomatically field to ON_ERROR_RECOVERY in app.json or app.config.js file. If not, please check the documentation. The reason why you need this is to avoid automatic updates which can also block your app on splash screen.

Updated Solution

Because of the limitation in javascript we can't cancel any external Promise (not created by us or when its reject method is not exposed to us). Also the function fetchUpdateAsync exposed to us is not a promise but rather contains fetch promise and returns its result.

So, here we have two options:

  1. Cancel reloading the app to update after a timeout.

But note that updates will be fetched in background and stored on the device. Next time whenever user restarts the app, update will be installed. I think this is just fine as this approach doesn't block anything for user and also there is a default timeout for http request clients like fetch and axios so, request will error out in case of poor/no internet connection.

Here is the code:

try {
    const update = await Updates.checkForUpdateAsync();
    if (update.isAvailable) {
        const updateFetchPromise = Updates.fetchUpdateAsync();
        const timeoutInMillis = 10000; // 10 seconds
        const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject("timedout"), timeoutInMillis))

        // This will return only one Promise
        Promise.race([updateFetchPromise, timeoutPromise])
            .then(() => Updates.reloadAsync())
            .catch((error) => {
                if (error === 'timedout') {
                    // Here you can show some toast as well
                    console.log("Updates were not cancelled but reload is stopped.")
                } else if (error === 'someKnownError') {
                    // Handle error
                } else {
                    // Log error and/or show a toast message
                }
            })
    } else {
        // Perform some action when update is not available
    }
} catch (err) {
    // Handle error
}
  1. Change the expo-updates package just for your app using a patch

Here you can return a cancel method with Updates.fetchUpdateAsync() and use it with setTimeout to cancel the fetch request. I won't be providing any code for this part but if you are curious I can definitely provide some help.

Please refer this section to understand use of fallbackToCacheTimeout in eas updates.



Old solution:

Now, for aborting or bypassing the promise i.e. Updates.fetchUpdateAsync in your case. You can basically throw an Error in setTimeout after whatever time duration you want, so that, catch block will be executed, bypassing the promises. Here is the old code :

try{
  const update = await Updates.checkForUpdateAsync();
  if(update.isAvailable){
      // Throw error after 10 seconds.
      const timeout = setTimeout(() => { throw Error("Unable to fetch updates. Skipping..") }, 10000)

      await Updates.fetchUpdateAsync();

      // Just cancel the above timeout so, no error is thrown.
      clearTimeout(timeout)

      await Updates.reloadAsync();
  }else{}
}catch(err){}
Manan Bordia
  • 394
  • 2
  • 4
  • 12
  • Ok thanks, that's something interesting ! Thank you. However, I notice that the await call is still running even after throwing the error. So that can impact on the performance right ? With fetch API, we can use an Abort Controller to cancelled the call correctly. But still, this is a simple working solution. – Julien Pepe Jan 20 '23 at 21:11
  • @julien-pepe Added new solution. – Manan Bordia Feb 17 '23 at 22:47