Pause / Resume

This commit is contained in:
Chris Li 2016-09-19 14:02:47 -04:00
parent f22415e3c9
commit 043ad93fcd
6 changed files with 64 additions and 41 deletions

View File

@ -18,7 +18,7 @@ class DownloadTasksController: UITableViewController, NSFetchedResultsController
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
tabBarItem.title = LocalizedStrings.LibraryTabTitle.download tabBarItem.title = LocalizedStrings.download
tabBarItem.image = UIImage(named: "Download") tabBarItem.image = UIImage(named: "Download")
tabBarItem.selectedImage = UIImage(named: "DownloadFilled") tabBarItem.selectedImage = UIImage(named: "DownloadFilled")
} }
@ -188,29 +188,29 @@ class DownloadTasksController: UITableViewController, NSFetchedResultsController
case .Downloading: case .Downloading:
let pause = UITableViewRowAction(style: .Normal, title: "Pause") { (action, indexPath) in let pause = UITableViewRowAction(style: .Normal, title: "Pause") { (action, indexPath) in
let operation = PauseBookDwonloadOperation(bookID: bookID) let operation = PauseBookDwonloadOperation(bookID: bookID)
GlobalQueue.shared.addOperation(operation) Network.shared.queue.addOperation(operation)
tableView.setEditing(false, animated: true) tableView.setEditing(false, animated: true)
} }
actions.insert(pause, atIndex: 0) actions.insert(pause, atIndex: 0)
case .Paused: case .Paused:
let resume = UITableViewRowAction(style: .Normal, title: "Resume") { (action, indexPath) in let resume = UITableViewRowAction(style: .Normal, title: "Resume") { (action, indexPath) in
let operation = ResumeBookDwonloadOperation(bookID: bookID) let operation = ResumeBookDwonloadOperation(bookID: bookID)
GlobalQueue.shared.addOperation(operation) Network.shared.queue.addOperation(operation)
tableView.setEditing(false, animated: true) tableView.setEditing(false, animated: true)
} }
actions.insert(resume, atIndex: 0) actions.insert(resume, atIndex: 0)
case .Error: case .Error:
let retry = UITableViewRowAction(style: .Normal, title: "Restart") { (action, indexPath) in let restart = UITableViewRowAction(style: .Normal, title: "Restart") { (action, indexPath) in
let operation = ResumeBookDwonloadOperation(bookID: bookID) let operation = ResumeBookDwonloadOperation(bookID: bookID)
GlobalQueue.shared.addOperation(operation) Network.shared.queue.addOperation(operation)
tableView.setEditing(false, animated: true) tableView.setEditing(false, animated: true)
} }
actions.insert(retry, atIndex: 0) actions.insert(restart, atIndex: 0)
default: default:
break break
} }
let cancel = UITableViewRowAction(style: .Destructive, title: LocalizedStrings.Common.cancel) { (action, indexPath) -> Void in let cancel = UITableViewRowAction(style: .Destructive, title: LocalizedStrings.cancel) { (action, indexPath) -> Void in
if let bookID = downloadTask.book?.id { if let bookID = downloadTask.book?.id {
if let operation = Network.shared.operations[bookID] { if let operation = Network.shared.operations[bookID] {
// When download is ongoing // When download is ongoing
@ -289,4 +289,12 @@ class DownloadTasksController: UITableViewController, NSFetchedResultsController
tableView.endUpdates() tableView.endUpdates()
//refreshTabBarBadgeCount() //refreshTabBarBadgeCount()
} }
// MARK: - LocalizedStrings
class LocalizedStrings {
static let download = NSLocalizedString("Download", comment: "Library, download tab")
}
} }

View File

@ -49,7 +49,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.8.777</string> <string>1.8.827</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>

View File

@ -21,7 +21,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.8.780</string> <string>1.8.830</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
<key>NSExtensionMainStoryboard</key> <key>NSExtensionMainStoryboard</key>

View File

@ -50,21 +50,18 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSe
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if let error = error {print(error.localizedDescription)} if let error = error {print(error.localizedDescription)}
guard let error = error, let bookID = task.taskDescription else {return}
self.context.performBlockAndWait { let context = NSManagedObjectContext.mainQueueContext
guard let book = Book.fetch(bookID, context: self.context), guard let error = error,
let downloadTask = book.downloadTask else {return} let bookID = task.taskDescription,
if let resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData] as? NSData { let downloadTask = Book.fetch(bookID, context: context)?.downloadTask else {return}
// If download task doesnt exist, it must mean download is cancelled by user
// DownloadTask object will have been deleted when user tap Cancel button / table row action if let resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData] as? NSData {
downloadTask.totalBytesWritten = task.countOfBytesReceived Preference.resumeData[bookID] = resumeData
downloadTask.state = .Paused downloadTask.state = .Paused
downloadTask.totalBytesWritten = task.countOfBytesReceived
// Save resume data to disk } else {
Preference.resumeData[bookID] = resumeData downloadTask.state = .Error
} else {
downloadTask.state = .Error
}
} }
} }

View File

@ -64,20 +64,32 @@ class DownloadBookOperation: URLSessionDownloadTaskOperation {
} }
override func operationDidCancel() { override func operationDidCancel() {
// Update CoreData // Not Reachable
let context = NSManagedObjectContext.mainQueueContext if let error = errors.first as? Operations.ReachabilityCondition.Error where error == .NotReachable {
context.performBlockAndWait { return
guard let bookID = self.bookID,
let book = Book.fetch(bookID, context: context) else {return}
if !self.produceResumeData {book.isLocal = false}
guard let downloadTask = book.downloadTask else {return}
if self.produceResumeData {
downloadTask.state = .Paused
} else {
context.deleteObject(downloadTask)
}
} }
// Update Core Data
if produceResumeData {
let context = NSManagedObjectContext.mainQueueContext
context.performBlockAndWait({
guard let bookID = self.bookID,
let book = Book.fetch(bookID, context: context) else {return}
book.isLocal = nil
})
} else {
let context = NSManagedObjectContext.mainQueueContext
context.performBlockAndWait({
guard let bookID = self.bookID,
let book = Book.fetch(bookID, context: context) else {return}
book.isLocal = false
guard let downloadTask = book.downloadTask else {return}
context.deleteObject(downloadTask)
})
}
// URLSessionDelegate save resume data and update downloadTask
} }
// MARK: - Helper // MARK: - Helper
@ -192,7 +204,14 @@ class ResumeBookDwonloadOperation: Operation {
override func execute() { override func execute() {
guard let data: NSData = Preference.resumeData[bookID], guard let data: NSData = Preference.resumeData[bookID],
let operation = DownloadBookOperation(bookID: bookID, resumeData: data) else {return} let operation = DownloadBookOperation(bookID: bookID, resumeData: data) else {
if let operation = DownloadBookOperation(bookID: bookID) {
produceOperation(operation)
}
finish()
return
}
Network.shared.queue.addOperation(operation) Network.shared.queue.addOperation(operation)
finish() finish()
} }

View File

@ -27,12 +27,11 @@ class URLSessionDownloadTaskOperation: Operation {
addObserver(NetworkObserver()) addObserver(NetworkObserver())
addObserver(DidCancelObserver { _ in addObserver(DidCancelObserver { _ in
if self.produceResumeData { if self.produceResumeData {
downloadTask.cancelByProducingResumeData({ (data) in }) downloadTask.cancelByProducingResumeData({ _ in })
} else { } else {
downloadTask.cancel() downloadTask.cancel()
} }
} })
)
} }
func cancel(produceResumeData produceResumeData: Bool) { func cancel(produceResumeData produceResumeData: Bool) {