transistorsoft / react-native-background-fetch

Periodic callbacks in the background for both IOS and Android
MIT License
1.43k stars 191 forks source link

It only seems to work while debugging. It won't in release #445

Closed abdullahIsa closed 1 year ago

abdullahIsa commented 1 year ago

Your Environment

Expected Behavior

It should run every 15 minutes on any android devices

Actual Behavior

Does not seem to work as i have a way to check when was last run and its always on default which is "pending..." instead of time of last run

Steps to Reproduce

  1. Install react native 0.70.6
  2. Maybe use same phone or similar android version
  3. Test check in release mode not debug mode
  4. See if runs and all ok

Context

I am trying to run a task every 15 minutes, a simple function will get called that has api to call the server for latest data, basically syncing users local db with remote db.

Debug logs

include iOS / Android logs

Code

import React, {
  useContext,
  createContext,
  useCallback,
  useState,
  useEffect,
} from 'react';
import BackgroundFetch from 'react-native-background-fetch';
import {backgroundDataGetter} from './accountHelper';

const DailyTaskHookContext = createContext({});

export const DailyTaskContextProvider = ({children}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    const taskCallback = async taskId => {
      console.log('Background fetch started');
      await backgroundDataGetter();
      BackgroundFetch.finish(taskId);
      console.log('Background fetch finished');
    };

    if (isAuthenticated) {
      BackgroundFetch.configure(
        {
          minimumFetchInterval: 15, // 15 minutes
          stopOnTerminate: false,
          enableHeadless: true,
          forceAlarmManager: true,
          requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY,
        },
        taskCallback,
        error => console.log('Background fetch failed to start', error),
      );
      BackgroundFetch.start();
      console.log('Background fetch initiated');
    } else {
      BackgroundFetch.stop();
      console.log('Background fetch stopped');
    }

    return () => {
      BackgroundFetch.stop();
    };
  }, [isAuthenticated]);

  const startDailyTask = useCallback(async () => {
    if (__DEV__ !== true) {
      BackgroundFetch.stop();
      setIsAuthenticated(true);
    }
  }, []);

  const stopDailyTask = useCallback(async () => {
    setIsAuthenticated(false);
  }, []);

  return (
    <DailyTaskHookContext.Provider
      value={{
        startDailyTask,
        stopDailyTask,
      }}>
      {children}
    </DailyTaskHookContext.Provider>
  );
};

export const useDailyTaskHook = () => useContext(DailyTaskHookContext);

i got no idea what is wrong as no error or anything, am i missing something to add for release mode ? thanks.

abdullahIsa commented 1 year ago

Also may i know how to implement BackgroundFetch.registerHeadlessTask(MyHeadlessTask); in my code ? as i only want to start task if user is authenticated

New code note yet fully tested, want to know if correct

/* eslint-disable no-unreachable */
import React, {
  useContext,
  createContext,
  useCallback,
  useState,
  useEffect,
} from 'react';
import BackgroundFetch from 'react-native-background-fetch';
import {backgroundDataGetter} from './accountHelper';

const DailyTaskHookContext = createContext({});

const taskCallback = async taskId => {
  console.log('Background fetch started');
  await backgroundDataGetter();
  BackgroundFetch.finish(taskId);
  console.log('Background fetch finished');
};

const onTimeout = async taskId => {
  console.warn('[BackgroundFetch] TIMEOUT task: ', taskId);
  BackgroundFetch.finish(taskId);
};

const synWithDb = () => {
  BackgroundFetch.configure(
    {
      minimumFetchInterval: 15, // 15 minutes
      stopOnTerminate: false,
      enableHeadless: true,
      forceAlarmManager: true,
      requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY,
    },
    taskCallback,
    onTimeout,
  );
  BackgroundFetch.start();
  console.log('Background fetch initiated');
};

export const DailyTaskContextProvider = ({children}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    if (isAuthenticated) {
      BackgroundFetch.registerHeadlessTask(synWithDb);
    } else {
      BackgroundFetch.stop();
      console.log('Background fetch stopped');
    }

    return () => {
      BackgroundFetch.stop();
    };
  }, [isAuthenticated]);

  const startDailyTask = useCallback(async () => {
    if (__DEV__ !== true) {
      BackgroundFetch.stop();
      setIsAuthenticated(true);
    }
  }, []);

  const stopDailyTask = useCallback(async () => {
    setIsAuthenticated(false);
  }, []);

  return (
    <DailyTaskHookContext.Provider
      value={{
        startDailyTask,
        stopDailyTask,
      }}>
      {children}
    </DailyTaskHookContext.Provider>
  );
};

export const useDailyTaskHook = () => useContext(DailyTaskHookContext);
christocracy commented 1 year ago

how to implement BackgroundFetch.registerHeadlessTask(MyHeadlessTask);

See the API docs in the README enableHeadless

abdullahIsa commented 1 year ago

how to implement BackgroundFetch.registerHeadlessTask(MyHeadlessTask);

See the API docs in the README enableHeadless

ah thanks recently noticed the headless part, in that case is this code correct https://github.com/transistorsoft/react-native-background-fetch/issues/445#issuecomment-1496110303

my app.js, its a complex project and i need it to start only after authenticated DailyTasksHook

const App = () => {
  useEffect(() => {
    const isHermes = () => !!global.HermesInternal;
    console.log('Engine isHermes:', isHermes());
  }, []);

  return (
    <Network.NetInfoContextProvider>
      <AppUpdater.UpdaterContextProvider>
        <DeepLink.DeepLinkProvider>
          <Socket.SocketContextProvider>
            <DailyTasksHook.DailyTaskContextProvider>
              <Auth.GoogleAuthenticationHook.GoogleAuthenticationProvider>
                <Auth.AuthenticationHook.AuthenticationProvider>
                  <AppError.ApiErrorContextProvider>
                    <Permiter.PermiterContextProvider>
                      <AppState.AppStateModeContextProvider>
                        <SharingIntent.ReceiveSharingIntentProvider>
                          <Nav.RootNavigator />
                          <FlashMessage
                            position={
                              Platform.OS === 'ios'
                                ? 'top'
                                : {
                                    top: StatusBar.currentHeight,
                                    left: 0,
                                    right: 0,
                                  }
                            }
                            floating={Platform.OS !== 'ios'}
                            style={styles.flashMessage}
                            duration={3000}
                          />
                        </SharingIntent.ReceiveSharingIntentProvider>
                      </AppState.AppStateModeContextProvider>
                    </Permiter.PermiterContextProvider>
                  </AppError.ApiErrorContextProvider>
                </Auth.AuthenticationHook.AuthenticationProvider>
              </Auth.GoogleAuthenticationHook.GoogleAuthenticationProvider>
            </DailyTasksHook.DailyTaskContextProvider>
          </Socket.SocketContextProvider>
        </DeepLink.DeepLinkProvider>
      </AppUpdater.UpdaterContextProvider>
    </Network.NetInfoContextProvider>
  );
};
abdullahIsa commented 1 year ago

Update:

Is this ok instead ?

index.js, i dont want to make it all messy inside index.js, is calling it this way ok ?

/**
 * @format
 */
import './wdyr';
import './headlessTask';
import {AppRegistry} from 'react-native';
import {enableFreeze} from 'react-native-screens';
import App from './App';
import {name as appName} from './app.json';

// https://github.com/software-mansion/react-native-screens#readme
enableFreeze(true);

AppRegistry.registerComponent(appName, () => App);

headlessTask.js

import BackgroundFetch from 'react-native-background-fetch';
import {backgroundDataGetter} from './src/components/background/dailyUpdater/accountHelper';

const taskCallback = async taskId => {
  console.log('Background fetch started');
  await backgroundDataGetter(); // here i will check if current user is authenticated in localdb or still has access before calling server api
  BackgroundFetch.finish(taskId);
  console.log('Background fetch finished');
};

const onTimeout = async taskId => {
  console.warn('[BackgroundFetch] TIMEOUT task: ', taskId);
  BackgroundFetch.finish(taskId);
};

const synWithDb = () => {
  BackgroundFetch.configure(
    {
      minimumFetchInterval: 15, // 15 minutes
      stopOnTerminate: false,
      enableHeadless: true,
      forceAlarmManager: true,
      requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY,
    },
    taskCallback,
    onTimeout,
  );
  BackgroundFetch.start();
  console.log('Background fetch initiated');
};

BackgroundFetch.registerHeadlessTask(synWithDb);
abdullahIsa commented 1 year ago

also update, in https://github.com/transistorsoft/react-native-background-fetch/tree/master/example i dont see anywhere config is being used how do i use the config options ?

abdullahIsa commented 1 year ago

update i found it https://github.com/transistorsoft/react-native-background-fetch/blob/master/example/App.tsx will update back after following the example properly

christocracy commented 1 year ago

Headless-tasks are easily tested with simulated-tasks. Do not use forceAlarmManager: true when simulating tasks.

abdullahIsa commented 1 year ago

update i followed through example, very explanative no idea how i missed it but i got some question.

if i dont call BackgroundFetch.scheduleTask() no task seems to run or BackgroundFetch.configure() callback does not execute, so do i need to call BackgroundFetch.scheduleTask() with periodic: true for it to always run ? and if so would it also be the one to make the task BackgroundFetch.registerHeadlessTask() run when app is closed ?

if BackgroundFetch.scheduleTask() is just an additional functionality if i call BackgroundFetch.configure() at app.js in useEffect will it run after 15 minutes?

i am asking cause no task running unless i call BackgroundFetch.scheduleTask()

christocracy commented 1 year ago

BackgroundFetch.scheduleTask() is just an additional functionality

It is not required. .configure the plug-in without forceAlarmManager: true, simulate a task as documented in the readme, observe $ adb logcat and post the logs here.

abdullahIsa commented 1 year ago

my bad forgot to call BackgroundFetch.start(); , all working well, took a while in debug mode but thats not an issue, i have one last question regarding BackgroundFetch.scheduleTask().

can BackgroundFetch.scheduleTask() be ran multiples time with different taskId ? if i set BackgroundFetch.scheduleTask() to run every 1 minute, if i close app will it still continue running or only runs when app open?

christocracy commented 1 year ago

can BackgroundFetch.scheduleTask() be ran multiples time with different taskId ?

Yes.

if i set BackgroundFetch.scheduleTask() to run every 1 minute, if i close app will it still continue running or only runs when app open?

Why don’t you try it and see for yourself while observing $ adb logcat?

abdullahIsa commented 1 year ago

ah sure will try that later, will consider this solved thanks.

akapoorleda commented 1 year ago

@abdullahIsa I just want to confirm, can i use this library for multiple api post call requests??

abdullahIsa commented 1 year ago

@abdullahIsa I just want to confirm, can i use this library for multiple api post call requests??

yes can, as long set well, i recommend just for calling apis and doing things like updating localdb, does its job well.

akapoorleda commented 1 year ago

@abdullahIsa Okay thanks.