2018年04月6日网站服务器迁移完成……

iOS 7 SDK:后台传输服务

ios 苏 demo 1601℃ 0评论

本文主要说说如何使用iOS 7多任务处理中的后台传输服务(Background Transfer Service),并讲述如何创建一个app–当不在前台运行时也能下载文件的app。一旦完成全部下载,还会弹出一个通知信息提醒用户。

后台传输服务最初是由ios 6引入的,允许app在前台和后台传输文件,但有时间限制。最大的问题是“limited minutes”不允许用户下载或上传比较大的文件。这也是苹果在IOS 7中改进这项功能的原因。

在iOS 7中,该功能发生了以下变化:
iOS系统管理下载和上传内容
当用户关闭app后继续传输内容
没有时间限制
可以随时加入队列(前台和后台)
为了处理认证、错误或者completion事件,app会被唤醒
该app包含一个Progress View

后台传输服务可以用来处理几个不同的有用的任务,比如上传照片或视频,结合后台获取和远程通知,以及保持应用更新(比如购买书籍、TV shows、游戏内容以及其他)。

1.设置工程
要创建该服务,我们需要一个single view,并包含如下属性:
A ViewController
A NavigationController
A Bar Item (to start the Download)
An UIDocumentInterationController (to open the PDF document download)

首先,开始一个新的Xcode iPhone工程。然后创建一个Single View app。接着到Main.Storyboard,在view中添加一些对象。想要添加NavigationController,选中Default View Controller。在Xcode菜单中,选择Editor > Embed In > Navigation Controller。把Bar Item和Progress View拖放至View Controller。一旦完成,View Controller
然后,添加必要的属性来与我们之前添加的对象进行交互。在ViewController.h添加以下代码:
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
– (IBAction)start:(id)sender;
现在改变ViewController.m的视图。你会看到一个警示,不过不用担心,我们稍后再修复它。回到Main.Storyboard,并链接好相关的属性和动作,这些步骤比较琐碎。

2.NSURLSession
NSURLSession类和相关的类提供了一个可以通过HTTP上传或者下载内容的API,主要用来管理一系列传输任务。我们需要创建三个与这个类直接相关的对象:NSURLSession, NSURLSessionDownloadTask以及 UIDocumentInteractionController。

ViewController.h看起来会是这样:
@property (nonatomic) NSURLSession *session;
@property (nonatomic) NSURLSessionDownloadTask *downloadTask;
@property (strong, nonatomic) UIDocumentInteractionController *documentInteractionController;
另外,你还要声明四个协议:NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate以及UIDocumentInteractionControllerDelegate。

@interface应该是像这样的:
@interface ViewController : UIViewController < NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate,UIDocumentInteractionControllerDelegate>
我们将要添加的属性对于session的实例化和操作,以及下载处理非常有好处:可以恢复、暂停、取消或检索状态信息。最后一个属性–UIDocumentInterationController在本文中是用来展示下载的PDF文档。

打开ViewController.m。第一个需要完成的任务是把字符串添加至文件下载的位置。你应该使用一个标准的苹果PDF。
static NSString *DownloadURLString = @”https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/ObjC_classic/FoundationObjC.pdf”;
在viewDidLoad 方法中, 我们实例化并设置session和progress view:
self.session = [self backgroundSession];
self.progressView.progress = 0;
self.progressView.hidden = YES;

由于你执行了一个并不存在的backgroundSession方法,所以你必须声明它,该方法主要用来产生一个在后台运行的会话。NSURLSessionConfiguration接收的NSString参数是我们创建的会话的ID,对于每个NSURLSession实例,这个ID必须是独一无二的。
完整的方法如下:
– (NSURLSession *)backgroundSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@”com.example.apple-samplecode.SimpleBackgroundTransfer.BackgroundSession”];
session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
return session;
}
(IBAction)start:(id)sender方法开启了文档下载。你将会初始化一个NSURL和 NSURLRequest,并使用downloadTask属性把请求对象传递至downloadTaskWithRequest方法,完整的方法如下:
– (IBAction)start:(id)sender {
if (self.downloadTask) {
return;
}
NSURL *downloadURL = [NSURL URLWithString:DownloadURLString];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
self.downloadTask = [self.session downloadTaskWithRequest:request];
[self.downloadTask resume];
self.progressView.hidden = NO;
}

3.协议
在这一点上,你会注意到3个警示,它们表示应该执行协议方法。NSURLSessionDownloadDelegate协议定义了处理下载任务的方法。想要执行下载,它需要使用三个委托方法,所以添加以下三个方法:
1. (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:
(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
2. (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL {
3. (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes

(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite方法主要用来跟踪整个下载进程,同时也相应地更新 progressView。整个方法如下:

– (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
if (downloadTask == self.downloadTask) {
double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
NSLog(@”DownloadTask: %@ progress: %lf”, downloadTask, progress);
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
});
}
}

4.下载任务
(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL方法处理数据(origin and destination)。它在下载完成后控制文件。简化起见,它通知delegate下载任务已经完成。它包含已经完成的session task和download task,以及文件URL(可以在此找到临时文件)。看起来应该是这样:
– (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = [URLs objectAtIndex:0];
NSURL *originalURL = [[downloadTask originalRequest] URL];
NSURL *destinationURL = [documentsDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
NSError *errorCopy;
// For the purposes of testing, remove any esisting file at the destination.
[fileManager removeItemAtURL:destinationURL error:NULL];
BOOL success = [fileManager copyItemAtURL:downloadURL toURL:destinationURL error:&errorCopy];
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
//download finished – open the pdf
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:destinationURL];
// Configure Document Interaction Controller
[self.documentInteractionController setDelegate:self];
// Preview PDF
[self.documentInteractionController presentPreviewAnimated:YES];
self.progressView.hidden = YES;
});
} else {
NSLog(@”Error during the copy: %@”, [errorCopy localizedDescription]);
}
}
最后 (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes必须也被声明,但要清楚我们不会进一步使用它。
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
}
5.Session Tasks
你几乎完成了这个类,只剩下两个方法: (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error和 – (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session.

第一个方法通知delegate任务已经完成了数据传输,你也应该用它来跟踪发生的任何错误。
– (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error == nil) {
NSLog(@”Task: %@ completed successfully”, task);
} else {
NSLog(@”Task: %@ completed with error: %@”, task, [error localizedDescription]);
}
double progress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
});
self.downloadTask = nil;
}
最后,你需要添加 (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session方法,它通知delegate所有的session消息列队已经交付。为了加载UILocalNotification,它实例化AppDelegate.你应该使用如下方法:
– (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
NSLog(@”All tasks are finished”);
}
由于你没有把AppDelegate.h移植到你的类中,所以会出现几个错误:
#import “AppDelegate.h”
把两个对象添加至AppDelegate.h:
@property (strong, nonatomic) UIWindow *window;
@property (copy) void (^backgroundSessionCompletionHandler)();
在AppDelegate.m中,你应该执行delegate方法(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler.它通知delegate–与URL session相关的事件正等待处理,并调用自定义方法(presentNotification)通知用户,在文件加载完毕时。完整的方法如下:
– (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler {
self.backgroundSessionCompletionHandler = completionHandler;
//add notification
[self presentNotification];
}
6.本地通知
presentNotification方法用UILocalNotification类来创建本地通知。它创建一个声音,并使用在通知中使用徽标系统–badge system,完整方法如下:
-(void)presentNotification{
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = @”Download Complete!”;
localNotification.alertAction = @”Background Transfer Download!”;
//On sound
localNotification.soundName = UILocalNotificationDefaultSoundName;
//increase the badge number of application plus 1
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
顺便提下,为了重新计算app icon左上角的counter,你必须在AppDelegate中把下边一行代码添加至 (void)applicationDidBecomeActive:(UIApplication *)application方法。
application.applicationIconBadgeNumber = 0;

打赏

转载请注明:苏demo的别样人生 » iOS 7 SDK:后台传输服务

   如果本篇文章对您有帮助,欢迎向博主进行赞助,赞助时请写上您的用户名。
支付宝直接捐助帐号oracle_lee@qq.com 感谢支持!
喜欢 (0)or分享 (0)
发表我的评论
取消评论
表情