mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-26 05:18:31 -04:00
commit
This commit is contained in:
parent
73a720194f
commit
3bbb2b1f23
@ -34,6 +34,8 @@ class Buttons: LPTBarButtonItemDelegate {
|
||||
delegate?.didTapForwardButton()
|
||||
case toc:
|
||||
delegate?.didTapTOCButton()
|
||||
case library:
|
||||
delegate?.didTapLibraryButton()
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.8.3669</string>
|
||||
<string>1.8.3683</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.8.3687</string>
|
||||
<string>1.8.3701</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
|
@ -214,7 +214,6 @@
|
||||
973DD4271D36E3E4009D45DB /* SettingDetailController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingDetailController.swift; path = "Kiwix-iOS/Controller/Setting/SettingDetailController.swift"; sourceTree = SOURCE_ROOT; };
|
||||
974C49621DA307FF00E276E1 /* injection.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = injection.js; sourceTree = "<group>"; };
|
||||
974C49671DA4266200E276E1 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
|
||||
974F33C51D99CAD700879D35 /* BookmarkOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkOperation.swift; sourceTree = "<group>"; };
|
||||
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; };
|
||||
@ -273,7 +272,6 @@
|
||||
97D4D64E1D874E6E00C1B065 /* SearchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchOperation.swift; sourceTree = "<group>"; };
|
||||
97D6811A1D6E2A7100E5FA99 /* DownloadTasksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadTasksController.swift; sourceTree = "<group>"; };
|
||||
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalQueue.swift; sourceTree = "<group>"; };
|
||||
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshLibraryOperation.swift; sourceTree = "<group>"; };
|
||||
97D6811E1D6F70AC00E5FA99 /* ScanLocalBook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanLocalBook.swift; sourceTree = "<group>"; };
|
||||
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateWidgetDataSourceOperation.swift; sourceTree = "<group>"; };
|
||||
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionDownloadTaskOperation.swift; sourceTree = "<group>"; };
|
||||
@ -291,7 +289,6 @@
|
||||
97D6813D1D6F712800E5FA99 /* DownloadTask+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "DownloadTask+CoreDataProperties.swift"; path = "Classes/DownloadTask+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
97D6813E1D6F712800E5FA99 /* Language+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Language+CoreDataProperties.swift"; path = "Classes/Language+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
97DB65D91D4576B600A2CC42 /* BookmarkWidgetCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkWidgetCell.swift; sourceTree = "<group>"; };
|
||||
97DF259B1D6F7612001648A3 /* BookOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookOperation.swift; sourceTree = "<group>"; };
|
||||
97DF259F1D6F996B001648A3 /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
|
||||
97E609F01D103DED00EBCB9D /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; };
|
||||
97E60A011D10423A00EBCB9D /* Others.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Others.swift; sourceTree = "<group>"; };
|
||||
@ -372,16 +369,6 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
970A2A201DD562B60078BB7C /* old */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
974F33C51D99CAD700879D35 /* BookmarkOperation.swift */,
|
||||
97DF259B1D6F7612001648A3 /* BookOperation.swift */,
|
||||
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */,
|
||||
);
|
||||
name = old;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
971187051CEB426E00B9909D /* libkiwix */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -784,7 +771,6 @@
|
||||
97E5712A1CA0525300FF4F1D /* Operation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
970A2A201DD562B60078BB7C /* old */,
|
||||
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */,
|
||||
9764CBD21D8083AA00072D6A /* ArticleOperation.swift */,
|
||||
970A2A211DD562CB0078BB7C /* BookOperations.swift */,
|
||||
|
@ -2,16 +2,4 @@
|
||||
<Bucket
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
scope = "0"
|
||||
stopOnStyle = "0">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
@ -1,218 +0,0 @@
|
||||
//
|
||||
// DownloadBookOperation.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 8/25/16.
|
||||
// Copyright © 2016 Chris Li. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
import ProcedureKit
|
||||
|
||||
class DownloadBookOperation: URLSessionDownloadTaskOperation {
|
||||
let bookID: String?
|
||||
let progress: DownloadProgress
|
||||
|
||||
override init(downloadTask: URLSessionDownloadTask) {
|
||||
progress = DownloadProgress(completedUnitCount: downloadTask.countOfBytesReceived, totalUnitCount: downloadTask.countOfBytesExpectedToReceive)
|
||||
bookID = downloadTask.taskDescription
|
||||
super.init(downloadTask: downloadTask)
|
||||
name = downloadTask.taskDescription
|
||||
|
||||
if UIApplication.shared.applicationState == .active,
|
||||
let url = downloadTask.originalRequest?.url {
|
||||
add(ReachabilityCondition(url: url, connectivity: .ViaWiFi))
|
||||
}
|
||||
|
||||
// Update Coredata
|
||||
let context = NSManagedObjectContext.mainQueueContext
|
||||
context.performAndWait {
|
||||
guard let bookID = self.bookID,
|
||||
let book = Book.fetch(bookID, context: context),
|
||||
let downloadTask = DownloadTask.fetch(book, context: context) else {return}
|
||||
book.state = .downloading
|
||||
downloadTask.state = .queued
|
||||
|
||||
// Overwrite progress
|
||||
self.progress.completedUnitCount = book.downloadTask?.totalBytesWritten ?? 0
|
||||
self.progress.totalUnitCount = book.fileSize
|
||||
}
|
||||
}
|
||||
|
||||
convenience init?(bookID: String, resumeData: Data) {
|
||||
if #available(iOS 10.0, *) {
|
||||
guard let data = DownloadBookOperation.correctFuckingResumeData(resumeData) else {return nil}
|
||||
let downloadTask = Network.shared.session.downloadTask(withResumeData: data)
|
||||
downloadTask.taskDescription = bookID
|
||||
self.init(downloadTask: downloadTask)
|
||||
} else {
|
||||
let downloadTask = Network.shared.session.downloadTask(withResumeData: resumeData)
|
||||
downloadTask.taskDescription = bookID
|
||||
self.init(downloadTask: downloadTask)
|
||||
}
|
||||
}
|
||||
|
||||
convenience init?(bookID: String) {
|
||||
let context = NSManagedObjectContext.mainQueueContext
|
||||
guard let book = Book.fetch(bookID, context: context),
|
||||
let url = book.url else { return nil }
|
||||
|
||||
let task = Network.shared.session.downloadTask(with: url)
|
||||
task.taskDescription = bookID
|
||||
self.init(downloadTask: task)
|
||||
}
|
||||
|
||||
override func operationDidCancel() {
|
||||
// Not Reachable
|
||||
if let error = errors.first as? Operations.ReachabilityCondition.Error, error == .NotReachable {
|
||||
return
|
||||
}
|
||||
|
||||
// Update Core Data
|
||||
if produceResumeData {
|
||||
let context = NSManagedObjectContext.mainQueueContext
|
||||
context.performAndWait({
|
||||
guard let bookID = self.bookID,
|
||||
let book = Book.fetch(bookID, context: context) else {return}
|
||||
book.state = .downloading
|
||||
})
|
||||
} else {
|
||||
let context = NSManagedObjectContext.mainQueueContext
|
||||
context.performAndWait({
|
||||
guard let bookID = self.bookID,
|
||||
let book = Book.fetch(bookID, context: context) else {return}
|
||||
book.state = .cloud
|
||||
|
||||
guard let downloadTask = book.downloadTask else {return}
|
||||
context.delete(downloadTask)
|
||||
})
|
||||
}
|
||||
|
||||
// URLSessionDelegate save resume data and update downloadTask
|
||||
}
|
||||
|
||||
// MARK: - Helper
|
||||
|
||||
fileprivate class func correctFuckingResumeData(_ data: Data?) -> Data? {
|
||||
let kResumeCurrentRequest = "NSURLSessionResumeCurrentRequest"
|
||||
let kResumeOriginalRequest = "NSURLSessionResumeOriginalRequest"
|
||||
|
||||
guard let data = data, let resumeDictionary = (try? PropertyListSerialization.propertyList(from: data, options: [.mutableContainersAndLeaves], format: nil)) as? NSMutableDictionary else {
|
||||
return nil
|
||||
}
|
||||
|
||||
resumeDictionary[kResumeCurrentRequest] = correctFuckingRequestData(resumeDictionary[kResumeCurrentRequest] as? Data)
|
||||
resumeDictionary[kResumeOriginalRequest] = correctFuckingRequestData(resumeDictionary[kResumeOriginalRequest] as? Data)
|
||||
|
||||
let result = try? PropertyListSerialization.data(fromPropertyList: resumeDictionary, format: PropertyListSerialization.PropertyListFormat.xml, options: PropertyListSerialization.WriteOptions())
|
||||
return result
|
||||
}
|
||||
|
||||
fileprivate class func correctFuckingRequestData(_ data: Data?) -> Data? {
|
||||
guard let data = data else {
|
||||
return nil
|
||||
}
|
||||
if NSKeyedUnarchiver.unarchiveObject(with: data) != nil {
|
||||
return data
|
||||
}
|
||||
guard let archive = (try? PropertyListSerialization.propertyList(from: data, options: [.mutableContainersAndLeaves], format: nil)) as? NSMutableDictionary else {
|
||||
return nil
|
||||
}
|
||||
// Rectify weird __nsurlrequest_proto_props objects to $number pattern
|
||||
var k = 0
|
||||
while archive["$objects"]?[1].object(forKey: "$\(k)") != nil {
|
||||
k += 1
|
||||
}
|
||||
var i = 0
|
||||
while archive["$objects"]?[1].object(forKey: "__nsurlrequest_proto_prop_obj_\(i)") != nil {
|
||||
let arr = archive["$objects"] as? NSMutableArray
|
||||
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_prop_obj_\(i)"] {
|
||||
dic.setObject(obj, forKey: "$\(i + k)" as NSCopying)
|
||||
dic.removeObject(forKey: "__nsurlrequest_proto_prop_obj_\(i)")
|
||||
arr?[1] = dic
|
||||
archive["$objects"] = arr
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
if archive["$objects"]?[1]["__nsurlrequest_proto_props"] != nil {
|
||||
let arr = archive["$objects"] as? NSMutableArray
|
||||
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_props"] {
|
||||
dic.setObject(obj, forKey: "$\(i + k)" as NSCopying)
|
||||
dic.removeObject(forKey: "__nsurlrequest_proto_props")
|
||||
arr?[1] = dic
|
||||
archive["$objects"] = arr
|
||||
}
|
||||
}
|
||||
// Rectify weird "NSKeyedArchiveRootObjectKey" top key to NSKeyedArchiveRootObjectKey = "root"
|
||||
if archive["$top"]?["NSKeyedArchiveRootObjectKey"] != nil {
|
||||
(archive["$top"]? as AnyObject).set(archive["$top"]?["NSKeyedArchiveRootObjectKey"], forKey: NSKeyedArchiveRootObjectKey)
|
||||
(archive["$top"]? as AnyObject).removeObject(forKey: "NSKeyedArchiveRootObjectKey")
|
||||
}
|
||||
// Re-encode archived object
|
||||
let result = try? PropertyListSerialization.data(fromPropertyList: archive, format: PropertyListSerialization.PropertyListFormat.binary, options: PropertyListSerialization.WriteOptions())
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class RemoveBookOperation: Procedure {
|
||||
let bookID: String
|
||||
|
||||
init(bookID: String) {
|
||||
self.bookID = bookID
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
let context = NSManagedObjectContext.mainQueueContext
|
||||
context.performAndWait {
|
||||
guard let zimFileURL = ZimMultiReader.shared.readers[self.bookID]?.fileURL else {return}
|
||||
_ = try? FileManager.default.removeItem(at: 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? FileManager.default.removeItem(at: idxFolderURL)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
class PauseBookDwonloadOperation: Procedure {
|
||||
let bookID: String
|
||||
|
||||
init(bookID: String) {
|
||||
self.bookID = bookID
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
Network.shared.operations[bookID]?.cancel(produceResumeData: true)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
class ResumeBookDwonloadOperation: Procedure {
|
||||
let bookID: String
|
||||
|
||||
init(bookID: String) {
|
||||
self.bookID = bookID
|
||||
super.init()
|
||||
name = "Resume Book Dwonload Operation, bookID = \(bookID)"
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
guard let data: Data = Preference.resumeData[bookID] as Data?,
|
||||
let operation = DownloadBookOperation(bookID: bookID, resumeData: data) else {
|
||||
if let operation = DownloadBookOperation(bookID: bookID) {
|
||||
produce(operation)
|
||||
}
|
||||
|
||||
finish()
|
||||
return
|
||||
}
|
||||
Network.shared.queue.addOperation(operation)
|
||||
finish()
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
//
|
||||
// BookmarkMigrationOperation.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 9/26/16.
|
||||
// Copyright © 2016 Chris Li. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import CloudKit
|
||||
import ProcedureKit
|
||||
|
||||
class BookmarkMigrationOperation: Procedure {
|
||||
private let context: NSManagedObjectContext
|
||||
|
||||
override init() {
|
||||
self.context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
||||
context.parent = NSManagedObjectContext.mainQueueContext
|
||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
|
||||
super.init()
|
||||
add(condition: MutuallyExclusive<GlobalQueue>())
|
||||
name = String(describing: self)
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
context.performAndWait {
|
||||
let pids = Book.fetchLocal(self.context).flatMap({$1.pid})
|
||||
for pid in pids {
|
||||
var books = Book.fetch(pid: pid, context: self.context)
|
||||
let latestBook = books.removeFirst()
|
||||
for book in books {
|
||||
book.articles.forEach({$0.book = latestBook})
|
||||
}
|
||||
}
|
||||
if self.context.hasChanges {_ = try? self.context.save()}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkTrashOperation: Procedure {
|
||||
private let context: NSManagedObjectContext
|
||||
private let articles: [Article]
|
||||
|
||||
init(articles: [Article]) {
|
||||
self.context = NSManagedObjectContext.mainQueueContext
|
||||
self.articles = articles
|
||||
|
||||
super.init()
|
||||
// add(condition: MutuallyExclusive<BookmarkController>())
|
||||
name = String(describing: self)
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
context.perform {
|
||||
self.articles.forEach() {
|
||||
$0.isBookmarked = false
|
||||
$0.bookmarkDate = nil
|
||||
}
|
||||
|
||||
// Get books whose zim file removed, but are retain by bookmarks, and whose bookmarks are all removed
|
||||
let books = Set(self.articles.flatMap({$0.book}))
|
||||
.filter({Article.fetchBookmarked(in: $0, with: self.context).count == 0 && $0.state == .retained})
|
||||
books.forEach({ (book) in
|
||||
if let _ = book.meta4URL {
|
||||
book.state = .cloud
|
||||
} else {
|
||||
self.context.delete(book)
|
||||
}
|
||||
})
|
||||
|
||||
if self.context.hasChanges {_ = try? self.context.save()}
|
||||
}
|
||||
|
||||
if articles.count > 0 {
|
||||
// produce(UpdateWidgetDataSourceOperation())
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkCloudKitOperation: Procedure {
|
||||
let article: Article
|
||||
|
||||
init(article: Article) {
|
||||
self.article = article
|
||||
super.init()
|
||||
name = String(describing: self)
|
||||
}
|
||||
|
||||
override func execute() {
|
||||
// guard let bookID = article.book?.id else {finish(); return}
|
||||
// let container = CKContainer(identifier: "iCloud.org.kiwix")
|
||||
// container.accountStatusWithCompletionHandler { (status, error) in
|
||||
// guard status == .Available else {self.finish(); return}
|
||||
//
|
||||
// container.fetchUserRecordIDWithCompletionHandler({ (recordID, error) in
|
||||
// guard let ownerName = recordID?.recordName else {self.finish(); return}
|
||||
// let database = container.privateCloudDatabase
|
||||
// let zoneID = CKRecordZoneID(zoneName: bookID, ownerName: ownerName)
|
||||
// database.fetchRecordZoneWithID(zoneID, completionHandler: { (zone, error) in
|
||||
// if let zone = zone {
|
||||
//
|
||||
// } else {
|
||||
// database.
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// guard let bookID = article.book?.id else {finish(); return}
|
||||
//
|
||||
// let recordID = CKRecordID(recordName: bookID + "|" + article.path)
|
||||
// let database = CKContainer(identifier: "iCloud.org.kiwix").privateCloudDatabase
|
||||
//
|
||||
// database.fetchRecordWithID(recordID) { (record, error) in
|
||||
// if let record = record {
|
||||
// if self.article.isBookmarked {
|
||||
// self.populate(record, with: self.article)
|
||||
// database.saveRecord(record, completionHandler: { (record, error) in
|
||||
// self.finish()
|
||||
// })
|
||||
// } else {
|
||||
// database.deleteRecordWithID(recordID, completionHandler: { (recordID, error) in
|
||||
// self.finish()
|
||||
// })
|
||||
// }
|
||||
// } else {
|
||||
// guard self.article.isBookmarked else {self.finish(); return}
|
||||
// let record = CKRecord(recordType: "Article", recordID: recordID)
|
||||
// self.populate(record, with: self.article)
|
||||
// database.saveRecord(record, completionHandler: { (record, error) in
|
||||
// self.finish()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func populate(_ record: CKRecord, with article: Article) {
|
||||
record["path"] = self.article.path as CKRecordValue?
|
||||
record["title"] = self.article.title as CKRecordValue?
|
||||
record["snippet"] = self.article.snippet as CKRecordValue?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,9 +67,10 @@ fileprivate class Process: Procedure, InputProcedure, XMLParserDelegate {
|
||||
let toBeDeleted = storeBookIDs.subtracting(memoryBookIDs)
|
||||
hasUpdate = toBeDeleted.count > 0
|
||||
context.performAndWait {
|
||||
toBeDeleted.forEach({ (id) in
|
||||
|
||||
})
|
||||
for id in toBeDeleted {
|
||||
guard let book = Book.fetch(id, context: self.context) else {continue}
|
||||
self.context.delete(book)
|
||||
}
|
||||
}
|
||||
|
||||
if context.hasChanges { try? context.save() }
|
||||
|
@ -1,131 +0,0 @@
|
||||
//
|
||||
// RefreshLibraryOperation.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 2/7/16.
|
||||
// Copyright © 2016 Chris Li. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import ProcedureKit
|
||||
|
||||
class RefreshLibraryOperation: GroupProcedure {
|
||||
|
||||
private(set) var hasUpdate = false
|
||||
private(set) var firstTime = Preference.libraryLastRefreshTime == nil
|
||||
|
||||
init(invokedByUser: Bool = false) {
|
||||
let retrieve = Retrieve()
|
||||
let process = Process()
|
||||
process.injectResultFromDependency(retrieve)
|
||||
super.init(operations: [retrieve, process])
|
||||
|
||||
addObserver(NetworkObserver())
|
||||
if UIApplication.sharedApplication().applicationState == .Active {
|
||||
add(ReachabilityCondition(url: Retrieve.url))
|
||||
}
|
||||
|
||||
addObserver(WillExecuteObserver { _ in
|
||||
(UIApplication.sharedApplication().delegate as! AppDelegate).registerNotification()
|
||||
})
|
||||
|
||||
process.addObserver(DidFinishObserver { [unowned self] (operation, errors) in
|
||||
guard let operation = operation as? Process else {return}
|
||||
self.hasUpdate = operation.hasUpdate
|
||||
self.firstTime = operation.firstTime
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class Retrieve: Procedure, ResultInjection {
|
||||
private static let url = URL(string: "https://download.kiwix.org/library/library.xml")!
|
||||
private var result: Data?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
name = "Library Retrieve"
|
||||
}
|
||||
|
||||
fileprivate override func execute() {
|
||||
guard !isCancelled else {return}
|
||||
let task = URLSession.shared.dataTask(with: Retrieve.url, completionHandler: { (data, response, error) in
|
||||
self.result = data
|
||||
self.finish()
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
private class Process: Procedure, XMLParserDelegate, AutomaticInjectionOperationType {
|
||||
var requirement: Data?
|
||||
fileprivate(set) var hasUpdate = false
|
||||
fileprivate(set) var firstTime = false
|
||||
private let context: NSManagedObjectContext
|
||||
private var oldBookIDs = Set<String>()
|
||||
private var newBookIDs = Set<String>()
|
||||
|
||||
override init() {
|
||||
self.context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
||||
context.parent = NSManagedObjectContext.mainQueueContext
|
||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
super.init()
|
||||
name = "Library Process"
|
||||
}
|
||||
|
||||
override fileprivate func execute() {
|
||||
guard let data = requirement else {finish(); return}
|
||||
let xmlParser = XMLParser(data: data)
|
||||
xmlParser.delegate = self
|
||||
xmlParser.parse()
|
||||
finish()
|
||||
}
|
||||
|
||||
fileprivate func saveManagedObjectContexts() {
|
||||
context.performAndWait { self.context.saveIfNeeded() }
|
||||
context.parent?.performAndWait { self.context.parent?.saveIfNeeded() }
|
||||
}
|
||||
|
||||
// MARK: NSXMLParser Delegate
|
||||
|
||||
@objc fileprivate func parserDidStartDocument(_ parser: XMLParser) {
|
||||
context.performAndWait { () -> Void in
|
||||
self.oldBookIDs = Set(Book.fetchAll(self.context).map({ $0.id }))
|
||||
}
|
||||
}
|
||||
|
||||
@objc fileprivate func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
|
||||
guard elementName == "book",
|
||||
let id = attributeDict["id"] else {return}
|
||||
|
||||
if !oldBookIDs.contains(id) {
|
||||
hasUpdate = true
|
||||
context.performAndWait({ () -> Void in
|
||||
Book.add(attributeDict, context: self.context)
|
||||
})
|
||||
}
|
||||
newBookIDs.insert(id)
|
||||
}
|
||||
|
||||
@objc fileprivate func parserDidEndDocument(_ parser: XMLParser) {
|
||||
let idsToDelete = oldBookIDs.subtracting(newBookIDs)
|
||||
|
||||
context.performAndWait({ () -> Void in
|
||||
idsToDelete.forEach({ (id) in
|
||||
guard let book = Book.fetch(id, context: self.context) else {return}
|
||||
|
||||
// Delete Book object only if book is online, i.e., is not associated with a download task or is not local
|
||||
guard book.state == .cloud else {return}
|
||||
self.context.delete(book)
|
||||
self.hasUpdate = true
|
||||
})
|
||||
})
|
||||
|
||||
saveManagedObjectContexts()
|
||||
firstTime = Preference.libraryLastRefreshTime == nil
|
||||
Preference.libraryLastRefreshTime = Date()
|
||||
}
|
||||
|
||||
@objc fileprivate func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
|
||||
saveManagedObjectContexts()
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
|
||||
//
|
||||
// ZimReader.m
|
||||
// KiwixTest
|
||||
|
Loading…
x
Reference in New Issue
Block a user