2016-05-11 11 views
1

私はUIViewに進捗バーを含んでいます。私がやりたいことは簡単です、私はボタンを持っています、ユーザーはそのボタンをクリックし、アプリケーションはファイルをダウンロードし、進行状況バーに進行状況を表示します。ユーザーが最初にダウンロードボタンをクリックすると、これを行うことができます。しかし、ユーザーがもう一度ダウンロードをクリックすると、NSURLSession代理人は呼び出されません。iOS - NSURLSessionが2回目に動作しない

私のUIViewの.m

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     [self configure]; 
    } 
    return self; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super initWithCoder:aDecoder]; 
    if (self) { 
     [self configure]; 
    } 
    return self; 
} 

-(void)configure 
{ 
    [self createSpinner]; 
    [self createProgressBar]; 

    NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; 
    self.docDirectoryURL = [URLs objectAtIndex:0]; 

    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.tinkytickles"]; 
    sessionConfiguration.HTTPMaximumConnectionsPerHost = 1; 
    self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration 
               delegate:self 
              delegateQueue:nil]; 
} 

-(void)createSpinner 
{ 
    [self setBackgroundColor:[UIColor colorWithWhite:1.0f alpha:0.5f]]; 

    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 
    [self addSubview:spinner]; 
    [spinner setColor:original_new_dark_grey]; 
    [spinner setUserInteractionEnabled:NO]; 
    [spinner setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)]; 
    [spinner setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; 
    [spinner startAnimating]; 
} 

-(void)createProgressBar 
{ 
    self.progressBar = [[TYMProgressBarView alloc] initWithFrame:CGRectMake(0, 0, 280, 15)]; 
    [self.progressBar setBarBackgroundColor:[UIColor whiteColor]]; 
    [self.progressBar setBarBorderColor:original_new_dark_grey]; 
    [self.progressBar setBarFillColor:original_new_dark_grey]; 
    [self.progressBar setBarBorderWidth:1.0f]; 
    [self addSubview:self.progressBar]; 
    [self.progressBar setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)]; 
    [self.progressBar setHidden:YES]; 

    self.label = [[UILabel alloc] initWithFrame:CGRectMake(self.progressBar.frame.origin.x, self.progressBar.frame.origin.y - 30, self.progressBar.frame.size.width, 25)]; 
    [self.label setText:NSLocalizedString(locDownloading, nil)]; 
    [self.label setTextAlignment:NSTextAlignmentCenter]; 
    [self.label setTextColor:original_new_dark_grey]; 
    [self.label setFont:quicksand_14]; 
    [self addSubview:self.label]; 
    [self.label setHidden:YES]; 
} 

-(void)showProgressBarWithProgress:(CGFloat)progress withText:(NSString *)text 
{ 
    [spinner setHidden:YES]; 

    [self.label setText:[NSString stringWithFormat:NSLocalizedString(locDownloadingAt, nil), text]]; 
    [self.label setHidden:NO]; 
    [self.progressBar setHidden:NO]; 
    [self.progressBar setProgress:progress]; 
} 


-(void)stopAnimating 
{ 
    [spinner stopAnimating]; 
} 

-(void)startDownloadingURL:(PromoterDownloadInfo *)downloadInfo 
{ 
    info = downloadInfo; 

    if (!info.isDownloading) 
    { 
     if (info.taskIdentifier == -1) 
     { 
      info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]]; 
      info.taskIdentifier = info.downloadTask.taskIdentifier; 
      [info.downloadTask resume]; 
     } 
     else 
     { 
      info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData]; 
      [info.downloadTask resume]; 
      info.taskIdentifier = info.downloadTask.taskIdentifier; 
     } 
    } 
    else 
    { 
     [info.downloadTask cancelByProducingResumeData:^(NSData *resumeData) { 
      if (resumeData != nil) { 
       info.taskResumeData = [[NSData alloc] initWithData:resumeData]; 
      } 
     }]; 
    } 

    info.isDownloading = !info.isDownloading; 
} 

-(void)stopDownload:(PromoterDownloadInfo *)downloadInfo 
{ 
    if (!info.isDownloading) 
    { 
     if (info.taskIdentifier == -1) 
     { 
      info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]]; 
     } 
     else 
     { 
      info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData]; 
     } 

     info.taskIdentifier = info.downloadTask.taskIdentifier; 
     [info.downloadTask resume]; 
     info.isDownloading = YES; 
    } 

    [self stopAnimating]; 
    [self removeFromSuperview]; 
} 

#pragma mark - NSURLSession Delegate method implementation 

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location 
{ 
    NSError *error; 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 

    NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent; 
    NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:destinationFilename]; 

    if ([fileManager fileExistsAtPath:[destinationURL path]]) { 
     [fileManager removeItemAtURL:destinationURL error:nil]; 
    } 

    BOOL success = [fileManager copyItemAtURL:location 
             toURL:destinationURL 
             error:&error]; 

    if (success) { 
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
      [self stopAnimating]; 
      [self removeFromSuperview]; 
     }]; 
    } 
    else 
    { 
     NSLog(@"Unable to copy temp file. Error: %@", [error localizedDescription]); 
    } 
} 


-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ 
    if (error != nil) { 
     NSLog(@"Download completed with error: %@", [error localizedDescription]); 
    } 
    else{ 
     NSLog(@"Download finished successfully."); 
    } 
} 


-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 
{ 
    if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) { 
     NSLog(@"Unknown transfer size"); 
    } 
    else 
    { 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      info.downloadProgress = (double)totalBytesWritten/(double)totalBytesExpectedToWrite; 
      [self showProgressBarWithProgress:info.downloadProgress withText:info.fileTitle]; 
     }); 
    } 
} 


-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session 
{ 
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; 

    // Check if all download tasks have been finished. 
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { 

     if ([downloadTasks count] == 0) { 
      if (appDelegate.backgroundTransferCompletionHandler != nil) { 
       // Copy locally the completion handler. 
       void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler; 

       // Make nil the backgroundTransferCompletionHandler. 
       appDelegate.backgroundTransferCompletionHandler = nil; 

       [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
        // Call the completion handler to tell the system that there are no other background transfers. 
        completionHandler(); 

        // Show a local notification when all downloads are over. 
        UILocalNotification *localNotification = [[UILocalNotification alloc] init]; 
        localNotification.alertBody = NSLocalizedString(locDownloadComplete, nil); 
        [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification]; 
       }]; 
      } 
     } 
    }]; 
} 

私はこのような本のUIViewを使用します。

PromoterDownloadInfo *info = [[PromoterDownloadInfo alloc] initWithFileTitle:self.title andDownloadSource:@"https://www.mywebsite.com/file.zip"]; 
PromotersDownloadView *downloadView = [[PromotersDownloadView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
[self.navigationController.view addSubview:downloadView]; 
[downloadView startDownloadingURL:info]; 

私はそれは素晴らしい作品ダウンロードボタンをクリックした初めて。 2回目のNSURLSessionのみdidCompleteWithErrorメソッドが呼び出されます。ここで2回目のログから取得します:

2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists! 
2016-05-12 00:50:50.614 APP[32990:1230386] Download finished successfully. 

私は間違っていますか? NSURLSessionConfigurationを1回だけ作成しようとしましたが、この方法では代理人メソッドは呼び出されません。私は何をすべきか?

+0

@AndreyChernukhaエラーはありません。私は私の質問で提供したログを取得します。 (NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite'が呼び出されていない – imstillalive

+0

@SausageMachineしかし、情報ユーザーがダウンロードボタンをクリックするたびに作成されますので、ダウンロードは常に最初はNOです – imstillalive

+0

@Robダウンロードしたいファイルが10個ある場合。 10回のバックグラウンドセッションを作成しますか? – imstillalive

答えて

1

あなたは言った:

を私はダウンロードボタンをクリックした最初の時間は、それは素晴らしい作品。エラーはあなたが与えられた識別子のために一つだけの背景NSURLSessionをインスタンス化することを指摘された(そしてあなたは、一般的にのみ欲しい/必要

2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!<br /> 

:...ここで二度目のログを私から得るものです単一の背景セッション)。複数のインスタンスをインスタンス化する場合は、一意の識別子を与えますが、バックグラウンドセッションの処理は、不必要に複数のセッションを行うことなく複雑になります。私はあなたが1つのバックグラウンドセッションだけを望むことをお勧めします。

あなたは言った:

を、私は一度だけNSURLSessionConfigurationを作成しようとしましたが、この方法では、何のデリゲートメソッドが呼び出されなかっます。

はい、1つのセッション構成が必要です。同様に重要なことに、バックグラウンドセッションオブジェクトは1つだけです。

デリゲートオブジェクトが更新する必要があるビューを追跡できないという問題があると思われます。または、あなたのセッションオブジェクトへの参照が失われていて、参照がnilだったとします。それは、いくつかの異なることがあります、そして、あなたがこれをどうやって見ていないかは分かりません。

私は、このセッション構成コードをビューから移動し、どこでも参照できる共有インスタンスを用意することをお勧めします(たとえば、シングルトンがうまく動作するため、最初から必要な場所またはアプリケーションデリゲートのhandleEventsForBackgroundURLSessionメソッドから)。

唯一の課題は、どのビューがどのネットワーク要求を追跡しているかを追跡する方法です。このビューのインスタンス化に関係なく、すべての不完全な要求を追跡する単一のビューが必要ですか?その場合は、NSNotificationCenterの通知を使用することができます(このように、進捗状況の更新を通知する必要があるビューであれば、カスタム通知を確認できます)。または、特定のビューは、そのビューから開始したリクエストのみを扱いますか?その場合、ビューまたはオブジェクトがステータス更新について知る必要のある値(セッションオブジェクトがどのビューがどのタスクを気にしているかを把握できる方法)をマップする辞書の値をtaskIdentifierにマップする辞書を維持することができます。それはちょうどあなたのアプリの要件に依存します。

関連する問題