Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App is refreshing again once i downloaded the app from Test Flight #155

Open
danberdevteam opened this issue Feb 27, 2025 · 7 comments
Open

Comments

@danberdevteam
Copy link

danberdevteam commented Feb 27, 2025

I have added the hot-updater using the supabase provider and it's working fine. but there was only one issue when I installed the app from the Test Flight every time the app refreshed after installing the app from test flight because in the last bundle update, I deployed the bundle with the option.

yarn hot-updater deploy -i -f

Expected Result: The app should download the updates while installing the app from TF. Don't need to download the updates after installing the app. Updates should be downloaded only when the new updates are available and the user has already installed the app on his phone.

Here is my code

  useEffect(() => {
    checkForAppUpdate();
  }, []);

 const checkForAppUpdate = async () => {
    try {
      const updateInfo = await HotUpdater.checkForUpdate({
        source: SOURCE,
        requestHeaders: {
          // Add any necessary request headers here
        },
      });

      if (!updateInfo) {
        return {
          status: 'UP_TO_DATE',
        };
      }

      await HotUpdater.runUpdateProcess({
        source: SOURCE,
        requestHeaders: {
          // Add any necessary request headers here
        },
        reloadOnForceUpdate: true, // Automatically reload the app on force updates
      });

      return updateInfo;
    } catch (error) {
      console.error('Failed to check for update:', error);
      return null;
    }
  };

ScreenRecording_02-27-2025.14-31-17_1.MP4
@gronxb
Copy link
Owner

gronxb commented Feb 27, 2025

#135

This looks similar. Please take a look at the response.

However, The good news is that we are planning to release a feature for environment separation soon, and it might make this possible.

@danberdevteam
Copy link
Author

@gronxb so you are saying it will happen on the test flight only? But maybe it can be happen on the app store as well. When the user downloads and installs the app from app store so app will refresh again on first time.

Give me a better solution to fix this because I am displaying the custom screen to the user for downloading the updates. and it will be display to user when user download and install the app and open the app on first time.

@gronxb
Copy link
Owner

gronxb commented Feb 27, 2025

sorry update example😅

@nvu
Copy link

nvu commented Feb 28, 2025

@gronxb First, thank you for your passionate development.

I don't know if it's the same issue, but I think what @danberdevteam describing is a case where the testflight deployment happened later than the forced deploy.

For reference, in the case of microsoft-code-push, the timestamp is recorded with the initial package, so even if the version code are the same, if the timestamp of the package is newer, the bundle is not updated.

https://github.com/microsoft/react-native-code-push/blob/6015386f22c61417cd2b2ae2860a9ad69c37f643/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java#L350-L365

@danberdevteam
Copy link
Author

danberdevteam commented Mar 3, 2025

@gronxb In our application, we display a message screen prompting the user to download updates if any are available.

It works fine when the user has already installed the app from TestFlight, opens the app, and new updates are available—the user sees the screen to download the updates.

Image

However, this screen also appears when the user installs the app from TestFlight or the App Store for the first time because HotUpdater.checkForUpdate does not return the UP_TO_DATE status initially.
HotUpdater.checkForUpdate should return the UP_TO_DATE status when the user installs the app from TestFlight or the App Store.

Below is my component, which is rendered at the root level of the application.

import React, {useEffect, useRef} from 'react';
import {useState} from 'react';
import {AppState, Dimensions, StyleSheet} from 'react-native';
import {
  Button,
  Container,
  Content,
  Spinner,
  StackHeader1,
  Text,
  View,
} from '../common';

import InfoIcon from '../assets/icons/app-circle-info.svg';

import {HotUpdater} from '@hot-updater/react-native';

import {colors} from '../config/theme';
import {Alert} from '../util/alert';

/* =============================================================================
<OTAUpdatesRequired />
============================================================================= */
const OTAUpdatesRequired = () => {
  const [loader, setLoader] = useState(false);

  const appState = useRef(AppState.currentState);
  const [showUpdatesRequired, setShowUpdatesRequired] = useState(true);

  const SOURCE =
    'https://<>.supabase.co/functions/v1/update-server';

  useEffect(() => {
    checkForAppUpdate();
  }, []);

  // when the user come form the background
  useEffect(() => {
    const subscription = AppState.addEventListener('change', nextAppState => {
      if (appState.current.match(/background/) && nextAppState === 'active') {
        checkForAppUpdate();
      }

      appState.current = nextAppState;
    });

    return () => {
      subscription.remove();
    };
  }, []);

  const checkForAppUpdate = async () => {
    try {
      const updateInfo = await HotUpdater.checkForUpdate({
        source: SOURCE,
        requestHeaders: {
          // Add any necessary request headers here
        },
      });

      if (!updateInfo) {
        return {
          status: 'UP_TO_DATE',
        };
      }

      setShowUpdatesRequired(true);

      return updateInfo;
    } catch (error) {
      console.error('Failed to check for update:', error);
      return null;
    }
  };

  const _downloadNewUpdates = async () => {
    try {
      setLoader(true);

      await HotUpdater.runUpdateProcess({
        source: SOURCE,
        requestHeaders: {
          // Add any necessary request headers here
        },
        reloadOnForceUpdate: true, // Automatically reload the app on force updates
      });

      setShowUpdatesRequired(false);
      setLoader(false);
    } catch (error: any) {
      Alert.showError(error.message);
      console.error('Failed to updates:', error);
      setLoader(false);
    }
  };

  if (showUpdatesRequired) {
    return (
      <Container style={styles.container}>
        <StackHeader1 backButton={false} />
        <Content contentContainerStyle={styles.content}>
          <View>
            <View center>
              <InfoIcon width={40} height={40} fill={colors.primary} />
            </View>

            <Text my={15} align="center" size={20}>
              Update Required
            </Text>
            <Text align="center" size={16} color={colors.secondaryText}>
              There is a new update available. Please update the app for an
              improved experience.
            </Text>
          </View>

          <Button mt={30} title="Update" onPress={_downloadNewUpdates} />
        </Content>

        <Spinner visible={loader} />
      </Container>
    );
  }

  return null;
};

const {height, width} = Dimensions.get('window');

const styles = StyleSheet.create({
  container: {
    flex: 1,
    top: 0,
    left: 0,
    right: 0,
    width: width,
    height: height,
    position: 'absolute',
    backgroundColor: colors.white,
  },
  content: {
    flex: 1,
    paddingBottom: 80,
    paddingHorizontal: 40,
    backgroundColor: colors.white,
    justifyContent: 'center',
  },
});

export default OTAUpdatesRequired;

@gronxb
Copy link
Owner

gronxb commented Mar 3, 2025

Yes, I have confirmed the issue you mentioned.

Currently, when running the hot-updater deploy command, Babel executes and generates HotUpdater.HOT_UPDATER_BUNDLE_ID.

if (!buildOutDir) {
return {
name: "replace-hot-updater-bundle-id",
visitor: {
MemberExpression(path: NodePath<t.MemberExpression>) {
if (
t.isIdentifier(path.node.object, { name: "HotUpdater" }) &&
t.isIdentifier(path.node.property, {
name: "HOT_UPDATER_BUNDLE_ID",
})
) {
path.replaceWith(
t.stringLiteral("00000000-0000-0000-0000-000000000000"),
);
}
},
},
};
}

However, the issue is that Babel does not run during the initial installation, causing the bundle ID to default to 000000. Since the bundle ID follows UUIDv7, a larger string guarantees a more recent version.

To resolve this, ensuring that Babel runs even during the initial build should fix the issue.

Now that we have identified the solution, please wait a moment while I implement it.

@danberdevteam
Copy link
Author

@gronxb Thanks for the quick response. I am waiting for the fixes once you implement it then must inform me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants