expo-location doesn’t work in background

Hi! In those days I’ve made an app, learning about expo and localizations, in the end, everything work well, until integrate taskmanager to take in background the latitude and longitude. When the app is open (android), in each moment, I get my lat and long, but when I lock the phone or even close de app (but still open in multitasking), the data is stopped. I know it because I send all my data to supabase. Here is my App Component.

const Stack = createNativeStackNavigator();
const LOCATION_TASK = "background-location-task";

/**
 * This function requests foreground and background location permissions and starts location updates if
 * granted.
 */
const requestPermissions = async () => {
  const { status: foregroundStatus } =
    await Location.requestForegroundPermissionsAsync();
  if (foregroundStatus === "granted") {
    const { status: backgroundStatus } =
      await Location.requestBackgroundPermissionsAsync();
    if (backgroundStatus === "granted") {
      await Location.startLocationUpdatesAsync(LOCATION_TASK, {
        accuracy: Location.Accuracy.Highest,
        distanceInterval: 1,
        timeInterval: 1500,
      });
    }
  }
};

export function Index() {
  const { supabase, onUpdate, onSignOut } = useSupabase();
  const [session, setSession] = useState<Session>({
    access_token: undefined,
  } as Session);
  const data = useSelector((state: any) => state.location);

  /**
   * This function updates the location of a user by retrieving their current position and updating their
   * latitude, longitude, and speed in a database.
   * @param {string} userId - The `userId` parameter is a string that represents the unique identifier of
   * a user. It is used to update the location data of a specific user in the database.
   * @returns The `onUpdateLocation` function is returning the result of calling the `onUpdate` function
   * with the arguments "usersdata", `userId`, and an object containing the latitude, longitude, and
   * speed properties of the current location. The `onUpdate` function is not shown in the code snippet,
   * so it is unclear what it returns.
   */
  const onUpdateLocation = async (userId: string) => {
    const speed = data?.speed != null ? data?.speed * (3600 / 1000) : 0;
    const info = await onUpdate("usersdata", userId, {
      lat: data?.latitude,
      long: data?.longitude,
      speed: speed,
    });
    return info;
  };

  const onLogOut = async () => {
    await onSignOut();
  };

  useEffect(() => {
    requestPermissions();
    supabase.auth.onAuthStateChange((event: any, session: any) => {
      setSession(session);
    });
  }, []);

  useEffect(() => {
    if (session?.user != undefined) {
      onUpdateLocation(session?.user?.id);
    }
  }, [data]);

  return (
    <NativeBaseProvider>
      <NavigationContainer>
        <Stack.Navigator>
          {session?.access_token == undefined ? (
            <>
              <Stack.Screen
                name="Login"
                component={Login}
                options={{ title: "Iniciar sesión" }}
              />
            </>
          ) : (
            <>
              <Stack.Screen
                name="TrackList"
                component={TrackList}
                options={{
                  title: "Listado de ordenes",
                  headerBackVisible: false,
                  headerRight: () => (
                    <Entypo
                      onPress={() => onLogOut()}
                      name="log-out"
                      size={24}
                      color="black"
                    />
                  ),
                }}
              />
              <Stack.Screen name="Order" component={Order} />
            </>
          )}
        </Stack.Navigator>
      </NavigationContainer>
    </NativeBaseProvider>
  );
}

const mapState = ({ latitude, longitude, speed }: any) => ({
  latitude,
  longitude,
  speed,
});

const _App = connect(mapState)(Index);

TaskManager.defineTask(LOCATION_TASK, ({ data, error }: any) => {
  if (error) {
    // Error occurred - check `error.message` for more details.
    return;
  }
  if (data) {
    const { latitude, longitude, speed } = data?.locations[0]?.coords;
    LocationStore.dispatch(updateLocation({ latitude, longitude, speed }));
    // do something with the locations captured in the background
  }
});

export default _App;

For technical knowledge, i use redux to send data to keep updated the backend.
Please help me! :frowning:

I’ve updated my expo CLI, plugins, and file where I’m using the location task, and I tried this time with iOS, and it works well, but still the issue in android… Look my file now.

const Stack = createNativeStackNavigator();
const LOCATION_TASK = "background-location-task";

TaskManager.defineTask(LOCATION_TASK, ({ data, error }: any) => {
  if (error) {
    // Error occurred - check `error.message` for more details.
    console.log(error);
    return;
  }
  if (data) {
    const { latitude, longitude, speed } = data?.locations[0]?.coords;
    console.log(data?.locations[0]?.coords);
    LocationStore.dispatch(updateLocation({ latitude, longitude, speed }));
    // do something with the locations captured in the background
  }
});

export function Index() {
  const { supabase, onUpdate, onSignOut } = useSupabase();
  const [session, setSession] = useState<Session>({
    access_token: undefined,
  } as Session);
  const data = useSelector((state: any) => state.location);

  /**
   * This function updates the location of a user by retrieving their current position and updating their
   * latitude, longitude, and speed in a database.
   * @param {string} userId - The `userId` parameter is a string that represents the unique identifier of
   * a user. It is used to update the location data of a specific user in the database.
   * @returns The `onUpdateLocation` function is returning the result of calling the `onUpdate` function
   * with the arguments "usersdata", `userId`, and an object containing the latitude, longitude, and
   * speed properties of the current location. The `onUpdate` function is not shown in the code snippet,
   * so it is unclear what it returns.
   */
  const onUpdateLocation = async (userId: string) => {
    const speed = data?.speed != null ? data?.speed * (3600 / 1000) : 0;
    const info = await onUpdate("usersdata", userId, {
      lat: data?.latitude,
      long: data?.longitude,
      speed: speed,
    });
    return info;
  };

  // Start location tracking in background
  const startBackgroundUpdate = async () => {
    // Don't track position if permission is not granted
    const { granted } = await Location.getBackgroundPermissionsAsync();
    if (!granted) {
      console.log("location tracking denied");
      return;
    }

    // Make sure the task is defined otherwise do not start tracking
    const isTaskDefined = await TaskManager.isTaskDefined(LOCATION_TASK);
    if (!isTaskDefined) {
      console.log("Task is not defined");
      return;
    }

    // Don't track if it is already running in background
    const hasStarted = await Location.hasStartedLocationUpdatesAsync(
      LOCATION_TASK
    );
    if (hasStarted) {
      console.log("Already started");
      return;
    }

    await Location.startLocationUpdatesAsync(LOCATION_TASK, {
      // For better logs, we set the accuracy to the most sensitive option
      accuracy: Location.Accuracy.BestForNavigation,
      // Make sure to enable this notification if you want to consistently track in the background
      showsBackgroundLocationIndicator: true,
      foregroundService: {
        notificationTitle: "Location",
        notificationBody: "Location tracking in background",
        notificationColor: "#fff",
      },
    });
  };

  const onLogOut = async () => {
    await onSignOut();
    Location.stopLocationUpdatesAsync(LOCATION_TASK);
  };

  /**
   * This function requests foreground and background location permissions and starts location updates if
   * granted.
   */
  const requestPermissions = async () => {
    const { status: foregroundStatus } =
      await Location.requestForegroundPermissionsAsync();
    if (foregroundStatus === "granted") {
      const { status: backgroundStatus } =
        await Location.requestBackgroundPermissionsAsync();
      if (backgroundStatus === "granted") {
        await Location.startLocationUpdatesAsync(LOCATION_TASK, {
          accuracy: Location.Accuracy.Highest,
          distanceInterval: 1,
          timeInterval: 1500,
        });
      }
    }
  };

  useEffect(() => {
    requestPermissions();
    supabase.auth.onAuthStateChange((event: any, session: any) => {
      startBackgroundUpdate();
      setSession(session);
    });
  }, []);

  useEffect(() => {
    if (session?.user != undefined) {
      onUpdateLocation(session?.user?.id);
    }
  }, [data]);

  return (
    <NativeBaseProvider>
      <NavigationContainer>
        <Stack.Navigator>
          {session?.access_token == undefined ? (
            <>
              <Stack.Screen
                name="Login"
                component={Login}
                options={{ title: "Iniciar sesión" }}
              />
            </>
          ) : (
            <>
              <Stack.Screen
                name="TrackList"
                component={TrackList}
                options={{
                  title: "Listado de ordenes",
                  headerBackVisible: false,
                  headerRight: () => (
                    <Entypo
                      onPress={() => onLogOut()}
                      name="log-out"
                      size={24}
                      color="black"
                    />
                  ),
                }}
              />
              <Stack.Screen name="Order" component={Order} />
            </>
          )}
        </Stack.Navigator>
      </NavigationContainer>
    </NativeBaseProvider>
  );
}

const mapState = ({ latitude, longitude, speed }: any) => ({
  latitude,
  longitude,
  speed,
});

const _App = connect(mapState)(Index);

export default _App;

Hi there, was it possible for you to find the error? If so how?

No… Not yet, just created a bug report in GitHub Expo location and Task Manager doesn’t work in background · Issue #22183 · expo/expo · GitHub

  1. Permissions: Make sure you have the necessary permissions set up in your app to access location information in the background. On Android, you need to include the ACCESS_BACKGROUND_LOCATION permission in your app’s AndroidManifest.xml file. On iOS, you need to include the NSLocationAlwaysAndWhenInUseUsageDescription and NSLocationAlwaysUsageDescription keys in your app’s Info.plist file.
  2. Configuration: Ensure that you have correctly configured the Expo Location module to work in the background. You may need to set the accuracy property to a lower value, such as Location.Accuracy.Balanced or Location.Accuracy.Lowest, to allow background location updates.
  3. Background tasks: Expo provides a module called “TaskManager” that allows you to register and manage background tasks in your app. Make sure you have registered the appropriate task for background location updates using TaskManager.defineTask. You can refer to the Expo documentation for more details on setting up background tasks.
  4. Testing on a physical device: Keep in mind that some location-related functionality may not work as expected when testing on simulators or emulators. It is recommended to test your app on a physical device to ensure proper functionality in the background.

Hi, were you able to solve the problem, if so how?