AFNetworking の非同期性に関係する投稿のほとんどを読みました。しかし、私の問題は少し独特で、助けが必要です。
Web サーバーからコンテンツをダウンロードしていることをユーザーに通知するモーダル ビュー コントローラーから新しい非同期スレッドを開始しています。アイデアは、大量のファイル (100 以上) をダウンロードすることであり、うまく機能しています。ダウンロードに再試行回数を設定して、失敗した場合にファイルのダウンロードを再試行するようにしました (最大)。すべてがうまくダウンロードされています。問題は、それがいつ行われるか分からないことです。
理由は次のとおりです。JSON ファイルのリストをダウンロードしています。これらの JSON ファイルは、PDF およびその他の種類のファイルのリストを含む他の JSON ファイルのリストを定義します。ダウンロードするものの性質上、順番にダウンロードする必要があります。例えば:
- file1.json をダウンロード
- ダウンロード後に file1.json から JSON ファイルのリストを取得する
- これらのセカンダリ JSON ファイル (subFile1.json、subFile2.json など) をそれぞれ読み込みます。
- セカンダリ JSON ファイル (subFile1.json) がダウンロードされた後、その JSON ファイル (subFile1.json) からダウンロードするファイルのリストを取得します。
- JSON サブファイルで指定された各ファイルをダウンロードします (たとえば、subFile1.json)。
したがって、私のプロセスは階層的に見え、親ファイルが子ファイルの前にダウンロードされるようにします。 ) ON SUCCESS (成功ブロック内)
ご覧のとおり、ダウンロードするファイルの数は動的です。したがって、事前にダウンロードするファイルの数がわかりません。複数のレベルに降りて、Web サーバー上の複数のディレクトリから来る可能性があるという事実に基づいて、より困難になっています。
また、ダウンロードが失敗した場合 (最大再試行回数まで) には、各メソッドに対して再帰的なコールバックを行います。
ダウンロードごとに独自のスレッドが開始されるため (これは AFNetworking が行っていることだと思います)、すべてのダウンロードがいつ完了するかはわかりません。enqueueBatchOfHTTPRequestOperations が役立つかどうかはわかりません。よくわかりません。Web サーバーの複数のディレクトリからダウンロードしています。また、定義済みの JSON ファイルをダウンロードして解析するまでどこまで行くかわからないため、ダウンロードの各レベルに基づいて操作をバッチ処理する必要があります。
ヘルプ!!!!
コードを入れると役立つと思います。見るコードはたくさんありますが、難しい問題です (まあ、私のスキルのある人にとっては)。
コードを見ると、別のファイルをダウンロードする必要があるたびに、一番下のメソッドを呼び出します。
- (void)downloadLibraryFile:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount completionBlock:(DownloadLibraryFileCompletionBlock)completionBlock
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:fileOnServer]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:targetFile append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(@"Successfully downloaded file to %@", path);
completionBlock(fileOnServer, targetFile, retryCount, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionBlock(fileOnServer, targetFile, retryCount, error);
}];
[operation start];
}
これでAFNetorkingが起動し、ダウンロードが開始されます。完了すると完了ブロックが呼び出されますが、それらがすべて完了したことをどのように知ることができますか?
残りのコードは次のとおりです(上記のメソッドを含む)
- (void)downloadLibraryOnReset
{
// Find and copy the page defintion file to the documents directoy
// TODO localize the call to get the appropriate file based on language
dispatch_queue_t queue = dispatch_queue_create("Library Download Queue", NULL);
dispatch_async(queue, ^{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
// Save server root URL
self.serverRootURL = serverLibraryURL;
// Add last component onto download path
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitions];
// Get target location
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);;
NSString *targetFile = [dirPaths objectAtIndex:0];
targetFile = [targetFile stringByAppendingPathComponent:@"en"]; // TODO this needs to be localized based on language
targetFile = [targetFile stringByAppendingPathComponent:kPageDefinitionsDirectory];
self.pageDefintiionDirectoryURL = targetFile;
// Create the subdirectory off of the documents directory to contain the config files
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr createDirectoryAtPath:targetFile withIntermediateDirectories:YES attributes:nil error: NULL] == NO)
{
// Failed to create directory
}
NSString *pageDefinitionsFileURL = [targetFile stringByAppendingPathComponent:kPageDefinitions];
[self downloadPageDefinitionsFile:serverLibraryURL targetFile:pageDefinitionsFileURL retryCount:kDownloadRetryCount];
// Reset the resetContent flag to false
[defaults setBool:NO forKey:kResetContent];
});
}
- (void)downloadPageDefinitionsFile:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount
{
[self downloadLibraryFile:fileOnServer targetFile:targetFile retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
[self downloadPageDefinitionsFile:fileOnServer targetFile:targetFile retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), fileOnServer);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
}
// Copy down all config files defined in the pagedefinitions.json file
//code from other library
NSError* err = nil;
NSString *path = self.pageDefintiionDirectoryURL;
path = [path stringByAppendingPathComponent:kPageDefinitions];
NSData *data = [NSData dataWithContentsOfFile:path];
if(data)
{
// Convert to JSON Directory
NSMutableDictionary *pageDefinitionsDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
//fileVersion = pageDefinitionsDict[kFileVersion];
NSMutableArray *pages = pageDefinitionsDict[kPages];
for (NSMutableDictionary *page in pages)
{
MINPageDefinition *pageDef = [[MINPageDefinition alloc] initWithDictionary:page];
NSString *targetDirectory = [targetFile stringByDeletingLastPathComponent];
pageDef.pageURL = [targetDirectory stringByAppendingPathComponent:pageDef.pageConfigFileName];
//NSString *imageURL = [pageDef.pageURL stringByDeletingLastPathComponent];
pageDef.pageImageURL = [self.pageDefintiionDirectoryURL stringByAppendingPathComponent:pageDef.pageImageName];
[[MINPageStore sharedInstance].pageArray addObject:pageDef];
}
// Write modified pagedefinitions.json to the appropriate directory in Documents
[[MINPageStore sharedInstance] writePageDefinitionFile:path];
// Continue downloading page images and other config files,
for (MINPageDefinition *currPage in [[MINPageStore sharedInstance] pageArray])
{
[self downloadPageDefinitionImageFile:currPage retryCount:kDownloadRetryCount];
[self downloadPageDefinitionJSONFile:currPage retryCount:kDownloadRetryCount];
}
}
}
}];
}
- (void)downloadPageDefinitionJSONFile:(MINPageDefinition *)pageDef retryCount:(int)retryCount
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:pageDef.pageConfigFileName];
[self downloadLibraryFile:serverLibraryURL targetFile:pageDef.pageURL retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
[self downloadPageDefinitionJSONFile:pageDef retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
// Copy down all config files defined in the pagedefinitions.json file
if([pageDef.pageType isEqualToString:kGridView])
{
[self downloadGridViewContent:pageDef];
}
else
{
//NSLog(@">>>>FINISHED DOWNLOADING PAGE: %@", pageDef.pageName);
}
}
}];
}
- (void)downloadPageDefinitionImageFile:(MINPageDefinition *)pageDef retryCount:(int)retryCount
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:pageDef.pageImageName];
[self downloadLibraryFile:serverLibraryURL targetFile:pageDef.pageImageURL retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
[self downloadPageDefinitionImageFile:pageDef retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.masterViewController.tableView reloadData];
});
}
}];
}
- (void)downloadGridViewContent:(MINPageDefinition *)pageDef
{
// Parse off the json extension
// Use this to create a subdirectory under the pagedefinitions directoy
NSString *newDirectoryForGridView = [pageDef.pageURL stringByDeletingPathExtension];
newDirectoryForGridView = [newDirectoryForGridView lastPathComponent];
NSString *newGridViewDirectoryURL = [pageDef.pageURL stringByDeletingPathExtension];
// Create the subdirectory off of the documents directory to contain the config files
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr createDirectoryAtPath:newGridViewDirectoryURL withIntermediateDirectories:YES attributes:nil error: NULL] == NO)
{
// Failed to create directory
}
// Load the grid view config file
[MINVolume loadAlbumItems:pageDef.pageURL completionBlock:^(NSString *fileName, MINVolume *newVolume, NSError *error) {
if(!error)
{
if(newVolume && [newVolume.albumsArray count] > 0)
{
// Iterate through the albums and create directories for each album
for(MINAlbum *album in newVolume.albumsArray)
{
NSString *localAlbumDirectory = [newGridViewDirectoryURL stringByAppendingPathComponent:album.albumURL];
if ([filemgr createDirectoryAtPath:localAlbumDirectory withIntermediateDirectories:YES attributes:nil error: NULL] == NO)
{
// Failed to create directory
}
// Copy down all album content
for(MINAlbumItem *albumItem in album.albumItemsArray)
{
// Create names for local file and thumbnail
NSString *localAlbumItemFileURL = [localAlbumDirectory stringByAppendingPathComponent:albumItem.itemFileName];
NSString *localAlbumItemFileThumbURL = [localAlbumDirectory stringByAppendingPathComponent:albumItem.itemThumbnailImageName];
// Define paths for file and thumbnail on server
NSString *serverAlbumItemFileURL = [self.serverRootURL stringByAppendingPathComponent:newDirectoryForGridView];
serverAlbumItemFileURL = [serverAlbumItemFileURL stringByAppendingPathComponent:album.albumURL];
serverAlbumItemFileURL = [serverAlbumItemFileURL stringByAppendingPathComponent:albumItem.itemFileName];
NSString *serverAlbumItemFileThumbURL = [self.serverRootURL stringByAppendingPathComponent:newDirectoryForGridView];
serverAlbumItemFileThumbURL = [serverAlbumItemFileThumbURL stringByAppendingPathComponent:album.albumURL];
serverAlbumItemFileThumbURL = [serverAlbumItemFileThumbURL stringByAppendingPathComponent:albumItem.itemThumbnailImageName];
// Copy album item file
BOOL bFileExists = [filemgr fileExistsAtPath:localAlbumItemFileURL];
if(!bFileExists)
{
[self downloadAlbumItem:albumItem isThumbnail:(BOOL)false fileOnServer:serverAlbumItemFileURL targetFile:localAlbumItemFileURL retryCount:kDownloadRetryCount];
}
else
{
albumItem.itemURL = localAlbumItemFileURL;
}
// Copy album item thumbnail
BOOL bFileThumbnailExists = [filemgr fileExistsAtPath:localAlbumItemFileThumbURL];
if(!bFileThumbnailExists)
{
[self downloadAlbumItem:albumItem isThumbnail:true fileOnServer:serverAlbumItemFileThumbURL targetFile:localAlbumItemFileThumbURL retryCount:kDownloadRetryCount];
}
else
{
albumItem.itemThumbnailURL = localAlbumItemFileThumbURL;
}
}
}
}
else
{
NSLog(@"No volume found for file: %@", pageDef.pageConfigFileName);
}
}
}];
}
- (void)downloadAlbumItem:(MINAlbumItem *)albumItem isThumbnail:(BOOL)isThumbnail fileOnServer:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount
{
[self downloadLibraryFile:fileOnServer targetFile:targetFile retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
[self downloadAlbumItem:albumItem isThumbnail:isThumbnail fileOnServer:fileOnServer targetFile:targetFile retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), fileOnServer);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
}
if(isThumbnail)
albumItem.itemThumbnailURL = targetFile;
else
albumItem.itemURL = targetFile;
}
}];
}
- (void)downloadLibraryFile:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount completionBlock:(DownloadLibraryFileCompletionBlock)completionBlock
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:fileOnServer]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:targetFile append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(@"Successfully downloaded file to %@", path);
completionBlock(fileOnServer, targetFile, retryCount, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionBlock(fileOnServer, targetFile, retryCount, error);
}];
[operation start];
}