Reputation: 2197
My iOS app works in the following way
I have an array of audio file names, I check whether the file is present. If it's present it starts playing it. Once finished I start playing the next audio.
If the file is not present I make a NSURLSessionDataTask
POST request which returns me a string which is the URL of the file to be downloaded.(This URL is valid for one minute).
Once I receive the URL, I make a NSURLSessionDownloadTask
request and download the file, save the file and play the audio.
This entire process works perfectly fine when the app is in foreground. Also works perfectly fine when all the audios are present and the app is running in background.
The problem comes when the audio files are not present and the app is running in background
My code : BackgroundFetchManager.h
@interface BackgroundFetchManager : NSObject <NSURLSessionDownloadDelegate,NSURLSessionDataDelegate>
@property bool isDownloading;
@property NSURLSessionDownloadTask *download;
@property NSURLSessionDataTask *dataDownload;
@property (nonatomic, strong)NSURLSession *backgroundSession;
@property NSMutableArray *downloadQueue;
@property FileAccessManager *fileAccessManager;
@end
BackgroundFetchManager.m
Initialization function
-(id) init
{
self = [super init];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"testConfiguration"];
config.sessionSendsLaunchEvents = YES;
config.discretionary = YES;
self.backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
download = [NSURLSessionDownloadTask new];
dataDownload = [NSURLSessionDataTask new];
return self;
}
Data task
dataDownload = [self.backgroundSession dataTaskWithRequest:request];
[dataDownload setTaskDescription:@"Data task"];
Delegates of dataDownload (NSURLSessionDataTask
)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *url = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"This is the response received %@",url);
//download is the NSURLSessionDownloadTask
download = [self.backgroundSession downloadTaskWithURL:[NSURL URLWithString:url]];
[download resume];
}
Delegates of download (NSURLSessionDownloadTask
)
These are the ones which are not called when the app is in background
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
AppDelegate *a = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSURL *destinationURL = [fileAccessManager saveFile:a.downloadQueue[0] fromLocation:location]; // save file code
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// audio playing stuff
if (![task.taskDescription isEqualToString:@"Data task"]) {
NSLog(@"This is did complete with error %@", task );
AppDelegate *a = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if ([a.downloadQueue[0] isEqualToString:a.audioToBePlayed]) {
NSDictionary* userInfo = @{@"file": a.downloadQueue[0]};
[[NSNotificationCenter defaultCenter] postNotificationName:@"playThisStuff" object:nil userInfo:userInfo];
}
//starts next download
[a pop];
isDownloading = false;
if ([a.downloadQueue count] > 0) {
isDownloading = true;
[self downloadFile:a.downloadQueue[0]];
}
}
}
So in a nutshell, the downloadtask is not called after the datatask has finished.
As much as I know, NSURLSession
when configured to backgroundsession, the OS takes up the responsiblity of download, so ideally it should work. Am I missing something ?
Upvotes: 2
Views: 849
Reputation: 2365
The problem stems from the fact that iOS won't let you start a new task while the app is in the background. I believe that this is done to prevent apps from continuously firing off new tasks and keeping the app alive indefinitely.
Background tasks are generally suitable for gracefully handling your app transitioning from foreground to background while a task is in progress. It's the reason you're seeing the first task complete, but not the second.
One workaround would be to request background execution time from the OS when you fire off the initial data task:
UIBackgroundTaskIdentifier backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
}];
That should at least give your app a few minutes of background time to initiate the second task. Be sure to call -endBackgroundTask:
after you call -resume
on the second task.
Unfortunately, this cannot guarantee that all of your files will download. If you find that you're running out of background time, you may be better off using Silent Push Notifications to wake your app and download periodically.
Upvotes: 1