首先,我们来处理下载结束事件。之所以说是下载结束,而不是下载完成,是因为如果下载过程中发生了错误,那么整个下载过程也就结束了。接下来,我们就从处理下载错误开始。
处理下载结束事件
处理下载错误
继续在beginDownload里,添加下面的代码:
if let resUrl = self.downloadUrl.text {
Alamofire.download(.GET, resUrl, destination: dest)
.progress {
bytesRead,
totalBytesRead,
totalBytesExpectedToRead in
// This closure is NOT called on the
// main queue for performance reasons.
// To update your ui, dispatch to the main queue.
dispatch_async(dispatch_get_main_queue()) {
let progress =
Float(totalBytesRead) / Float(totalBytesExpectedToRead)
self.downloadProgress.progress = progress
}
}
.response { request, response, data, error in
if let error = error {
print("Download error: \(error.localizedDescription)")
}
else {
// Download successfully
}
}
}
这个response方法,我们在前面的视频中提到过,我们可以在progress后面串联起来处理下载结束的事件。当error不为nil时,我们就打印错误的详细信息。
按Command + R重新编译执行,重新下载我们之前的文件,在下载的过程中,我们关掉WiFi,就可以在控制台看到下载错误的提示了:
image在发生下载异常之后,我们能做的事情当然不仅仅是向控制台打印个消息这么简单,Alamofire还提供了恢复下载的功能,我们可以继续完善下载错误的处理机制。
Resume data
在response的参数里,有一个data,它是一个NSData optional对象,当有可恢复下载的内容时,它不为nil,我们可以利用它来恢复下载。
继续在.response里,添加下面的代码:
.response { [unowned self] (request, response, data, error) in
if let error = error {
print("Download error: \(error.localizedDescription)")
if let data = data {
print("Resume data exists")
self.displayNetworkAlert(
error.localizedDescription,
data: data,
destination: dest)
}
else {
print("Resume data is nil")
self.displayNetworkAlert(
error.localizedDescription)
}
}
else {
// Download successfully
}
}
这里,我们假设有一个叫做displayNetworkAlert的方法,它可以在发生网络错误的时候,给用户弹一个Alert。当存在可恢复数据时,允许用户恢复下载,否则,只是简单提示用户错误。接下来,我们来实现它。
我们把displayNetworkAlert的代码,添加到一个ViewController extension里:
extension ViewController {
private func displayNetworkAlert(errorMessage: String,
data: NSData? = nil,
destination: Request.DownloadFileDestination? = nil) {
let alert = UIAlertController(
title: "Network error",
message: errorMessage,
preferredStyle: .Alert)
if let data = data {
alert.addAction(UIAlertAction(title: "Resume",
style: UIAlertActionStyle.Default,
handler: { _ in
// Resume download here
print("Resume downloading...")
}))
alert.addAction(UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.Cancel,
handler: { _ in
// Reset the app
print("Cancel downloading...")
}))
}
else {
alert.addAction(UIAlertAction(title: "OK",
style: UIAlertActionStyle.Default,
handler: { _ in
// Cancel download here
print("Cancel downloading...")
}))
}
self.presentViewController(alert,
animated: true, completion: nil)
}
}
这段代码逻辑很简单,我们显示了用一个<key style="box-sizing: border-box;">UIAlertController</key>显示了错误信息,并且根据是否可以恢复下载给了用户不同的选项。然后,我们来实现不同的action,首先是恢复下载:
alert.addAction(UIAlertAction(title: "Resume",
style: UIAlertActionStyle.Default,
handler: { _ in
// Resume download here
print("Resume downloading...")
if let destination = destination {
defer {
Alamofire.download(resumeData: data,
destination: destination)
.progress {
bytesRead,
totalBytesRead,
totalBytesExpectedToRead in
dispatch_async(dispatch_get_main_queue()) {
let progress =
Float(totalBytesRead) / Float(totalBytesExpectedToRead)
self.downloadProgress.progress = progress
}
}
}
}
}))
简单起见,我们只列出了恢复下载的部分。Alamofire.download有一个重载的版本,它接受一个NSData和一个DownloadFileDestination,并且尝试重新恢复下载。然后我们用和之前类似的方式继续更新UIProgressView。
另外一个值的说明的地方是defer关键字。这是因为,当我们点击Resume按钮之后,当<key style="box-sizing: border-box;">UIAlertController</key>还在撤销自己时,如果我们执行到了progress中的代码,就会在控制台看到一些奇怪的错误提示。为了避免这个错误,我们要保证progress中的代码至少执行完一次之后,整个<key style="box-sizing: border-box;">UIAlertController</key>才彻底消失。因此,我们把恢复下载的代码用defer包围了起来。
这时,按Command + R重现编译执行,我们的App就可以恢复下载了。
image然后我们来处理用户点击Cancel以及不可恢复下载的情况,这种情况很简单,我们只要重置App的UI就可以了:
alert.addAction(UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.Cancel,
handler: { _ in
// Reset the app
print("Cancel downloading...")
self.downloadUrl.text = nil
self.downloadProgress.progress = 0
self.beginBtn.enabled = false
self.suspendOrResumeBtn.enabled = false
self.cancelBtn.enabled = false
}))
最后,我们添加下载成功完成的提示:
.response { request, response, data, error in
if let error = error {
// Omit for simplicity...
}
else {
// Download successfully
let alert = UIAlertController(title: "Success",
message: "Download finished successfully!",
preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK",
style: UIAlertActionStyle.Default,
handler: { _ in
print("Finish downloading...")
self.downloadUrl.text = nil
self.downloadProgress.progress = 0
self.beginBtn.enabled = false
self.suspendOrResumeBtn.enabled = false
self.cancelBtn.enabled = false
}))
self.presentViewController(alert,
animated: true, completion: nil)
}
}
image
这样,下载这部分的功能就开发完了。
暂停和恢复下载
暂停和恢复下载的功能实现起来非常简单,之前的Alamofire.download方法,会返回一个Alamofire.Request对象,我们调用它的suspend和resume方法就可以了。首先,在ViewController里,我们添加一个属性:
class ViewController: UIViewController {
var currStatus = DownloadStatus.NotStarted
var request: Alamofire.Request?
// Omit for simplicity...
}
然后,在发起下载请求的时候,保存这个Request:
// Button actions
@IBAction func beginDownload(sender: AnyObject) {
print("Begin downloading...")
/*
* Omit for simplicity...
*/
if let resUrl = self.downloadUrl.text {
self.request = Alamofire.download(.GET,
resUrl, destination: dest)
}
/*
* Omit for simplicity...
*/
}
最后,在suspendOrResumeDownload方法里,添加下面的代码:
// Button actions
@IBAction func suspendOrResumeDownload(sender: AnyObject) {
var btnTitle: String?
switch self.currStatus {
case .Downloading:
print("Suspend downloading...")
// TODO: Add suspending code here
self.currStatus = .Suspended
btnTitle = "Resume"
self.request!.suspend()
case .Suspended:
print("Resume downloading...")
// TODO: Add resuming code here
self.currStatus = .Downloading
btnTitle = "Suspend"
self.request!.resume()
case .NotStarted, .Cancelled:
break
}
self.suspendOrResumeBtn.setTitle(btnTitle,
forState: UIControlState.Normal)
}
很简单,我们只是调用了对应的Alamofire.Request对象的suspend()和resume()方法。然后,按Command + R重新执行,我们就可以看到对应的结果了。
image取消下载
终于到最后一个功能了。一个好消息就是,取消下载和暂停/恢复下载一样的简单,调用Request对象的cancel()方法就好。我们给Cancel按钮的事件处理函数添加下面的代码:
// Button actions
@IBAction func cancelDownload(sender: AnyObject) {
print("Cancel downloading...")
switch self.currStatus {
case .Downloading, .Suspended:
// TODO: Add cancel code here
self.request?.cancel()
self.currStatus = .Cancelled
self.cancelBtn.enabled = false
self.suspendOrResumeBtn.enabled = false
self.suspendOrResumeBtn.setTitle("Suspend",
forState: UIControlState.Normal)
default:
break
}
}
这里,有一点要说明的是,如果你为特定的request"注册"了错误处理函数(就像我们之前在.response()中那样),那么调用cancel()方法将触发对应的错误处理函数,像这样:
image此时,如果你点击Resume按钮,下载仍然是可以恢复进行的。
网友评论