React Native Expo Error : Invalid consumer argument - Must conform to EXTaskConsumerInterface protocol

136 views Asked by At

I am encountering an error in my React Native application that states "Possible Unhandled Promise Rejection (id: 0): Error: Invalid consumer argument. It must be a class that conforms to EXTaskConsumerInterface protocol." This error occurs during execution, and I'm struggling to understand its root cause and how to resolve it.

There is also the problem that notifications only work when sending calling sendPushNotification(expoPushToken.data) outside the background fetch code. The development app crashes whenever my BackgroundFetch.registerTaskAsync() code is called. My goal is to send a push notification at the time specified by the user.

Error Message:

Possible Unhandled Promise Rejection (id: 0):
Error: Invalid `consumer` argument. It must be a class that conforms to EXTaskConsumerInterface protocol.
Error: Invalid `consumer` argument. It must be a class that conforms to EXTaskConsumerInterface protocol.
    at construct (native)
    at apply (native)
    at _construct (http://10.150.240.83:8081/node_modules%5Cexpo%5CAppEntry.bundle//&platform=ios&dev=true&hot=false&lazy=true:27045:28)
    at Wrapper (http://10.150.240.83:8081/node_modules%5Cexpo%5CAppEntry.bundle//&platform=ios&dev=true&hot=false&lazy=true:27005:64)
    at construct (native)
    at _createSuperInternal (http://10.150.240.83:8081/node_modules%5Cexpo%5CAppEntry.bundle//&platform=ios&dev=true&hot=false&lazy=true:122694:322)
    at call (native)
    at CodedError (http://10.150.240.83:8081/node_modules%5Cexpo%5CAppEntry.bundle//&platform=ios&dev=true&hot=false&lazy=true:122707:26)

Following is what I tried so far.

Here is the code for the background tasks file:

import { sendPushNotification, useNotification } from "app/services/notifications"
import * as BackgroundFetch from "expo-background-fetch"
import * as TaskManager from "expo-task-manager"

export const BACKGROUND_FETCH_TASK_NOTIFY = "background-remote-notification"

TaskManager.defineTask(BACKGROUND_FETCH_TASK_NOTIFY, async ({data, error}) => {
  if(error){
    console.log(error.message);
  }
  else if(data){
  const now = Date.now()

  console.log(`Got notification background fetch call at date: ${new Date(now).toISOString()}`)

  try {
    
    const { expoPushToken } = useNotification(); // Use useNotification hook here to get the expoPushToken
    console.log(expoPushToken);

    if (expoPushToken) {
      await sendPushNotification(expoPushToken.data);
    } else {
      console.log("Expo push token not available.");
    }
  } catch (error) {
    console.error("Error handling background fetch task:", error);
  }

}

  // return BackgroundFetch.BackgroundFetchResult.NoData
})

export async function registerNotificationsAsync(notificationTime: Date) {
  try {
    console.log("Notification task initiation")
    // Calculate the time until the next user-defined time
    const now = new Date()

    let timeDiff = notificationTime.getTime() - now.getTime()
    if (timeDiff < 0) {
      timeDiff += 24 * 60 * 60 * 1000 // If the time has passed today, schedule for tomorrow
    }

    return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK_NOTIFY, {
      minimumInterval: Math.round(timeDiff / 1000), // Convert milliseconds to seconds
      stopOnTerminate: false, // android only,
      startOnBoot: true, // android only
    })
  } catch (error) {
    console.error("Error in registerNotificationsAsync", error)
    throw error
  }
}

export async function unregisterBackgroundFetchAsync(taskName: string) {
  console.log("Background fetch task unregister initiated")
  if(await TaskManager.isTaskRegisteredAsync(taskName)) {
  return BackgroundFetch.unregisterTaskAsync(taskName)
  }else{
    console.log("Background fetch " + taskName + " not registered")
  }
}

Here is the code for the where the task registration code is called:

import { BACKGROUND_FETCH_TASK_NOTIFY, registerNotificationsAsync, unregisterBackgroundFetchAsync } from "app/utils/backgroundTasks";
import { types } from "mobx-state-tree";

export const SettingsStoreModel = types
  .model("SettingsStore", {
    isNotificationsEnabled: types.optional(types.boolean, false),
    notificationTime: types.optional(types.Date, new Date(8).setHours(17,0,0,0)), // default time is 5 PM
  })
  .actions((self) => ({
    toggleNotifications() {
      self.isNotificationsEnabled = !self.isNotificationsEnabled;
      console.log("Notifications changes to "+ self.isNotificationsEnabled)
      if (registerNotificationsAsync && self.isNotificationsEnabled) {
        registerNotificationsAsync(self.notificationTime);
      } else if (unregisterBackgroundFetchAsync) {
        unregisterBackgroundFetchAsync(BACKGROUND_FETCH_TASK_NOTIFY);
      }
    },
    setNotificationTime(time: Date) {
      self.notificationTime = time;
      console.log("Notification time changes to "+ self.notificationTime)
        if(registerNotificationsAsync){
      registerNotificationsAsync(self.notificationTime);
        }
    },
  }));

Using the above logic, I could call toggleNotification or setNotificationTime to register for the background task. Except this is not what is happening and I can't seem to find a solution online.

I'm using Expo for my React Native project, and it seems like there might be an issue related to the consumer argument and its conformity to the EXTaskConsumerInterface protocol. However, I'm uncertain about how to resolve this issue. Please let me know if I can provide any more information. Any guidance or insights into this error would be greatly appreciated. Thank you!

0

There are 0 answers