4

I implement the performFetchWithCompletionHandler (ie: background fetch) to download some data from a server.

To do this, inside performFetchWithCompletionHandler, to not block the main thread, i create and start a new task (so in a background thread), and when the task is finished i call the CompletionHandler given with performFetchWithCompletionHandler

But no matter, as soon as the performFetchWithCompletionHandler is called i receive immediately in the log (and before the downloading task finish)

Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called.

so i can't understand how to do ? does it's mean that all the job must be done inside the procedure performFetchWithCompletionHandler (so in the main thread) and performFetchWithCompletionHandler must return only when all the job is finished, blocking by this way the main thread ?

please help me to understand how to do correctly

This is my code (it's in delphi, but it's does exactly what i describe before without the network connection that i replace by sleep(15000) ) :

class procedure TMainForm.performFetchWithCompletionHandler(self: id; _cmd: SEL; application: PUIApplication; completionHandler: id);
begin

  aTaskID := SharedApplication.beginBackgroundTaskWithExpirationHandler(SynchLocationsTaskExpired);

  //load the Profiles in other thread to not block the calling thread
  fSynchLocationsTask := TThread.CreateAnonymousThread(
    procedure
    begin

      Sleep(15000);


      TThread.Queue(nil,
        procedure
        begin
          ExecutePerformFetchCompletionHandler(completionHandler, UIBackgroundFetchResultNewData); // ==> here the app crash like completionHandler is not valid anymore 
          SharedApplication.endBackgroundTask(aTaskID);  
        end);

    end);
  fSynchLocationsTask.FreeOnTerminate := False;
  fSynchLocationsTask.Start;

  // => here i receive in log Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called

end;
rmaddy
  • 314,917
  • 42
  • 532
  • 579
zeus
  • 12,173
  • 9
  • 63
  • 184
  • can you just show us the code? – mfaani Jul 22 '17 at 09:38
  • it's a little hard to show you because it's written in delphi and i m afraid you will not understand it, but the process is exactly like i say. performFetchWithCompletionHandler => create a background task => when task finish call completionHandler – zeus Jul 22 '17 at 09:42
  • I'm not sure, but have you tried not wrapping it inside a background task? I think as long as your have the appropriate backgroundMode selected for this in Xcode capabilities (ie Background Fetch) you should be fine. Perhaps you wrapping it is what confuses the compiler, but that shouldn't be the case – mfaani Jul 22 '17 at 09:44
  • 2
    You don't need to worry about blocking the main thread in `performFetchWithCompletionHandler` as you are not in the foreground; however if you return from this function without having called the `completionHandler` you must call `beginBackgroundTaskWithExpirationHandler` before returning and call `endBackgroundTask` after you call the completion handler – Paulw11 Jul 22 '17 at 09:48
  • not yet tried to wrap it inside a background task, but i m just curious if this is the good way to go? Maybe simply i need to do everything in the main thread inside performFetchWithCompletionHandler and not complicate my life with background thread? how other do ? – zeus Jul 22 '17 at 09:48
  • 1
    @Paulw11 : thank, can you clarify a little more (i think you can even make it as a useful answer). Is it a normal practice (ie: the way to go) to do everything in the main thread inside performFetchWithCompletionHandler ? i know the app is not in foreground, but what if the task take say 30 s to finish and in the middle the user want to put the app in foreground ? on the other hand it's seam that using beginBackgroundTaskWithExpirationHandler inside performFetchWithCompletionHandler look more like a hack – zeus Jul 22 '17 at 09:52
  • If you are performing a network operation then it is typical for that operation to be completed asynchronously. `beginBackgroundTaskWithExpirationHandler` is intended for exactly this situation; where you need more time in a background context. – Paulw11 Jul 22 '17 at 09:54
  • @Paulw11 yes i know about beginBackgroundTaskWithExpirationHandler but it's seam strange to be obliged to do this inside performFetchWithCompletionHandler when performFetchWithCompletionHandler is **only made** to perform network operation. and i don't really need more time (because beginBackgroundTaskWithExpirationHandler gave us already 30s). Also on all the sample i found on the net, none use beginBackgroundTaskWithExpirationHandler (exept one made by you) – zeus Jul 22 '17 at 09:57
  • 1
    The problem is that you are returning from the function without calling the completion handler and iOS doesn't know that you still have a background task running; that is why you need to call `beginBackgroundTaskWithExpirationHandler` – Paulw11 Jul 22 '17 at 10:01
  • @Paulw11 : Also I think that if it's was normal to process the network operation asynchonously (that is the case in normal world), ios will at least wait 30s (the time it gave us to execute the task) to test if the completion handler was never called. maybe the way to go is to do everything in synch ? – zeus Jul 22 '17 at 10:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149876/discussion-between-paulw11-and-loki). – Paulw11 Jul 22 '17 at 10:04
  • @Paulw11 : i confirm, even when i call beginBackgroundTaskWithExpirationHandler i still receive **immediatly** Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called. – zeus Jul 22 '17 at 10:53
  • Then you will need to share some code – Paulw11 Jul 22 '17 at 10:55
  • @Paulw11 i updated the question, and added the code, hope you will understand it – zeus Jul 22 '17 at 11:03
  • @Paulw11 also not that when i call completionHandler (in the background task), the app crash, look like completionHandler is not valid anymore :( – zeus Jul 22 '17 at 11:25
  • @Paulw11 ok i found that when i do imp_implementationWithBlock(completionHandler) inside the performFetchWithCompletionHandler then it's "seam" to work. this is really strange behavior – zeus Jul 22 '17 at 13:00
  • https://stackoverflow.com/a/23834114/95954 – Rudy Velthuis Jul 22 '17 at 18:29
  • @RudyVelthuis ... i not sure i understand, what i need to see in https://stackoverflow.com/questions/18315771/performfetchwithcompletionhandler-never-gets-fired/23834114#23834114 ? about debug mode ? – zeus Jul 22 '17 at 19:26
  • They seemed to have the same problem and solved it. And "debug mode" seems to be their problem, indeed. – Rudy Velthuis Jul 22 '17 at 19:33
  • aah ok, no no my problem is connected to delphi i thing. i must do in delphi imp_implementationWithBlock(completionHandler) to make it working. i don't know why in objective-c / swift then don't need to do this but i m not a specialist – zeus Jul 22 '17 at 19:38
  • @RudyVelthuis i need to call imp_implementationWithBlock(completionHandler) to make IOS understand that i m doing some stuff in background and not immediatly fire Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called. – zeus Jul 22 '17 at 19:43
  • Because `application:performFetchWithCompletionhandler:` expects a *block* (similar to Delphi's anonymous methods, but with a different binary interface) as the second parameter. – Rudy Velthuis Jul 22 '17 at 19:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149903/discussion-between-loki-and-rudy-velthuis). – zeus Jul 22 '17 at 20:14
  • @RudyVelthuis yes this i know but what i don't know is why i need first to do imp_implementationWithBlock(completionHandler) and them to my process in backgroundthread, and then i can do completionHandler. but it's not really matter important it's that it's work – zeus Jul 22 '17 at 20:16
  • `imp_implementationWithBlock()` Creates a pointer to a function that calls the specified block when the method is called. – Rudy Velthuis Jul 22 '17 at 20:36
  • yes i know, but why i need to create this pointer **at the beginning** even if i call this function much more later that is a mistery. I found also that all doc on internet about imp_implementationWithBlock (even on apple) is false (regarding the parameters). it's work in all exemples i found only by luck – zeus Jul 22 '17 at 21:30

1 Answers1

0
- (void)application:(UIApplication*)application performFetchWithCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler {
    NSLog(@"Started background fetch");

    // Create URL session based on NSURLSession object which is required for all background fetch operations.
    MyUrlSession* urlSession = MyUrlSession.new;

    // Update is asynchronous so we have to tell to application that
    // we will call completion handler asynchronously. It prevents showing warning
    // that completion handler was never called.
    UIBackgroundTaskIdentifier backgroundTaskIdentifier = [application beginBackgroundTaskWithName:@"MyBgTask" expirationHandler:^{
        // This will cancel update and will call completion handler with error.
        NSLog(@"Expiration handler for background fetch");
        [urlSession cancel];
    }];

    // Start URL session with completion handler.
    [urlSession startWithCompletionHandler:^(BOOL successful, NSString *errorString) {
        NSLog(@"Background fetch completed with success = %d, error = %@", successful, errorString);
        [application endBackgroundTask:backgroundTaskIdentifier];
        completionHandler(successful ? UIBackgroundFetchResultNewData : UIBackgroundFetchResultFailed);
        NSLog(@"Background fetch has ended.");
    }];
}