This commit is contained in:
Chris Li 2016-12-26 16:16:56 -05:00
parent 3bbb2b1f23
commit f3690739e9
9 changed files with 235 additions and 25 deletions

74
.gitignore vendored
View File

@ -1,9 +1,67 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xcuserstate
Pods
Kiwix/libkiwix/C&C++
Kiwix/libkiwix/include
Kiwix/libkiwix/shared
Kiwix/libkiwix/static
*.a
Kiwix/libkiwix/iOS
Kiwix/libkiwix/macOS
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
Kiwix/libkiwix/

View File

@ -274,8 +274,10 @@ class CloudBooksController: CoreDataTableBaseController, UITableViewDelegate, UI
switch book.spaceState {
case .enough:
let action = UITableViewRowAction(style: UITableViewRowActionStyle.normal, title: LocalizedStrings.download, handler: { _ in
// guard let download = DownloadBookOperation(bookID: book.id) else {return}
// Network.shared.queue.addOperation(download)
guard let url = book.url else {return}
let download = BookDownloadProcedure(session: DownloadManager.shared.session, bookID: book.id, url: url)
DownloadManager.shared.queue.add(operation: download)
self.tableView.setEditing(false, animated: true)
})
action.backgroundColor = UIColor.defaultTint
return [action]

View File

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

View File

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

View File

@ -63,6 +63,8 @@
975227CD1D0227E8001D1DDE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227CA1D0227E8001D1DDE /* Main.storyboard */; };
975227CE1D0227E8001D1DDE /* Setting.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227CB1D0227E8001D1DDE /* Setting.storyboard */; };
975227D01D022814001D1DDE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227CF1D022814001D1DDE /* LaunchScreen.storyboard */; };
9757C74A1E10660B008A9469 /* DownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9757C7491E10660B008A9469 /* DownloadManager.swift */; };
9757C74C1E106958008A9469 /* BackgroundDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9757C74B1E106958008A9469 /* BackgroundDownload.swift */; };
975B90FE1CEB909100D13906 /* iOSExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975B90FD1CEB909100D13906 /* iOSExtensions.swift */; };
9764CBD11D806AD800072D6A /* RefreshLibControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9764CBD01D806AD800072D6A /* RefreshLibControl.swift */; };
9764F5931D830EF200E0B1C4 /* liblzma.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9764F5921D830EF200E0B1C4 /* liblzma.tbd */; };
@ -217,6 +219,8 @@
975227CA1D0227E8001D1DDE /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = "Kiwix-iOS/Storyboard/Main.storyboard"; sourceTree = SOURCE_ROOT; };
975227CB1D0227E8001D1DDE /* Setting.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Setting.storyboard; path = "Kiwix-iOS/Storyboard/Setting.storyboard"; sourceTree = SOURCE_ROOT; };
975227CF1D022814001D1DDE /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = "Kiwix-iOS/Storyboard/LaunchScreen.storyboard"; sourceTree = SOURCE_ROOT; };
9757C7491E10660B008A9469 /* DownloadManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadManager.swift; sourceTree = "<group>"; };
9757C74B1E106958008A9469 /* BackgroundDownload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundDownload.swift; sourceTree = "<group>"; };
975B90FD1CEB909100D13906 /* iOSExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = iOSExtensions.swift; path = "Kiwix-iOS/iOSExtensions.swift"; sourceTree = SOURCE_ROOT; };
9763275D1D64FE0F0034F120 /* BookDetailController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookDetailController.swift; sourceTree = "<group>"; };
9764CBD01D806AD800072D6A /* RefreshLibControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshLibControl.swift; sourceTree = "<group>"; };
@ -751,6 +755,7 @@
isa = PBXGroup;
children = (
97DF259F1D6F996B001648A3 /* Network.swift */,
9757C7491E10660B008A9469 /* DownloadManager.swift */,
9726591A1D8DB91200D1DFFB /* DownloadProgress.swift */,
);
path = Network;
@ -773,6 +778,7 @@
children = (
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */,
9764CBD21D8083AA00072D6A /* ArticleOperation.swift */,
9757C74B1E106958008A9469 /* BackgroundDownload.swift */,
970A2A211DD562CB0078BB7C /* BookOperations.swift */,
973A5C981DEBC54800C7804C /* CloudKit.swift */,
973208281DD223DB00EDD3DC /* RefreshLibrary.swift */,
@ -1111,6 +1117,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9757C74C1E106958008A9469 /* BackgroundDownload.swift in Sources */,
970E7F771D9DBEA900741290 /* SettingController.swift in Sources */,
973A5C951DEA6DD000C7804C /* URLResponseCache.swift in Sources */,
973207A51DD1984700EDD3DC /* SearchBooksController.swift in Sources */,
@ -1137,6 +1144,7 @@
97A1FD3A1D6F724E00A80EE2 /* reader.cpp in Sources */,
973207A31DD1983D00EDD3DC /* LanguageFilterController.swift in Sources */,
97D681371D6F711A00E5FA99 /* Article.swift in Sources */,
9757C74A1E10660B008A9469 /* DownloadManager.swift in Sources */,
972F81571DDBFC79008D7289 /* Search.swift in Sources */,
970E7F831DA0305000741290 /* WelcomeController.swift in Sources */,
97A1FD3B1D6F724E00A80EE2 /* stringTools.cpp in Sources */,

View File

@ -7,7 +7,7 @@
<key>Bookmarks.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>1</integer>
</dict>
<key>Kiwix-iOS.xcscheme</key>
<dict>
@ -20,57 +20,57 @@
<key>9722121A1D3ECCFE00C0DCF2</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>973BCCE81CEB3FA400F10B44</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>973BCCFB1CEB3FA400F10B44</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>973BCD061CEB3FA500F10B44</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>9779C2FD1D4574280064CC8E</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>9779C3121D4575AD0064CC8E</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>97A2AB871C1B80FF00052E74</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>97A2AB9E1C1B80FF00052E74</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>97A2ABA91C1B810000052E74</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>97CF3EEA1D428F9600AE82FE</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
<key>97E609EE1D103DED00EBCB9D</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
</dict>
</dict>

View File

@ -12,7 +12,7 @@ import CoreData
class DownloadTask: NSManagedObject {
class func fetch(_ book: Book, context: NSManagedObjectContext) -> DownloadTask? {
class func fetch(book: Book, context: NSManagedObjectContext) -> DownloadTask? {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "DownloadTask")
fetchRequest.predicate = NSPredicate(format: "book = %@", book)
let downloadTask = DownloadTask.fetch(fetchRequest, type: DownloadTask.self, context: context)?.first ?? insert(DownloadTask.self, context: context)

View File

@ -0,0 +1,62 @@
//
// DownloadManager.swift
// Kiwix
//
// Created by Chris Li on 12/25/16.
// Copyright © 2016 Chris Li. All rights reserved.
//
import ProcedureKit
class DownloadManager: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {
static let shared = DownloadManager()
let queue = ProcedureQueue()
private override init() {
super.init()
}
private(set) lazy var session: Foundation.URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "org.kiwix.www")
configuration.allowsCellularAccess = false
configuration.isDiscretionary = false
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}()
// MARK: - URLSessionDelegate
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
}
// MARK: - URLSessionTaskDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let operation = queue.operations.flatMap({$0 as? BookDownloadProcedure})
.filter({$0.task.taskDescription == task.taskDescription})
.first else {
if let bookID = task.taskDescription,
let resumeData = (error as? NSError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
let operation = BookDownloadProcedure(session: session, bookID: bookID, resumeData: resumeData)
DownloadManager.shared.queue.add(operation: operation)
}
return
}
if let error = error {
operation.finish(withError: error)
} else {
operation.finish()
}
}
// MARK: - URLSessionDownloadDelegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
}
}

View File

@ -0,0 +1,80 @@
//
// BookDownload.swift
// Kiwix
//
// Created by Chris Li on 12/25/16.
// Copyright © 2016 Chris Li. All rights reserved.
//
import ProcedureKit
class BackgroundDownloadProcedure: Procedure {
let task: URLSessionDownloadTask
let resumeDataProcessing: (Data?) -> Void
private let stateLock = NSLock()
private var produceResumeData = false
init(task: URLSessionDownloadTask, resumeData: @escaping (Data?) -> Void) {
self.task = task
self.resumeDataProcessing = resumeData
super.init()
add(observer: NetworkObserver())
addDidCancelBlockObserver { procedure, errors in
procedure.stateLock.withCriticalScope {
if procedure.produceResumeData {
procedure.task.cancel(byProducingResumeData: self.resumeDataProcessing)
} else {
procedure.task.cancel()
}
}
}
}
override func execute() {
stateLock.withCriticalScope {
guard !isCancelled, task.state == .suspended else { return }
task.resume()
}
}
func pause() {
produceResumeData = true
cancel()
}
}
class BookDownloadProcedure: BackgroundDownloadProcedure {
init(task: URLSessionDownloadTask) {
super.init(task: task) { data in
print("cancelled, resume data length = \(data?.count)")
}
addDidFinishBlockObserver { (procedure, errors) in
print("Download has finished")
}
}
convenience init(session: URLSession, bookID: String, url: URL) {
let task = session.downloadTask(with: url)
task.taskDescription = bookID
self.init(task: task)
}
convenience init(session: URLSession, bookID: String, resumeData: Data) {
let task = session.downloadTask(withResumeData: resumeData)
task.taskDescription = bookID
self.init(task: task)
}
override func execute() {
let context = AppDelegate.persistentContainer.viewContext
context.performAndWait {
guard let bookID = self.task.taskDescription,
let book = Book.fetch(bookID, context: context),
let downloadTask = DownloadTask.fetch(book: book, context: context) else {return}
downloadTask.state = .queued
}
super.execute()
}
}