Search code examples
iosbackground-process

iOS - background fetch and threads


I have app, that uses background fetch and update location during it. It works fine with this code:

-(void)backgroundDidUpdateLocations:(NSArray *)locations
{    
    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier locationUpdateTaskID = [app beginBackgroundTaskWithExpirationHandler:
    ^{
        dispatch_async(dispatch_get_main_queue(),
        ^{            
            if (locationUpdateTaskID != UIBackgroundTaskInvalid)
            {

                if (_backgroundFetchCompletionHandler != nil)
                {
                    NSLog(@"BG location finished 1");
                    _backgroundFetchCompletionHandler(UIBackgroundFetchResultNewData);
                    _backgroundFetchCompletionHandler = nil;
                }

                [app endBackgroundTask:locationUpdateTaskID];
                locationUpdateTaskID = UIBackgroundTaskInvalid;
            }
        });
    }];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{                        
        if (_delegate && [_delegate respondsToSelector:@selector(myLocationManager:didUpdateLocations:)])
        {
            [_delegate myLocationManager:self didUpdateLocations:locations];
        }

        dispatch_async(dispatch_get_main_queue(),
        ^{            
            if (locationUpdateTaskID != UIBackgroundTaskInvalid)
            {
                if (_backgroundFetchCompletionHandler != nil)
                {
                    NSLog(@"BG location finished 2");
                    _backgroundFetchCompletionHandler(UIBackgroundFetchResultNewData);
                    _backgroundFetchCompletionHandler = nil;
                }

                [app endBackgroundTask:locationUpdateTaskID];
                locationUpdateTaskID = UIBackgroundTaskInvalid;
            }
        });
    });

}

But there is a problem with this:

[_delegate myLocationManager:self didUpdateLocations:locations];

if I use some simple call, it fine. But if _delegate method runs some another background operation, like download data from internet, I have a problem. Download is started asynchronously, so my background fetch is "closed" with backgroundFetchCompletionHandler before data are delivered. Data are than delivered outside of this scope and I am afraid, it can cause some problems. How to solve this problem correctly?

Edit: I can not modify _delegate code, so pass handler as variable is not an option.


Solution

  • If the delegate is set and responds then pass it the completion handler so that it can do its work and call the completion handler when it's done. If the delegate isn't set or doesn't respond then call the completion handler directly.