diff --git a/Kiwix-iOS/AppDelegate.swift b/Kiwix-iOS/AppDelegate.swift index 0f5200ae..62f2d173 100644 --- a/Kiwix-iOS/AppDelegate.swift +++ b/Kiwix-iOS/AppDelegate.swift @@ -31,7 +31,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Register notification let settings = UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: nil) // Here are the notification permission the app wants - application.registerUserNotificationSettings(settings) +// application.registerUserNotificationSettings(settings) // Set background refresh interval application.setMinimumBackgroundFetchInterval(86400) diff --git a/Kiwix-iOS/Controller/Library/DownloadTasksController.swift b/Kiwix-iOS/Controller/Library/DownloadTasksController.swift index a0bb1808..9c928a4a 100644 --- a/Kiwix-iOS/Controller/Library/DownloadTasksController.swift +++ b/Kiwix-iOS/Controller/Library/DownloadTasksController.swift @@ -177,37 +177,38 @@ class DownloadTasksController: UITableViewController, NSFetchedResultsController override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {} override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { - guard let downloadTask = self.fetchedResultController.objectAtIndexPath(indexPath) as? DownloadTask else {return nil} + guard let downloadTask = self.fetchedResultController.objectAtIndexPath(indexPath) as? DownloadTask, + let bookID = downloadTask.book?.id else {return nil} var actions = [UITableViewRowAction]() switch downloadTask.state { case .Downloading: let pause = UITableViewRowAction(style: .Normal, title: "Pause") { (action, indexPath) in - guard let bookID = downloadTask.book?.id else {return} let operation = PauseBookDwonloadOperation(bookID: bookID) GlobalQueue.shared.addOperation(operation) tableView.setEditing(false, animated: true) } actions.insert(pause, atIndex: 0) case .Paused: - if let book = downloadTask.book, - let resumeData = Preference.resumeData[book.id] { - let resume = UITableViewRowAction(style: .Normal, title: "Resume") { (action, indexPath) in - let task = Network.shared.session.downloadTaskWithResumeData(resumeData) - let operation = DownloadBookOperation(downloadTask: task) - Network.shared.queue.addOperation(operation) - tableView.setEditing(false, animated: true) - } - actions.insert(resume, atIndex: 0) - } else { - let restart = UITableViewRowAction(style: .Normal, title: "Restart") { (action, indexPath) in - guard let bookID = downloadTask.book?.id, - let operation = DownloadBookOperation(bookID: bookID) else {return} - Network.shared.queue.addOperation(operation) - tableView.setEditing(false, animated: true) - } - actions.insert(restart, atIndex: 0) + let resume = UITableViewRowAction(style: .Normal, title: "Resume") { (action, indexPath) in + let operation = ResumeBookDwonloadOperation(bookID: bookID) + GlobalQueue.shared.addOperation(operation) + tableView.setEditing(false, animated: true) } + actions.insert(resume, atIndex: 0) +// +// if let book = downloadTask.book, +// let resumeData = Preference.resumeData[book.id] { +// +// } else { +// let restart = UITableViewRowAction(style: .Normal, title: "Restart") { (action, indexPath) in +// guard let bookID = downloadTask.book?.id, +// let operation = DownloadBookOperation(bookID: bookID) else {return} +// Network.shared.queue.addOperation(operation) +// tableView.setEditing(false, animated: true) +// } +// actions.insert(restart, atIndex: 0) +// } default: break } diff --git a/Kiwix-iOS/Info.plist b/Kiwix-iOS/Info.plist index 6c3466ac..29e4eb6e 100644 --- a/Kiwix-iOS/Info.plist +++ b/Kiwix-iOS/Info.plist @@ -49,7 +49,7 @@ CFBundleVersion - 1.8.669 + 1.8.725 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/Kiwix-iOSWidgets/Bookmarks/Info.plist b/Kiwix-iOSWidgets/Bookmarks/Info.plist index 878a7252..a31e582b 100644 --- a/Kiwix-iOSWidgets/Bookmarks/Info.plist +++ b/Kiwix-iOSWidgets/Bookmarks/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 1.8.672 + 1.8.728 NSExtension NSExtensionMainStoryboard diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index ff96f3c7..c314ca7b 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 97219DBD1D383A00009FDFF1 /* BookmarkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97219DBC1D383A00009FDFF1 /* BookmarkController.swift */; }; 9722122B1D3FCCE200C0DCF2 /* MainControllerShowHide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9722122A1D3FCCE200C0DCF2 /* MainControllerShowHide.swift */; }; 972659191D8AE4B400D1DFFB /* Alerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972659181D8AE4B400D1DFFB /* Alerts.swift */; }; + 9726591B1D8DB91200D1DFFB /* DownloadProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9726591A1D8DB91200D1DFFB /* DownloadProgress.swift */; }; 9734E54E1D289D060061C39B /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9734E54D1D289D060061C39B /* Welcome.storyboard */; }; 973BCCEC1CEB3FA400F10B44 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BCCEB1CEB3FA400F10B44 /* AppDelegate.swift */; }; 973BCCF31CEB3FA400F10B44 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 973BCCF21CEB3FA400F10B44 /* Assets.xcassets */; }; @@ -108,7 +109,6 @@ 97A1FD431D6F728200A80EE2 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD3E1D6F728200A80EE2 /* FileManager.swift */; }; 97A1FD441D6F728200A80EE2 /* Preference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD401D6F728200A80EE2 /* Preference.swift */; }; 97A1FD451D6F728200A80EE2 /* StringTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD411D6F728200A80EE2 /* StringTools.swift */; }; - 97A461B71D74819000AC3DED /* DownloadProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A461B61D74819000AC3DED /* DownloadProgress.swift */; }; 97A7017F1D2C59CA00AAE2D8 /* GetStartedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A7017E1D2C59CA00AAE2D8 /* GetStartedController.swift */; }; 97A8AD841D6C951A00584ED1 /* LocalBooksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A8AD831D6C951A00584ED1 /* LocalBooksController.swift */; }; 97A8AD871D6CF38000584ED1 /* EmptyTableConfigExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A8AD861D6CF38000584ED1 /* EmptyTableConfigExtension.swift */; }; @@ -256,6 +256,7 @@ 97219DBC1D383A00009FDFF1 /* BookmarkController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BookmarkController.swift; path = "Kiwix-iOS/Controller/Bookmark/BookmarkController.swift"; sourceTree = SOURCE_ROOT; }; 9722122A1D3FCCE200C0DCF2 /* MainControllerShowHide.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MainControllerShowHide.swift; path = "Kiwix-iOS/Controller/Main/MainControllerShowHide.swift"; sourceTree = SOURCE_ROOT; }; 972659181D8AE4B400D1DFFB /* Alerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Alerts.swift; sourceTree = ""; }; + 9726591A1D8DB91200D1DFFB /* DownloadProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadProgress.swift; sourceTree = ""; }; 9734E54D1D289D060061C39B /* Welcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Welcome.storyboard; path = "Kiwix-iOS/Storyboard/Welcome.storyboard"; sourceTree = SOURCE_ROOT; }; 973BCCE91CEB3FA400F10B44 /* Kiwix.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Kiwix.app; sourceTree = BUILT_PRODUCTS_DIR; }; 973BCCEB1CEB3FA400F10B44 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = "Kiwix-OSX/AppDelegate.swift"; sourceTree = SOURCE_ROOT; }; @@ -340,7 +341,6 @@ 97A2AB881C1B80FF00052E74 /* Kiwix.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Kiwix.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97A2AB9F1C1B80FF00052E74 /* Kiwix-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Kiwix-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 97A2ABAA1C1B810000052E74 /* Kiwix-iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Kiwix-iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 97A461B61D74819000AC3DED /* DownloadProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadProgress.swift; sourceTree = ""; }; 97A7017E1D2C59CA00AAE2D8 /* GetStartedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GetStartedController.swift; path = "Kiwix-iOS/Controller/Welcome/GetStartedController.swift"; sourceTree = SOURCE_ROOT; }; 97A8AD831D6C951A00584ED1 /* LocalBooksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalBooksController.swift; sourceTree = ""; }; 97A8AD861D6CF38000584ED1 /* EmptyTableConfigExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyTableConfigExtension.swift; sourceTree = ""; }; @@ -852,7 +852,6 @@ 97A1FD3C1D6F728200A80EE2 /* Tools */ = { isa = PBXGroup; children = ( - 97A461B61D74819000AC3DED /* DownloadProgress.swift */, 97A1FD3D1D6F728200A80EE2 /* Extensions.swift */, 97A1FD3E1D6F728200A80EE2 /* FileManager.swift */, 97C601DD1D7F342100362D4F /* HTMLHeading.swift */, @@ -967,6 +966,7 @@ isa = PBXGroup; children = ( 97DF259F1D6F996B001648A3 /* Network.swift */, + 9726591A1D8DB91200D1DFFB /* DownloadProgress.swift */, 97A1FD1B1D6F71D800A80EE2 /* URLResponseCache.swift */, ); path = Network; @@ -1520,6 +1520,7 @@ 97A8AD871D6CF38000584ED1 /* EmptyTableConfigExtension.swift in Sources */, 971A102E1D022AD5007FC62C /* TableViewCells.swift in Sources */, 97DF259D1D6F9053001648A3 /* URLSessionDownloadTaskOperation.swift in Sources */, + 9726591B1D8DB91200D1DFFB /* DownloadProgress.swift in Sources */, 97E60A021D10423A00EBCB9D /* ShadowViews.swift in Sources */, 970E68BA1D3809A3001E8514 /* MainController.swift in Sources */, 970C65501D398D5A007032F8 /* BookmarkControllerAnimator.swift in Sources */, @@ -1574,7 +1575,6 @@ 971A103B1D022C2C007FC62C /* AdjustLayoutTBVC.swift in Sources */, 97219DBD1D383A00009FDFF1 /* BookmarkController.swift in Sources */, 97D6812E1D6F70DE00E5FA99 /* Kiwix.xcdatamodeld in Sources */, - 97A461B71D74819000AC3DED /* DownloadProgress.swift in Sources */, 971A10341D022AEC007FC62C /* BookmarkTBVC.swift in Sources */, 97A1FD1D1D6F71D800A80EE2 /* URLResponseCache.swift in Sources */, 9764F5971D8339D500E0B1C4 /* JSInjection.swift in Sources */, diff --git a/Kiwix/Tools/DownloadProgress.swift b/Kiwix/Network/DownloadProgress.swift similarity index 91% rename from Kiwix/Tools/DownloadProgress.swift rename to Kiwix/Network/DownloadProgress.swift index b2244dfd..9b0f6e5e 100644 --- a/Kiwix/Tools/DownloadProgress.swift +++ b/Kiwix/Network/DownloadProgress.swift @@ -14,19 +14,13 @@ class DownloadProgress: NSProgress { private let timePointMinCount: Int = 20 private let timePointMaxCount: Int = 200 - init(completedUnitCount: Int64 = 0, totalUnitCount: Int64) { + init(completedUnitCount: Int64, totalUnitCount: Int64) { super.init(parent: nil, userInfo: [NSProgressFileOperationKindKey: NSProgressFileOperationKindDownloading]) self.kind = NSProgressKindFile self.totalUnitCount = totalUnitCount self.completedUnitCount = completedUnitCount } - override var completedUnitCount: Int64 { - didSet { - add(completedUnitCount) - } - } - // MARK: - Descriptions var fractionCompletedDescription: String? { @@ -60,7 +54,8 @@ class DownloadProgress: NSProgress { setUserInfoObject(NSNumber(double: remainingSeconds), forKey: NSProgressEstimatedTimeRemainingKey) } - private func add(completedUnitCount: Int64) { + func addObservation(totalBytesWritten: Int64) { + completedUnitCount = totalBytesWritten let timeStamp = NSDate().timeIntervalSince1970 if let lastPoint = timePoints.last { guard timeStamp - lastPoint.timeStamp > 0.2 else {return} diff --git a/Kiwix/Network/Network.swift b/Kiwix/Network/Network.swift index 990eeb78..0c1e0386 100644 --- a/Kiwix/Network/Network.swift +++ b/Kiwix/Network/Network.swift @@ -74,7 +74,7 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSe func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { guard let bookID = downloadTask.taskDescription, let operation = operations[bookID] else {return} - operation.progress.completedUnitCount = totalBytesWritten + operation.progress.addObservation(totalBytesWritten) context.performBlock { guard let downloadTask = Book.fetch(bookID, context: self.context)?.downloadTask where downloadTask.state == .Queued else {return} diff --git a/Kiwix/Operations/BookOperation.swift b/Kiwix/Operations/BookOperation.swift index ec1d0e19..1af00037 100644 --- a/Kiwix/Operations/BookOperation.swift +++ b/Kiwix/Operations/BookOperation.swift @@ -11,7 +11,6 @@ import CoreData import Operations class DownloadBookOperation: URLSessionDownloadTaskOperation { - let bookID: String? let progress: DownloadProgress @@ -20,6 +19,33 @@ class DownloadBookOperation: URLSessionDownloadTaskOperation { bookID = downloadTask.taskDescription super.init(downloadTask: downloadTask) name = downloadTask.taskDescription + + // Update Coredata + let context = NSManagedObjectContext.mainQueueContext + context.performBlockAndWait { + guard let bookID = self.bookID, + let book = Book.fetch(bookID, context: context), + let downloadTask = DownloadTask.addOrUpdate(book, context: context) else {return} + book.isLocal = nil + downloadTask.state = .Queued + + // Overwrite progress + self.progress.completedUnitCount = book.downloadTask?.totalBytesWritten ?? 0 + self.progress.totalUnitCount = book.fileSize + } + } + + convenience init?(bookID: String, resumeData: NSData) { + if #available(iOS 10.0, *) { + guard let data = DownloadBookOperation.correctFuckingResumeData(resumeData) else {return nil} + let downloadTask = Network.shared.session.downloadTaskWithResumeData(data) + downloadTask.taskDescription = bookID + self.init(downloadTask: downloadTask) + } else { + let downloadTask = Network.shared.session.downloadTaskWithResumeData(resumeData) + downloadTask.taskDescription = bookID + self.init(downloadTask: downloadTask) + } } convenience init?(bookID: String) { @@ -30,13 +56,6 @@ class DownloadBookOperation: URLSessionDownloadTaskOperation { let task = Network.shared.session.downloadTaskWithURL(url) task.taskDescription = bookID self.init(downloadTask: task) - - let downloadTask = DownloadTask.addOrUpdate(book, context: context) - downloadTask?.state = .Queued - book.isLocal = nil - - progress.completedUnitCount = book.downloadTask?.totalBytesWritten ?? 0 - progress.totalUnitCount = book.fileSize } override func operationWillCancel(errors: [ErrorType]) { @@ -45,76 +64,26 @@ class DownloadBookOperation: URLSessionDownloadTaskOperation { override func operationDidCancel() { print("Download Task did cancel") - guard let bookID = bookID else {return} + + // Update CoreData let context = NSManagedObjectContext.mainQueueContext context.performBlockAndWait { - let book = Book.fetch(bookID, context: context) - if !self.produceResumeData {book?.isLocal = false} + 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} + guard let downloadTask = book.downloadTask else {return} if self.produceResumeData { downloadTask.state = .Paused } else { context.deleteObject(downloadTask) } - } } -} - -class RemoveBookOperation: Operation { - let bookID: String + // MARK: - Helper - init(bookID: String) { - self.bookID = bookID - super.init() - } - - override func execute() { - let context = NSManagedObjectContext.mainQueueContext - context.performBlockAndWait { - guard let zimFileURL = ZimMultiReader.shared.readers[self.bookID]?.fileURL else {return} - _ = try? NSFileManager.defaultManager().removeItemAtURL(zimFileURL) - - // Core data is updated by scan book operation - // Article removal is handled by cascade relationship - - guard let idxFolderURL = ZimMultiReader.shared.readers[self.bookID]?.idxFolderURL else {return} - _ = try? NSFileManager.defaultManager().removeItemAtURL(idxFolderURL) - } - finish() - } -} - -class PauseBookDwonloadOperation: Operation { - - let bookID: String - - init(bookID: String) { - self.bookID = bookID - super.init() - } - - override func execute() { - Network.shared.operations[bookID]?.cancel(produceResumeData: true) - finish() - } -} - -class ResumeBookDwonloadOperation: Operation { - let bookID: String - - init(bookID: String) { - self.bookID = bookID - super.init() - } - - override func execute() { - - } - - private func correctFuckingResumeData(data: NSData?) -> NSData? { + private class func correctFuckingResumeData(data: NSData?) -> NSData? { let kResumeCurrentRequest = "NSURLSessionResumeCurrentRequest" let kResumeOriginalRequest = "NSURLSessionResumeOriginalRequest" @@ -129,7 +98,7 @@ class ResumeBookDwonloadOperation: Operation { return result } - private func correctFuckingRequestData(data: NSData?) -> NSData? { + private class func correctFuckingRequestData(data: NSData?) -> NSData? { guard let data = data else { return nil } @@ -174,3 +143,58 @@ class ResumeBookDwonloadOperation: Operation { return result } } + +class RemoveBookOperation: Operation { + let bookID: String + + init(bookID: String) { + self.bookID = bookID + super.init() + } + + override func execute() { + let context = NSManagedObjectContext.mainQueueContext + context.performBlockAndWait { + guard let zimFileURL = ZimMultiReader.shared.readers[self.bookID]?.fileURL else {return} + _ = try? NSFileManager.defaultManager().removeItemAtURL(zimFileURL) + + // Core data is updated by scan book operation + // Article removal is handled by cascade relationship + + guard let idxFolderURL = ZimMultiReader.shared.readers[self.bookID]?.idxFolderURL else {return} + _ = try? NSFileManager.defaultManager().removeItemAtURL(idxFolderURL) + } + finish() + } +} + +class PauseBookDwonloadOperation: Operation { + let bookID: String + + init(bookID: String) { + self.bookID = bookID + super.init() + } + + override func execute() { + Network.shared.operations[bookID]?.cancel(produceResumeData: true) + finish() + } +} + +class ResumeBookDwonloadOperation: Operation { + let bookID: String + + init(bookID: String) { + self.bookID = bookID + super.init() + name = "Resume Book Dwonload Operation, bookID = \(bookID)" + } + + override func execute() { + guard let data: NSData = Preference.resumeData[bookID], + let operation = DownloadBookOperation(bookID: bookID, resumeData: data) else {return} + Network.shared.queue.addOperation(operation) + finish() + } +}