diff --git a/Kiwix-iOS/Info.plist b/Kiwix-iOS/Info.plist index 9a5605a5..e4f812f2 100644 --- a/Kiwix-iOS/Info.plist +++ b/Kiwix-iOS/Info.plist @@ -49,7 +49,7 @@ CFBundleVersion - 1.8.3557 + 1.8.3635 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/Kiwix-iOSWidgets/Bookmarks/Info.plist b/Kiwix-iOSWidgets/Bookmarks/Info.plist index cd8242fe..ab1f1b41 100644 --- a/Kiwix-iOSWidgets/Bookmarks/Info.plist +++ b/Kiwix-iOSWidgets/Bookmarks/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 1.8.3557 + 1.8.3635 NSExtension NSExtensionMainStoryboard diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index f7242f7f..e5956492 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -44,7 +44,7 @@ 9734E54E1D289D060061C39B /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9734E54D1D289D060061C39B /* Welcome.storyboard */; }; 973A5C921DEA3F5600C7804C /* CoreDataTableBaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973A5C911DEA3F5600C7804C /* CoreDataTableBaseController.swift */; }; 973A5C951DEA6DD000C7804C /* URLResponseCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973A5C931DEA6CA900C7804C /* URLResponseCache.swift */; }; - 973A5C991DEBC54800C7804C /* CloudKitOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973A5C981DEBC54800C7804C /* CloudKitOperations.swift */; }; + 973A5C991DEBC54800C7804C /* CloudKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973A5C981DEBC54800C7804C /* CloudKit.swift */; }; 973BCD1A1CEB402900F10B44 /* KiwixTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BCD181CEB402900F10B44 /* KiwixTests.swift */; }; 973DD40F1D343F2F009D45DB /* libicudata.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4041D343F2F009D45DB /* libicudata.a */; }; 973DD4101D343F2F009D45DB /* libicui18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4051D343F2F009D45DB /* libicui18n.a */; }; @@ -192,7 +192,7 @@ 9734E54D1D289D060061C39B /* Welcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Welcome.storyboard; path = "Kiwix-iOS/Storyboard/Welcome.storyboard"; sourceTree = SOURCE_ROOT; }; 973A5C911DEA3F5600C7804C /* CoreDataTableBaseController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataTableBaseController.swift; sourceTree = ""; }; 973A5C931DEA6CA900C7804C /* URLResponseCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLResponseCache.swift; sourceTree = ""; }; - 973A5C981DEBC54800C7804C /* CloudKitOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitOperations.swift; sourceTree = ""; }; + 973A5C981DEBC54800C7804C /* CloudKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKit.swift; sourceTree = ""; }; 973BCD001CEB3FA500F10B44 /* Kiwix_OSXTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Kiwix_OSXTests.swift; sourceTree = ""; }; 973BCD021CEB3FA500F10B44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 973BCD0B1CEB3FA500F10B44 /* Kiwix_OSXUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Kiwix_OSXUITests.swift; sourceTree = ""; }; @@ -788,7 +788,7 @@ 97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */, 9764CBD21D8083AA00072D6A /* ArticleOperation.swift */, 970A2A211DD562CB0078BB7C /* BookOperations.swift */, - 973A5C981DEBC54800C7804C /* CloudKitOperations.swift */, + 973A5C981DEBC54800C7804C /* CloudKit.swift */, 973208281DD223DB00EDD3DC /* RefreshLibrary.swift */, 97D6811E1D6F70AC00E5FA99 /* ScanLocalBook.swift */, 972F81561DDBFC79008D7289 /* Search.swift */, @@ -1179,7 +1179,7 @@ 97A1FD261D6F71E200A80EE2 /* ZimReader.mm in Sources */, 97A1FD1C1D6F71D800A80EE2 /* KiwixURLProtocol.swift in Sources */, 97C2C26A1DDCC58500A9CC64 /* ArticleOperation.swift in Sources */, - 973A5C991DEBC54800C7804C /* CloudKitOperations.swift in Sources */, + 973A5C991DEBC54800C7804C /* CloudKit.swift in Sources */, 973208261DD21E9C00EDD3DC /* CoreDataContainer.swift in Sources */, 97D6813F1D6F712800E5FA99 /* Article+CoreDataProperties.swift in Sources */, 97A1FD441D6F728200A80EE2 /* Preference.swift in Sources */, diff --git a/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index e2573a59..32e8dcb3 100644 --- a/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -13,5 +13,37 @@ stopOnStyle = "0"> + + + + + + + + diff --git a/Kiwix/CoreData/Classes/Article.swift b/Kiwix/CoreData/Classes/Article.swift index cc4ff058..c0c470e6 100644 --- a/Kiwix/CoreData/Classes/Article.swift +++ b/Kiwix/CoreData/Classes/Article.swift @@ -8,7 +8,6 @@ import CoreData import CoreSpotlight -import CloudKit class Article: NSManagedObject { @@ -61,19 +60,19 @@ class Article: NSManagedObject { // MARK: - CloudKit - var cloudKitRecord: CKRecord? { - guard let url = url, let bookID = book?.id else {return nil} - let recordID = CKRecordID(recordName: url.absoluteString) - let bookRecordID = CKRecordID(recordName: bookID) - let record = CKRecord(recordType: "Article", recordID: recordID) - record["path"] = path as NSString? - record["title"] = title as NSString? - record["snippet"] = snippet as NSString? - record["thumbImagePath"] = thumbImagePath as NSString? - record["isBookmarked"] = isBookmarked as NSNumber - record["book"] = CKReference(recordID: bookRecordID, action: .deleteSelf) - return record - } +// var cloudKitRecord: CKRecord? { +// guard let url = url, let bookID = book?.id else {return nil} +// let recordID = CKRecordID(recordName: url.absoluteString) +// let bookRecordID = CKRecordID(recordName: bookID) +// let record = CKRecord(recordType: "Article", recordID: recordID) +// record["path"] = path as NSString? +// record["title"] = title as NSString? +// record["snippet"] = snippet as NSString? +// record["thumbImagePath"] = thumbImagePath as NSString? +// record["isBookmarked"] = isBookmarked as NSNumber +// record["book"] = CKReference(recordID: bookRecordID, action: .deleteSelf) +// return record +// } // MARK: - Properties diff --git a/Kiwix/CoreData/Classes/Book.swift b/Kiwix/CoreData/Classes/Book.swift index 98561f81..f70a4ebd 100644 --- a/Kiwix/CoreData/Classes/Book.swift +++ b/Kiwix/CoreData/Classes/Book.swift @@ -8,7 +8,7 @@ import Foundation import CoreData -import CloudKit + #if os(iOS) || os(watchOS) || os(tvOS) import UIKit #elseif os(OSX) @@ -102,30 +102,30 @@ class Book: NSManagedObject { } // MARK: - CloudKit - - var recordZoneID: CKRecordZoneID { - return CKRecordZoneID(zoneName: id, ownerName: CKCurrentUserDefaultName) - } - - var recordID: CKRecordID { - return CKRecordID(recordName: id, zoneID: recordZoneID) - } - - var record: CKRecord { - let record = CKRecord(recordType: "Book", recordID: recordID) - record["id"] = id as NSString? - record["title"] = title as NSString? - record["description"] = desc as NSString? - record["creator"] = creator as NSString? - record["publisher"] = publisher as NSString? - record["favicon"] = favIcon as NSData? - record["date"] = date as NSDate? - record["articleCount"] = articleCount as NSNumber - record["mediaCount"] = mediaCount as NSNumber - record["fileSize"] = fileSize as NSNumber - record["language"] = language?.code as NSString? - return record - } +// +// var recordZoneID: CKRecordZoneID { +// return CKRecordZoneID(zoneName: id, ownerName: CKCurrentUserDefaultName) +// } +// +// var recordID: CKRecordID { +// return CKRecordID(recordName: id, zoneID: recordZoneID) +// } +// +// var record: CKRecord { +// let record = CKRecord(recordType: "Book", recordID: recordID) +// record["id"] = id as NSString? +// record["title"] = title as NSString? +// record["description"] = desc as NSString? +// record["creator"] = creator as NSString? +// record["publisher"] = publisher as NSString? +// record["favicon"] = favIcon as NSData? +// record["date"] = date as NSDate? +// record["articleCount"] = articleCount as NSNumber +// record["mediaCount"] = mediaCount as NSNumber +// record["fileSize"] = fileSize as NSNumber +// record["language"] = language?.code as NSString? +// return record +// } // MARK: - Properties diff --git a/Kiwix/Operations/CloudKit.swift b/Kiwix/Operations/CloudKit.swift new file mode 100644 index 00000000..804f3778 --- /dev/null +++ b/Kiwix/Operations/CloudKit.swift @@ -0,0 +1,148 @@ +// +// CloudKitOperations.swift +// Kiwix +// +// Created by Chris Li on 11/27/16. +// Copyright © 2016 Chris Li. All rights reserved. +// + +import CloudKit +import ProcedureKit + +class BookmarkSyncOperation: GroupProcedure { + let articleURL: URL + + init(articleURL: URL) { + self.articleURL = articleURL + let database = CKContainer(identifier: "iCloud.org.kiwix").privateCloudDatabase + let zone = FetchBookRecordZoneOperation(database: database, bookID: articleURL.host!) + let book = FetchBookRecordOperation(database: database, bookID: articleURL.host!) + book.inject(dependency: zone, block: {book, zone, error in + book.recordZone = zone.recordZone + }) + + super.init(operations: [zone, book]) + } + + +} + +//class UpdateArticleOperation: Procedure { +// let database: CKDatabase +// let bookID: String +// +// init(database: CKDatabase, bookID: String) { +// self.database = database +// self.bookID = bookID +// super.init() +// } +// +// override func execute() { +// guard let zone = requirement.value else { +// finish() +// return +// } +// +// AppDelegate.persistentContainer.performBackgroundTask { (context) in +// guard let book = Book.fetch(self.bookID, context: context) else { +// self.finish() +// return +// } +// let fetch = CKFetchRecordsOperation(recordIDs: [bookRecordID]) +// fetch.database = self.database +// fetch.fetchRecordsCompletionBlock = { records, error in +// if let book = records?[bookRecordID] { +// +// } +// } +// } +// +// +// } +//} + +class FetchBookRecordOperation: Procedure { + let database: CKDatabase + let bookID: String + var recordZone: CKRecordZone? + var record: CKRecord? + + init(database: CKDatabase, bookID: String) { + self.database = database + self.bookID = bookID + super.init() + } + + override func execute() { + guard let zone = recordZone else { + finish() + return + } + AppDelegate.persistentContainer.performBackgroundTask { (context) in + let recordID = CKRecordID(recordName: self.bookID, zoneID: zone.zoneID) + let fetch = CKFetchRecordsOperation(recordIDs: [recordID]) + fetch.database = self.database + fetch.fetchRecordsCompletionBlock = { records, error in + if let record = records?[recordID] { + self.record = record + // update, or not + self.finish() + } else { + let record = CKRecord(recordType: "Book", recordID: recordID) + let modify = CKModifyRecordsOperation(recordsToSave: [record], recordIDsToDelete: nil) + modify.database = self.database + modify.modifyRecordsCompletionBlock = { saved, _, error in + if let record = saved?.first { + self.record = record + self.finish() + } else { + self.finish(withError: error) + } + } + CloudKitQueue.shared.add(operations: modify) + } + } + CloudKitQueue.shared.add(operations: fetch) + } + } +} + +class FetchBookRecordZoneOperation: Procedure { + let database: CKDatabase + let bookID: String + private(set) var recordZone: CKRecordZone? + + init(database: CKDatabase, bookID: String) { + self.database = database + self.bookID = bookID + super.init() + } + + override func execute() { + let zoneID = CKRecordZoneID(zoneName: bookID, ownerName: CKCurrentUserDefaultName) + let fetch = CKFetchRecordZonesOperation(recordZoneIDs: [zoneID]) + fetch.database = database + fetch.fetchRecordZonesCompletionBlock = {zones, error in + if let error = error as? CKError, (error.code == .partialFailure || error.code == .zoneNotFound) { + let zone = CKRecordZone(zoneID: zoneID) + let modify = CKModifyRecordZonesOperation(recordZonesToSave: [zone], recordZoneIDsToDelete: nil) + modify.database = self.database + modify.modifyRecordZonesCompletionBlock = { saved, _, error in + if let zone = saved?.first { + self.recordZone = zone + self.finish() + } else { + self.finish(withError: error) + } + } + CloudKitQueue.shared.add(operations: modify) + } else if let zone = zones?[zoneID] { + self.recordZone = zone + self.finish() + } else { + self.finish(withError: error) + } + } + CloudKitQueue.shared.add(operations: fetch) + } +} diff --git a/Kiwix/Operations/CloudKitOperations.swift b/Kiwix/Operations/CloudKitOperations.swift deleted file mode 100644 index 92a2d675..00000000 --- a/Kiwix/Operations/CloudKitOperations.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// CloudKitOperations.swift -// Kiwix -// -// Created by Chris Li on 11/27/16. -// Copyright © 2016 Chris Li. All rights reserved. -// - -import CloudKit -import ProcedureKit - -class BookmarkSyncOperation: GroupProcedure { - let articleURL: URL - - init(articleURL: URL) { - self.articleURL = articleURL - super.init(operations: []) - } - - -} - -class FetchBookRecordZoneOperation: Procedure, ResultInjection { - let database: CKDatabase - let bookID: String - var requirement: PendingValue = .void - fileprivate(set) var result: PendingValue = .pending - - init(database: CKDatabase, bookID: String) { - self.database = database - self.bookID = bookID - super.init() - } - - override func execute() { - let zoneID = CKRecordZoneID(zoneName: bookID, ownerName: CKCurrentUserDefaultName) - let fetch = CKFetchRecordZonesOperation(recordZoneIDs: [zoneID]) - fetch.database = database - fetch.fetchRecordZonesCompletionBlock = {zones, error in - if let error = error as? CKError, error.code == .zoneNotFound { - let zone = CKRecordZone(zoneID: zoneID) - let modify = CKModifyRecordZonesOperation(recordZonesToSave: [zone], recordZoneIDsToDelete: nil) - modify.database = self.database - modify.modifyRecordZonesCompletionBlock = { saved, _, error in - if let error = error { - self.finish(withError: error) - } else { -// self.result.value = saved?.first - } - } - } else { - - } - } - CloudKitQueue.shared.add(operations: fetch) - } -}