transistorsoft / react-native-background-fetch

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

[iOS] Multiple Simulated Events Fail - 'No launch handler registered for task with identifier' #495

Closed controtie closed 1 week ago

controtie commented 1 week ago

Your Environment

    BackgroundFetch.configure(
      {
        minimumFetchInterval: 15,
      },
      onLogSteps,
      onTimeout,
    );

Expected Behavior

Multiple simulated events should succeed in executing the registered handler

Actual Behavior

The first registered task completes, but subsequent calls error with "No handler registered error"

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No launch handler registered for task with identifier {public}@'

Steps to Reproduce

  1. Note: I'm putting my background registration code inside of a Context Wrapper, and a useEffect hook. I'm not sure that breaks things? What happens whe Background.configure is called multiple times (e.g. when the dependencies in useEffect change)?

Relevant code:

context/backgroundTasks.tsx

export const submitActivityStepCount = async (
  authToken: string,
  step_count: string | number,
) => {
  if (!authToken) return;

  try {
    const response = await fetch(
      getApiUrl('/api/v1/activity/forage/attempts/'),
      {
        method: 'PATCH',
        headers: {
          Authorization: `Bearer ${authToken}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({step_count}),
      },
    );

    if (response.status !== 200 && response.status !== 201) {
      throw 'Could not update step count for activity';
    }
  } catch (error) {
    throw error;
  }
};

export const BackgroundTasksProvider: React.FC<{
  children: ReactNode;
}> = ({children}) => {
  const {authToken} = useLoginContext();

  useEffect(() => {
    if (!authToken) {
      console.log('not registering, no auth token available...');
      return;
    }

    const onFetchTodaysStepCount = () => {
      const {startDate, endDate} = getDailyDateboundsUTC();
      const stepOptions = {
        startDate,
        endDate,
      };

      return new Promise((resolve, reject) => {
        AppleHealthKit.getStepCount(
          stepOptions,
          (callbackError: string, results: HealthValue) => {
            if (results) {
              resolve(results.value);
            }
          },
        );
      });
    };

    const onLogSteps = async (taskId: any) => {
      console.log('LOGGING STEPS');

      const count = await onFetchTodaysStepCount();
      console.log('count is', count);
      const forageStartingSteps = await AsyncStorage.getItem(
        FORAGE_START_STEPS,
      );
      const attemptSteps = count - (forageStartingSteps || 0);
      console.log('attempt steps:', attemptSteps);

      await submitActivityStepCount(authToken, attemptSteps);
      console.log('DONE');

      BackgroundFetch.finish(taskId);
    };

    const onTimeout = async (taskId: any) => {
      BackgroundFetch.finish(taskId);
    };

    console.log('Configuring background fetch...');
    BackgroundFetch.configure(
      {
        minimumFetchInterval: 15, // 15 minutes
      },
      onLogSteps,
      onTimeout,
    );
  }, [authToken]);

  return (
    <BackgroundTasksContext.Provider value={{}}>
      {children}
    </BackgroundTasksContext.Provider>
  );
};

App.tsx

function App(): React.JSX.Element {
  return (
    <LocalStorageContextWrapper>
      <LoginContextWrapper>
        <HealthKitContextWrapper>
          <GoogleFitContextWrapper>
            <FitnessDataContextWrapper>
              <BackgroundTasksProvider>
                <Navigation />
              </BackgroundTasksProvider>
            </FitnessDataContextWrapper>
          </GoogleFitContextWrapper>
        </HealthKitContextWrapper>
      </LoginContextWrapper>
    </LocalStorageContextWrapper>
  );
}

Context

I'm trying to log the user's steps to my server, even while the app is closed.

Debug logs

(lldb) e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.transistorsoft.fetch"]
[TSBackgroundFetch handleBGAppRefreshTask]
[TSBackgroundFetch scheduleBGAppRefresh] com.transistorsoft.fetch
- RNBackgroundFetch Received fetch event react-native-background-fetch
LOGGING STEPS
'count is', 3056
'attempt steps:', 0
(lldb) e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.transistorsoft.fetch"]
*** Assertion failure in -[BGTaskScheduler _simulateLaunchForTaskWithIdentifier:], BGTaskScheduler.m:595
<NSXPCConnection: 0x3022f95e0> connection to service with pid 81 named com.apple.duetactivityscheduler: Exception caught during invocation of reply block to message 'getPendingTaskRequestsWithCompletionHandler:'.

Exception: No launch handler registered for task with identifier {public}@
(
    0   CoreFoundation                      0x00000001975bab34 3A5F992A-D1CD-312E-BD2E-F7C66343A417 + 969524
    1   libobjc.A.dylib                     0x000000018f432f78 objc_exception_throw + 60
    2   Foundation                          0x0000000196a1f920 D92E19C1-6299-3E94-8614-C505D5ABCCDB + 7055648
    3   BackgroundTasks                     0x00000002128a5afc 14DB8219-DF20-3A1C-9B0F-4AC5FD8A7889 + 19196
    4   BackgroundTasks                     0x00000002128a42ac 14DB8219-DF20-3A1C-9B0F-4AC5FD8A7889 + 12972
    5   CoreFoundation                      0x00000001974ffc84 3A5F992A-D1CD-312E-BD2E-F7C66343A417 + 203908
    6   CoreFoundation                      0x00000001974ff710 3A5F992A-D1CD-312E-BD2E-F7C66343A417 + 202512
    7   Foundation                          0x00000001963ca698 D92E19C1-6299-3E94-8614-C505D5ABCCDB + 415384
    8   Foundation                          0x00000001963ca218 D92E19C1-6299-3E94-8614-C505D5ABCCDB + 414232
    9   Foundation                          0x00000001963c9b80 D92E19C1-6299-3E94-8614-C505D5ABCCDB + 412544
    10  libxpc.dylib                        0x00000001f3511ff8 042EA33B-3C8F-3FB8-869C-369A3115977C + 126968
    11  libxpc.dylib                        0x00000001f35047a8 042EA33B-3C8F-3FB8-869C-369A3115977C + 71592
    12  libdispatch.dylib                   0x0000000106e0283c _dispatch_client_callout3 + 20
    13  libdispatch.dylib                   0x0000000106e2190c _dispatch_mach_msg_async_reply_invoke + 392
    14  libdispatch.dylib                   0x0000000106e0a4a4 _dispatch_lane_serial_drain + 376
    15  libdispatch.dylib                   0x0000000106e0b408 _dispatch_lane_invoke + 408
    16  libdispatch.dylib                   0x0000000106e18404 _dispatch_root_queue_drain_deferred_wlh + 328
    17  libdispatch.dylib                   0x0000000106e17a38 _dispatch_workloop_worker_thread + 444
    18  libsystem_pthread.dylib             0x00000001f34b0f20 _pthread_wqthread + 288
    19  libsystem_pthread.dylib             0x00000001f34b0fc0 start_wqthread + 8
)
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No launch handler registered for task with identifier {public}@'
*** First throw call stack:
(0x1975bab28 0x18f432f78 0x196a1f920 0x2128a5afc 0x2128a42ac 0x1974ffc84 0x1974ff710 0x1963ca698 0x1963ca218 0x1963c9b80 0x1f3511ff8 0x1f35047a8 0x106e0283c 0x106e2190c 0x106e0a4a4 0x106e0b408 0x106e18404 0x106e17a38 0x1f34b0f20 0x1f34b0fc0)
libc++abi: terminating due to uncaught exception of type NSException
christocracy commented 1 week ago

IMG_0859

controtie commented 1 week ago

Sorry, my description wasn't clear, I'm running on a real device!

christocracy commented 1 week ago

What happens whe Background.configure is called multiple times

Don’t call it multiple times. You are to call it only once.