iOS-NSURLSession第二次不工作

iOS-NSURLSession第二次不工作,ios,objective-c,Ios,Objective C,我有一个ui视图,其中包含一个进度条。我想做的很简单,我有一个按钮,用户点击该按钮,应用程序下载文件并在进度条中显示进度。当用户第一次单击下载按钮时,我就能够做到这一点。但当用户再次单击下载时,NSURLSession不会调用委托 我的观点 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self configure]; } r

我有一个
ui视图
,其中包含一个进度条。我想做的很简单,我有一个按钮,用户点击该按钮,应用程序下载文件并在进度条中显示进度。当用户第一次单击下载按钮时,我就能够做到这一点。但当用户再次单击下载时,
NSURLSession
不会调用委托

我的观点

- (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];
我第一次点击下载按钮时,效果非常好。第二次调用
NSURLSession
only
didcompletewitheror
方法。以下是我第二次从日志中得到的信息:

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.
2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with  identifier com.app already exists!<br />
我做错了什么?我尝试只创建一次
NSURLSessionConfiguration
,但这样就不会调用委托方法。我该怎么办?你说:

我第一次点击下载按钮时,它工作得很好。。。以下是我第二次从日志中得到的信息:

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.
2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with  identifier com.app already exists!<br />
2016-05-12 00:50:47.440应用[32990:1230071]标识符为com.APP的后台URLSession已存在
该错误指出,对于给定标识符,您只需要实例化一个后台会话(通常只需要/想要一个后台会话)。如果要实例化多个会话,可以为它们提供唯一的标识符,但是处理后台会话已经足够复杂了,而且不需要多个会话。我建议你只需要一次背景培训

你说:

我尝试只创建一次
NSURLSessionConfiguration
,但这样就不会调用委托方法

是的,您应该有一个会话配置。而且,同样重要的是,只有一个后台会话对象

我怀疑您的委托对象无法跟踪它应该更新的视图有问题。或者您丢失了对会话对象的引用,而引用是
nil
。这可能是两件不同的事情,如果不看看你是怎么做到的,你很难知道

我建议将此会话配置代码移出视图,并拥有一些可在任何地方引用的共享实例(例如,单例运行良好,因此您可以从任何需要它的地方实例化它,无论是从视图还是从应用程序代理的
handleEventsForBackgroundURLSession
方法)


唯一的挑战是如何跟踪哪些视图跟踪哪些网络请求。是否希望有一个单独的视图来跟踪所有未完成的请求,而不管该视图何时实例化?如果是这样,您可以使用
NSNotificationCenter
通知(这样,任何希望获得进度更新通知的视图都可以查看您的自定义通知)。或者给定的视图是否只关心您从该视图发起的请求?在这种情况下,您可以维护字典,将
taskIdentifier
值映射到视图或对象需要了解的状态更新(您可以通过何种方式让会话对象跟踪哪些视图关心哪些任务)。这取决于你的应用程序的要求。

@AndreyChernukha我没有收到任何错误。我得到了我在问题中提供的日志。第二次
-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask did writeData:(int64_t)byteswrited totalbyteswrited:(int64_t)totalbyteswrited totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
不可用called@SausageMachine但是每次用户单击下载按钮时,信息都会被创建,因此isDownloading始终是初始设置NO@Rob如果我有10个文件要下载。我是否创建10个后台会话?