From 74169c0d86851def2e15202068fed2b37bdb1acc Mon Sep 17 00:00:00 2001 From: Chris Li Date: Fri, 8 Jul 2016 11:06:41 -0400 Subject: [PATCH 01/54] Project Setting: run auto build number increment script only when install --- Kiwix.xcodeproj/project.pbxproj | 4 ++-- README.md | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index a0adaa0ba..325709147 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -1632,14 +1632,14 @@ }; 97A4D7E21CF8C318006C731E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 12; + buildActionMask = 8; files = ( ); inputPaths = ( ); outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; shellScript = "#!/bin/bash\nbuildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$INFOPLIST_FILE\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\""; }; diff --git a/README.md b/README.md index 1bf62f13b..37397aa61 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,20 @@ Kiwix for iOS is an offline reader for wikipedia. Our mission is to give people equal and easy access to free knowledge of the world. Download it on [iTunes Store](https://itunes.apple.com/us/app/kiwix/id997079563). -## What's to come in version 1.6 -- Table of contents -- Some iPad search interface design changes -- Spotlight search integration (will index opened / read articles) - -### Stuff may be added in 1.6 -- Library enhancement -- Today widget (not likely) -- home screen icon 3D touch \ No newline at end of file +## What's new in version 1.6 + +New + +- Table of content +- Recent searches +- Search system: now fetch result from both title search and index search and use a new ranking system to sort them +- Enhanced layout javascript on iPhone +- Use SafariViewController to handle external links +- Access download.kiwix.org using https +- Enhanced Search UI on iPad and iPhone 6/6s Plus (landscape) + +Fixed + +- Downloading / paused book no longer got purged when they are removed from online library +- Removed code that mistakenly indicates app use Wallet +- Open main page when first book finish downloaded \ No newline at end of file From fb60a42ac5d5d5ee91ee86b29150febd1f2b4220 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 11 Jul 2016 14:00:56 -0400 Subject: [PATCH 02/54] Kiwix URL init + remove another passbook code file --- Kiwix-iOS/Model/KiwixURLProtocol.swift | 8 ++++- Kiwix.xcodeproj/project.pbxproj | 4 --- .../xcdebugger/Breakpoints_v2.xcbkptlist | 34 +++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/Kiwix-iOS/Model/KiwixURLProtocol.swift b/Kiwix-iOS/Model/KiwixURLProtocol.swift index 5c83a5b38..4deb8e705 100644 --- a/Kiwix-iOS/Model/KiwixURLProtocol.swift +++ b/Kiwix-iOS/Model/KiwixURLProtocol.swift @@ -47,7 +47,7 @@ class KiwixURLProtocol: NSURLProtocol { extension NSURL { class func kiwixURLWithZimFileid(id: String, contentURLString: String) -> NSURL? { - guard let escapedContentURLString = contentURLString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLPathAllowedCharacterSet()) else {return nil} + guard let escapedContentURLString = contentURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLPathAllowedCharacterSet()) else {return nil} let baseURLString = "kiwix://" + id return NSURL(string: escapedContentURLString, relativeToURL: NSURL(string: baseURLString)) } @@ -59,4 +59,10 @@ extension NSURL { } return NSURL.kiwixURLWithZimFileid(id, contentURLString: contentURLString) } + + convenience init?(id: ZIMID, contentURLString: String) { + guard let escapedContentURLString = contentURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLPathAllowedCharacterSet()) else {return nil} + let baseURLString = "kiwix://" + id + self.init(string: escapedContentURLString, relativeToURL: NSURL(string: baseURLString)) + } } diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 325709147..2f6856c2d 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -199,8 +199,6 @@ 979CB6661D05C44F005E1BA1 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6241D05C44F005E1BA1 /* NoCancelledDependencies.swift */; }; 979CB6671D05C44F005E1BA1 /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6251D05C44F005E1BA1 /* OperationCondition.swift */; }; 979CB6681D05C44F005E1BA1 /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6251D05C44F005E1BA1 /* OperationCondition.swift */; }; - 979CB6691D05C44F005E1BA1 /* PassbookCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6261D05C44F005E1BA1 /* PassbookCondition.swift */; }; - 979CB66A1D05C44F005E1BA1 /* PassbookCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6261D05C44F005E1BA1 /* PassbookCondition.swift */; }; 979CB66B1D05C44F005E1BA1 /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6271D05C44F005E1BA1 /* PhotosCondition.swift */; }; 979CB66C1D05C44F005E1BA1 /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6271D05C44F005E1BA1 /* PhotosCondition.swift */; }; 979CB66D1D05C44F005E1BA1 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6281D05C44F005E1BA1 /* ReachabilityCondition.swift */; }; @@ -1715,7 +1713,6 @@ 979CB64C1D05C44F005E1BA1 /* LocationCapability-iOS.swift in Sources */, 979CB6541D05C44F005E1BA1 /* PhotosCapability.swift in Sources */, 979CB6CA1D05D26E005E1BA1 /* WebViewController.swift in Sources */, - 979CB66A1D05C44F005E1BA1 /* PassbookCondition.swift in Sources */, 9711878E1CEB541100B9909D /* Kiwix.xcdatamodeld in Sources */, 971187961CEB542500B9909D /* DownloadTask+CoreDataProperties.swift in Sources */, 979CB64A1D05C44F005E1BA1 /* iCloudContainerCapability.swift in Sources */, @@ -1802,7 +1799,6 @@ buildActionMask = 2147483647; files = ( 976AB2671CBD8B3D00B06EB0 /* 1.5.xcmappingmodel in Sources */, - 979CB6691D05C44F005E1BA1 /* PassbookCondition.swift in Sources */, 971A10311D022AD5007FC62C /* RefreshHUD.swift in Sources */, 973970ED1CB9A04C00350507 /* SearchOperation.swift in Sources */, 979CB6BB1D05C520005E1BA1 /* NSOperation+Operations.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 ed9a9b4d4..c16992284 100644 --- a/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -2,4 +2,38 @@ + + + + + + + + + + From c6d4808e2305f937b3b0620c50a35350fb870d15 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 11 Jul 2016 16:17:39 -0400 Subject: [PATCH 03/54] Migration --- Kiwix-iOS/AppDelegate.swift | 1 + Kiwix-iOS/Controller/MainVC.swift | 1 + Kiwix-iOS/Model/Network.swift | 9 +- Kiwix.xcodeproj/project.pbxproj | 489 ++++-------------- Kiwix/GlobalOperationQueue.swift | 15 - .../Capability/CalendarCapability.swift | 42 -- .../OperationLib/Capability/Capability.swift | 131 ----- .../Capability/HealthCapability.swift | 89 ---- .../Capability/LocationCapability-OSX.swift | 86 --- .../Capability/LocationCapability-iOS.swift | 105 ---- .../Capability/LocationCapability-tvOS.swift | 88 ---- .../Capability/PassbookCapability.swift | 43 -- .../Capability/PhotosCapability.swift | 41 -- .../Capability/PushCapability-OSX.swift | 78 --- .../Capability/PushCapability-iOS.swift | 84 --- .../UserNotificationCapability.swift | 113 ---- .../iCloudContainerCapability.swift | 87 ---- .../Conditions/CalendarCondition.swift | 88 ---- .../Conditions/CloudCondition.swift | 96 ---- .../Conditions/HealthCondition.swift | 131 ----- .../Conditions/LocationCondition.swift | 172 ------ .../Conditions/NegatedCondition.swift | 56 -- .../Conditions/NoCancelledDependencies.swift | 46 -- .../Conditions/OperationCondition.swift | 112 ---- .../Conditions/PassbookCondition.swift | 45 -- .../Conditions/PhotosCondition.swift | 69 --- .../RemoteNotificationCondition.swift | 129 ----- .../Conditions/SilentCondition.swift | 41 -- .../UserNotificationCondition.swift | 124 ----- .../Exclusive/ExclusivityController.swift | 79 --- .../Exclusive/MutuallyExclusive.swift | 39 -- .../OperationLib/Observer/BlockObserver.swift | 47 -- .../Observer/OperationObserver.swift | 32 -- .../Observer/TimeoutObserver.swift | 60 --- .../Operations/BlockOperation.swift | 58 --- .../Operations/CKContainer+Operations.swift | 84 --- .../Operations/DelayOperation.swift | 72 --- .../Operations/Dictionary+Operations.swift | 31 -- .../Operations/GroupOperation.swift | 113 ---- .../Operations/LocationOperation.swift | 90 ---- .../Operations/NSLock+Operations.swift | 27 - .../Operations/NSOperation+Operations.swift | 38 -- Kiwix/OperationLib/Operations/Operation.swift | 412 --------------- .../Operations/OperationErrors.swift | 40 -- .../Operations/OperationQueue.swift | 156 ------ .../UIUserNotifications+Operations.swift | 49 -- .../Operations/URLSessionTaskOperation.swift | 74 --- .../Operations/AlertOperation.swift | 1 + Kiwix/Operations/GlobalOperationQueue.swift | 43 ++ .../NetworkObserver.swift | 1 + .../RefreshLibraryOperation.swift | 1 + Kiwix/{ => Operations}/SearchOperation.swift | 1 + Kiwix/{ => Operations}/UIOperations.swift | 1 + .../URLSessionDownloadTaskOperation.swift | 31 ++ .../ReachabilityCondition.swift | 5 +- Kiwix/ZimMultiReader.swift | 1 + Podfile | 1 + Podfile.lock | 15 +- 58 files changed, 196 insertions(+), 4017 deletions(-) delete mode 100644 Kiwix/GlobalOperationQueue.swift delete mode 100755 Kiwix/OperationLib/Capability/CalendarCapability.swift delete mode 100755 Kiwix/OperationLib/Capability/Capability.swift delete mode 100755 Kiwix/OperationLib/Capability/HealthCapability.swift delete mode 100755 Kiwix/OperationLib/Capability/LocationCapability-OSX.swift delete mode 100755 Kiwix/OperationLib/Capability/LocationCapability-iOS.swift delete mode 100755 Kiwix/OperationLib/Capability/LocationCapability-tvOS.swift delete mode 100755 Kiwix/OperationLib/Capability/PassbookCapability.swift delete mode 100755 Kiwix/OperationLib/Capability/PhotosCapability.swift delete mode 100755 Kiwix/OperationLib/Capability/PushCapability-OSX.swift delete mode 100755 Kiwix/OperationLib/Capability/PushCapability-iOS.swift delete mode 100755 Kiwix/OperationLib/Capability/UserNotificationCapability.swift delete mode 100755 Kiwix/OperationLib/Capability/iCloudContainerCapability.swift delete mode 100755 Kiwix/OperationLib/Conditions/CalendarCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/CloudCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/HealthCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/LocationCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/NegatedCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/NoCancelledDependencies.swift delete mode 100755 Kiwix/OperationLib/Conditions/OperationCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/PassbookCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/PhotosCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/RemoteNotificationCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/SilentCondition.swift delete mode 100755 Kiwix/OperationLib/Conditions/UserNotificationCondition.swift delete mode 100755 Kiwix/OperationLib/Exclusive/ExclusivityController.swift delete mode 100755 Kiwix/OperationLib/Exclusive/MutuallyExclusive.swift delete mode 100755 Kiwix/OperationLib/Observer/BlockObserver.swift delete mode 100755 Kiwix/OperationLib/Observer/OperationObserver.swift delete mode 100755 Kiwix/OperationLib/Observer/TimeoutObserver.swift delete mode 100755 Kiwix/OperationLib/Operations/BlockOperation.swift delete mode 100755 Kiwix/OperationLib/Operations/CKContainer+Operations.swift delete mode 100755 Kiwix/OperationLib/Operations/DelayOperation.swift delete mode 100755 Kiwix/OperationLib/Operations/Dictionary+Operations.swift delete mode 100755 Kiwix/OperationLib/Operations/GroupOperation.swift delete mode 100755 Kiwix/OperationLib/Operations/LocationOperation.swift delete mode 100755 Kiwix/OperationLib/Operations/NSLock+Operations.swift delete mode 100755 Kiwix/OperationLib/Operations/NSOperation+Operations.swift delete mode 100755 Kiwix/OperationLib/Operations/Operation.swift delete mode 100755 Kiwix/OperationLib/Operations/OperationErrors.swift delete mode 100755 Kiwix/OperationLib/Operations/OperationQueue.swift delete mode 100755 Kiwix/OperationLib/Operations/UIUserNotifications+Operations.swift delete mode 100755 Kiwix/OperationLib/Operations/URLSessionTaskOperation.swift rename Kiwix/{OperationLib => }/Operations/AlertOperation.swift (99%) create mode 100644 Kiwix/Operations/GlobalOperationQueue.swift rename Kiwix/{OperationLib/Observer => Operations}/NetworkObserver.swift (99%) rename Kiwix/{ => Operations}/RefreshLibraryOperation.swift (99%) rename Kiwix/{ => Operations}/SearchOperation.swift (99%) rename Kiwix/{ => Operations}/UIOperations.swift (99%) create mode 100644 Kiwix/Operations/URLSessionDownloadTaskOperation.swift rename Kiwix/{OperationLib/Conditions => }/ReachabilityCondition.swift (97%) diff --git a/Kiwix-iOS/AppDelegate.swift b/Kiwix-iOS/AppDelegate.swift index e3da4a473..8c44fb14b 100644 --- a/Kiwix-iOS/AppDelegate.swift +++ b/Kiwix-iOS/AppDelegate.swift @@ -7,6 +7,7 @@ import UIKit import CoreData +import PSOperations @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate { diff --git a/Kiwix-iOS/Controller/MainVC.swift b/Kiwix-iOS/Controller/MainVC.swift index 82433392d..87486eb7d 100644 --- a/Kiwix-iOS/Controller/MainVC.swift +++ b/Kiwix-iOS/Controller/MainVC.swift @@ -7,6 +7,7 @@ // import UIKit +import PSOperations class MainVC: UIViewController { diff --git a/Kiwix-iOS/Model/Network.swift b/Kiwix-iOS/Model/Network.swift index cbe3d8764..1fc63b670 100644 --- a/Kiwix-iOS/Model/Network.swift +++ b/Kiwix-iOS/Model/Network.swift @@ -7,6 +7,7 @@ // import UIKit +import PSOperations class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSURLSessionTaskDelegate, OperationQueueDelegate { static let sharedInstance = Network() @@ -40,7 +41,7 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU } session.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) -> Void in for task in downloadTasks { - let operation = URLSessionTaskOperation(task: task, produceResumeDataWhenCancel: true) + let operation = URLSessionDownloadTaskOperation(task: task, produceResumeData: true) operation.name = task.taskDescription operation.addObserver(NetworkObserver()) self.operationQueue.addOperation(operation) @@ -81,8 +82,8 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU } func cancel(book: Book) { - guard let id = book.id, let operation = operationQueue.getOperation(id) as? URLSessionTaskOperation else {return} - operation.produceResumeDataWhenCancel = false + guard let id = book.id, let operation = operationQueue.getOperation(id) as? URLSessionDownloadTaskOperation else {return} + operation.produceResumeData = false operation.cancel() } @@ -93,7 +94,7 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU let downloadTask = DownloadTask.addOrUpdate(book, context: context) downloadTask?.state = .Queued - let operation = URLSessionTaskOperation(task: task, produceResumeDataWhenCancel: true) + let operation = URLSessionDownloadTaskOperation(task: task, produceResumeData: true) operation.name = id operation.addObserver(NetworkObserver()) operationQueue.addOperation(operation) diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 2f6856c2d..d4feafef4 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -7,8 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - 970103FB1C6824FA00DC48F6 /* RefreshLibraryOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970103F91C6824FA00DC48F6 /* RefreshLibraryOperation.swift */; }; + 7356F9FACBB84380CFC8F68F /* Pods_Kiwix_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC884ACBBA260AF741C4C4FE /* Pods_Kiwix_iOS.framework */; }; 970C3DCA1CBD79450026A240 /* MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970C3DC91CBD79450026A240 /* MigrationPolicy.swift */; }; + 970C61971D34243600087758 /* URLSessionDownloadTaskOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970C61961D34243600087758 /* URLSessionDownloadTaskOperation.swift */; }; + 970C61991D3429E400087758 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970C61981D3429E400087758 /* ReachabilityCondition.swift */; }; 971046321D19B96E002141C0 /* XapianSearcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 971046311D19B96E002141C0 /* XapianSearcher.mm */; }; 971046341D1C830C002141C0 /* SearchTuneController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 971046331D1C830C002141C0 /* SearchTuneController.swift */; }; 9711871C1CEB448400B9909D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711871B1CEB448400B9909D /* libz.tbd */; }; @@ -105,7 +107,6 @@ 971A10811D022F74007FC62C /* Pic_P.png in Resources */ = {isa = PBXBuildFile; fileRef = 971A107D1D022F74007FC62C /* Pic_P.png */; }; 97254FDF1C2644560056950B /* ZIMMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97254FDE1C2644560056950B /* ZIMMultiReader.swift */; }; 9734E54E1D289D060061C39B /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9734E54D1D289D060061C39B /* Welcome.storyboard */; }; - 973970ED1CB9A04C00350507 /* SearchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973970EC1CB9A04C00350507 /* SearchOperation.swift */; }; 973BCCEC1CEB3FA400F10B44 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BCCEB1CEB3FA400F10B44 /* AppDelegate.swift */; }; 973BCCF31CEB3FA400F10B44 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 973BCCF21CEB3FA400F10B44 /* Assets.xcassets */; }; 973BCD011CEB3FA500F10B44 /* Kiwix_OSXTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BCD001CEB3FA500F10B44 /* Kiwix_OSXTests.swift */; }; @@ -142,8 +143,6 @@ 975B90F91CEB75CB00D13906 /* ZimFilesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975B90F81CEB75CB00D13906 /* ZimFilesController.swift */; }; 975B90FE1CEB909100D13906 /* iOSExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975B90FD1CEB909100D13906 /* iOSExtensions.swift */; }; 975B90FF1CEB909900D13906 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779987A1C1E1C9600B1DD5E /* Extensions.swift */; }; - 975B91001CEB983E00D13906 /* RefreshLibraryOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970103F91C6824FA00DC48F6 /* RefreshLibraryOperation.swift */; }; - 975B91011CEB984C00D13906 /* GlobalOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97DF23541CE807A1003E1E5A /* GlobalOperationQueue.swift */; }; 975B912F1CEB9B0F00D13906 /* Preference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973C8D5B1C25F945007272F9 /* Preference.swift */; }; 9763A6291CEB9E55008A2718 /* OSXExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9763A6281CEB9E55008A2718 /* OSXExtensions.swift */; }; 9763A62A1CEBA4F9008A2718 /* ZIMMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97254FDE1C2644560056950B /* ZIMMultiReader.swift */; }; @@ -158,6 +157,12 @@ 977998771C1E0B7900B1DD5E /* Article+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977998711C1E0B7900B1DD5E /* Article+CoreDataProperties.swift */; }; 977998781C1E0B7900B1DD5E /* Language+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977998721C1E0B7900B1DD5E /* Language+CoreDataProperties.swift */; }; 9779987B1C1E1C9600B1DD5E /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779987A1C1E1C9600B1DD5E /* Extensions.swift */; }; + 9779A1C81D34225E0071EFAB /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A1C21D34225E0071EFAB /* AlertOperation.swift */; }; + 9779A1C91D34225E0071EFAB /* GlobalOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A1C31D34225E0071EFAB /* GlobalOperationQueue.swift */; }; + 9779A1CA1D34225E0071EFAB /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A1C41D34225E0071EFAB /* NetworkObserver.swift */; }; + 9779A1CB1D34225E0071EFAB /* RefreshLibraryOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A1C51D34225E0071EFAB /* RefreshLibraryOperation.swift */; }; + 9779A1CC1D34225E0071EFAB /* SearchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A1C61D34225E0071EFAB /* SearchOperation.swift */; }; + 9779A1CD1D34225E0071EFAB /* UIOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A1C71D34225E0071EFAB /* UIOperations.swift */; }; 978C58961C1CD86E0077AE47 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978C588E1C1CD86E0077AE47 /* Language.swift */; }; 978C58981C1CD86E0077AE47 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978C58901C1CD86E0077AE47 /* Book.swift */; }; 978C589C1C1CD86E0077AE47 /* Article.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978C58941C1CD86E0077AE47 /* Article.swift */; }; @@ -165,98 +170,14 @@ 979C51531CECA9AF001707F2 /* StringTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979C51511CECA9AF001707F2 /* StringTools.swift */; }; 979C518D1CECAE4C001707F2 /* PreferenceWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979C518B1CECAE4C001707F2 /* PreferenceWindowController.swift */; }; 979CB60F1D04AD04005E1BA1 /* PreferenceTabController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB60E1D04AD04005E1BA1 /* PreferenceTabController.swift */; }; - 979CB6431D05C44F005E1BA1 /* CalendarCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6121D05C44F005E1BA1 /* CalendarCapability.swift */; }; - 979CB6441D05C44F005E1BA1 /* CalendarCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6121D05C44F005E1BA1 /* CalendarCapability.swift */; }; - 979CB6451D05C44F005E1BA1 /* Capability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6131D05C44F005E1BA1 /* Capability.swift */; }; - 979CB6461D05C44F005E1BA1 /* Capability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6131D05C44F005E1BA1 /* Capability.swift */; }; - 979CB6491D05C44F005E1BA1 /* iCloudContainerCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6151D05C44F005E1BA1 /* iCloudContainerCapability.swift */; }; - 979CB64A1D05C44F005E1BA1 /* iCloudContainerCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6151D05C44F005E1BA1 /* iCloudContainerCapability.swift */; }; - 979CB64B1D05C44F005E1BA1 /* LocationCapability-iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6161D05C44F005E1BA1 /* LocationCapability-iOS.swift */; }; - 979CB64C1D05C44F005E1BA1 /* LocationCapability-iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6161D05C44F005E1BA1 /* LocationCapability-iOS.swift */; }; - 979CB64D1D05C44F005E1BA1 /* LocationCapability-OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6171D05C44F005E1BA1 /* LocationCapability-OSX.swift */; }; - 979CB64E1D05C44F005E1BA1 /* LocationCapability-OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6171D05C44F005E1BA1 /* LocationCapability-OSX.swift */; }; - 979CB64F1D05C44F005E1BA1 /* LocationCapability-tvOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6181D05C44F005E1BA1 /* LocationCapability-tvOS.swift */; }; - 979CB6501D05C44F005E1BA1 /* LocationCapability-tvOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6181D05C44F005E1BA1 /* LocationCapability-tvOS.swift */; }; - 979CB6531D05C44F005E1BA1 /* PhotosCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61A1D05C44F005E1BA1 /* PhotosCapability.swift */; }; - 979CB6541D05C44F005E1BA1 /* PhotosCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61A1D05C44F005E1BA1 /* PhotosCapability.swift */; }; - 979CB6551D05C44F005E1BA1 /* PushCapability-iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61B1D05C44F005E1BA1 /* PushCapability-iOS.swift */; }; - 979CB6561D05C44F005E1BA1 /* PushCapability-iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61B1D05C44F005E1BA1 /* PushCapability-iOS.swift */; }; - 979CB6571D05C44F005E1BA1 /* PushCapability-OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61C1D05C44F005E1BA1 /* PushCapability-OSX.swift */; }; - 979CB6581D05C44F005E1BA1 /* PushCapability-OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61C1D05C44F005E1BA1 /* PushCapability-OSX.swift */; }; - 979CB6591D05C44F005E1BA1 /* UserNotificationCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61D1D05C44F005E1BA1 /* UserNotificationCapability.swift */; }; - 979CB65A1D05C44F005E1BA1 /* UserNotificationCapability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61D1D05C44F005E1BA1 /* UserNotificationCapability.swift */; }; - 979CB65B1D05C44F005E1BA1 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61F1D05C44F005E1BA1 /* CalendarCondition.swift */; }; - 979CB65C1D05C44F005E1BA1 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB61F1D05C44F005E1BA1 /* CalendarCondition.swift */; }; - 979CB65D1D05C44F005E1BA1 /* CloudCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6201D05C44F005E1BA1 /* CloudCondition.swift */; }; - 979CB65E1D05C44F005E1BA1 /* CloudCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6201D05C44F005E1BA1 /* CloudCondition.swift */; }; - 979CB65F1D05C44F005E1BA1 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6211D05C44F005E1BA1 /* HealthCondition.swift */; }; - 979CB6601D05C44F005E1BA1 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6211D05C44F005E1BA1 /* HealthCondition.swift */; }; - 979CB6611D05C44F005E1BA1 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6221D05C44F005E1BA1 /* LocationCondition.swift */; }; - 979CB6621D05C44F005E1BA1 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6221D05C44F005E1BA1 /* LocationCondition.swift */; }; - 979CB6631D05C44F005E1BA1 /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6231D05C44F005E1BA1 /* NegatedCondition.swift */; }; - 979CB6641D05C44F005E1BA1 /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6231D05C44F005E1BA1 /* NegatedCondition.swift */; }; - 979CB6651D05C44F005E1BA1 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6241D05C44F005E1BA1 /* NoCancelledDependencies.swift */; }; - 979CB6661D05C44F005E1BA1 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6241D05C44F005E1BA1 /* NoCancelledDependencies.swift */; }; - 979CB6671D05C44F005E1BA1 /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6251D05C44F005E1BA1 /* OperationCondition.swift */; }; - 979CB6681D05C44F005E1BA1 /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6251D05C44F005E1BA1 /* OperationCondition.swift */; }; - 979CB66B1D05C44F005E1BA1 /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6271D05C44F005E1BA1 /* PhotosCondition.swift */; }; - 979CB66C1D05C44F005E1BA1 /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6271D05C44F005E1BA1 /* PhotosCondition.swift */; }; - 979CB66D1D05C44F005E1BA1 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6281D05C44F005E1BA1 /* ReachabilityCondition.swift */; }; - 979CB66E1D05C44F005E1BA1 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6281D05C44F005E1BA1 /* ReachabilityCondition.swift */; }; - 979CB66F1D05C44F005E1BA1 /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6291D05C44F005E1BA1 /* RemoteNotificationCondition.swift */; }; - 979CB6701D05C44F005E1BA1 /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6291D05C44F005E1BA1 /* RemoteNotificationCondition.swift */; }; - 979CB6711D05C44F005E1BA1 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62A1D05C44F005E1BA1 /* SilentCondition.swift */; }; - 979CB6721D05C44F005E1BA1 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62A1D05C44F005E1BA1 /* SilentCondition.swift */; }; - 979CB6731D05C44F005E1BA1 /* UserNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62B1D05C44F005E1BA1 /* UserNotificationCondition.swift */; }; - 979CB6741D05C44F005E1BA1 /* UserNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62B1D05C44F005E1BA1 /* UserNotificationCondition.swift */; }; - 979CB6751D05C44F005E1BA1 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62D1D05C44F005E1BA1 /* ExclusivityController.swift */; }; - 979CB6761D05C44F005E1BA1 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62D1D05C44F005E1BA1 /* ExclusivityController.swift */; }; - 979CB6771D05C44F005E1BA1 /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62E1D05C44F005E1BA1 /* MutuallyExclusive.swift */; }; - 979CB6781D05C44F005E1BA1 /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB62E1D05C44F005E1BA1 /* MutuallyExclusive.swift */; }; - 979CB6791D05C44F005E1BA1 /* BlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6301D05C44F005E1BA1 /* BlockObserver.swift */; }; - 979CB67A1D05C44F005E1BA1 /* BlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6301D05C44F005E1BA1 /* BlockObserver.swift */; }; - 979CB67F1D05C44F005E1BA1 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6331D05C44F005E1BA1 /* NetworkObserver.swift */; }; - 979CB6831D05C44F005E1BA1 /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6351D05C44F005E1BA1 /* OperationObserver.swift */; }; - 979CB6841D05C44F005E1BA1 /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6351D05C44F005E1BA1 /* OperationObserver.swift */; }; - 979CB6871D05C44F005E1BA1 /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6371D05C44F005E1BA1 /* TimeoutObserver.swift */; }; - 979CB6881D05C44F005E1BA1 /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6371D05C44F005E1BA1 /* TimeoutObserver.swift */; }; - 979CB6AB1D05C520005E1BA1 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB69D1D05C520005E1BA1 /* AlertOperation.swift */; }; - 979CB6AD1D05C520005E1BA1 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB69E1D05C520005E1BA1 /* BlockOperation.swift */; }; - 979CB6AE1D05C520005E1BA1 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB69E1D05C520005E1BA1 /* BlockOperation.swift */; }; - 979CB6AF1D05C520005E1BA1 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB69F1D05C520005E1BA1 /* CKContainer+Operations.swift */; }; - 979CB6B01D05C520005E1BA1 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB69F1D05C520005E1BA1 /* CKContainer+Operations.swift */; }; - 979CB6B11D05C520005E1BA1 /* DelayOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A01D05C520005E1BA1 /* DelayOperation.swift */; }; - 979CB6B21D05C520005E1BA1 /* DelayOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A01D05C520005E1BA1 /* DelayOperation.swift */; }; - 979CB6B31D05C520005E1BA1 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A11D05C520005E1BA1 /* Dictionary+Operations.swift */; }; - 979CB6B41D05C520005E1BA1 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A11D05C520005E1BA1 /* Dictionary+Operations.swift */; }; - 979CB6B51D05C520005E1BA1 /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A21D05C520005E1BA1 /* GroupOperation.swift */; }; - 979CB6B61D05C520005E1BA1 /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A21D05C520005E1BA1 /* GroupOperation.swift */; }; - 979CB6B71D05C520005E1BA1 /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A31D05C520005E1BA1 /* LocationOperation.swift */; }; - 979CB6B81D05C520005E1BA1 /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A31D05C520005E1BA1 /* LocationOperation.swift */; }; - 979CB6B91D05C520005E1BA1 /* NSLock+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A41D05C520005E1BA1 /* NSLock+Operations.swift */; }; - 979CB6BA1D05C520005E1BA1 /* NSLock+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A41D05C520005E1BA1 /* NSLock+Operations.swift */; }; - 979CB6BB1D05C520005E1BA1 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A51D05C520005E1BA1 /* NSOperation+Operations.swift */; }; - 979CB6BC1D05C520005E1BA1 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A51D05C520005E1BA1 /* NSOperation+Operations.swift */; }; - 979CB6BD1D05C520005E1BA1 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A61D05C520005E1BA1 /* Operation.swift */; }; - 979CB6BE1D05C520005E1BA1 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A61D05C520005E1BA1 /* Operation.swift */; }; - 979CB6BF1D05C520005E1BA1 /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A71D05C520005E1BA1 /* OperationErrors.swift */; }; - 979CB6C01D05C520005E1BA1 /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A71D05C520005E1BA1 /* OperationErrors.swift */; }; - 979CB6C11D05C520005E1BA1 /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A81D05C520005E1BA1 /* OperationQueue.swift */; }; - 979CB6C21D05C520005E1BA1 /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A81D05C520005E1BA1 /* OperationQueue.swift */; }; - 979CB6C31D05C520005E1BA1 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A91D05C520005E1BA1 /* UIUserNotifications+Operations.swift */; }; - 979CB6C41D05C520005E1BA1 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6A91D05C520005E1BA1 /* UIUserNotifications+Operations.swift */; }; - 979CB6C51D05C520005E1BA1 /* URLSessionTaskOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6AA1D05C520005E1BA1 /* URLSessionTaskOperation.swift */; }; - 979CB6C61D05C520005E1BA1 /* URLSessionTaskOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6AA1D05C520005E1BA1 /* URLSessionTaskOperation.swift */; }; 979CB6C81D05CF37005E1BA1 /* SearchResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6C71D05CF37005E1BA1 /* SearchResultController.swift */; }; 979CB6CA1D05D26E005E1BA1 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6C91D05D26E005E1BA1 /* WebViewController.swift */; }; 97A7017F1D2C59CA00AAE2D8 /* GetStartedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A7017E1D2C59CA00AAE2D8 /* GetStartedController.swift */; }; 97A7140A1C274FCB00951244 /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A714091C274FCB00951244 /* DirectoryMonitor.swift */; }; - 97B50C7F1CA1E4810010BD79 /* UIOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97B50C7E1CA1E4810010BD79 /* UIOperations.swift */; }; 97BA32A51CEBC36300339A47 /* RootWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97BA32A31CEBC29500339A47 /* RootWindowController.swift */; }; 97D452BC1D16FF010033666F /* RecentSearchCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D452BB1D16FF010033666F /* RecentSearchCVC.swift */; }; 97D452BE1D1723FF0033666F /* CollectionViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D452BD1D1723FF0033666F /* CollectionViewCells.swift */; }; 97D55EF61D2075180081B523 /* TableOfContentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D55EF51D2075180081B523 /* TableOfContentsController.swift */; }; - 97DF23551CE807A1003E1E5A /* GlobalOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97DF23541CE807A1003E1E5A /* GlobalOperationQueue.swift */; }; 97E609F11D103DED00EBCB9D /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97E609F01D103DED00EBCB9D /* NotificationCenter.framework */; }; 97E609F41D103DED00EBCB9D /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97E609F31D103DED00EBCB9D /* TodayViewController.swift */; }; 97E609F71D103DED00EBCB9D /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97E609F51D103DED00EBCB9D /* MainInterface.storyboard */; }; @@ -265,8 +186,7 @@ 97E850CB1D2DA5B300A9F688 /* About.html in Resources */ = {isa = PBXBuildFile; fileRef = 97E850CA1D2DA5B300A9F688 /* About.html */; }; 97E891691CA976E90001CA32 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97E891681CA976E90001CA32 /* FileManager.swift */; }; 97F03CE21D2440470040D26E /* Search.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97F03CE11D2440470040D26E /* Search.storyboard */; }; - AEFF409D8D5B53BC90700424 /* Pods_Kiwix_OSX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DBAF14EE0505901A1570F23F /* Pods_Kiwix_OSX.framework */; }; - BECDBCEF4720E3E86FE63989 /* Pods_Kiwix_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CEA715EBFC96C75E73447A7 /* Pods_Kiwix_iOS.framework */; }; + EB1C0984A03359CD5CDC011C /* Pods_Kiwix_OSX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 198ECFA618CDD6B29CD462A0 /* Pods_Kiwix_OSX.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -334,12 +254,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0A4169C595E811A386A7FC63 /* Pods-Kiwix.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix.release.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix/Pods-Kiwix.release.xcconfig"; sourceTree = ""; }; - 6693AC18F86E44ABA71C3201 /* Pods_Kiwix.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Kiwix.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6CEA715EBFC96C75E73447A7 /* Pods_Kiwix_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Kiwix_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 86340F4A23677DE4554AE94B /* Pods-Kiwix-OSX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-OSX.release.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX.release.xcconfig"; sourceTree = ""; }; - 970103F91C6824FA00DC48F6 /* RefreshLibraryOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshLibraryOperation.swift; sourceTree = ""; }; + 198ECFA618CDD6B29CD462A0 /* Pods_Kiwix_OSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Kiwix_OSX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5B9300794E7F4EFE5B3E8F19 /* Pods-Kiwix-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS.release.xcconfig"; sourceTree = ""; }; + 69E75DFE2EF83272AC5B02BA /* Pods-Kiwix-OSX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-OSX.release.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX.release.xcconfig"; sourceTree = ""; }; + 6DCB0E958A1083CA248C5A12 /* Pods-Kiwix-OSX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-OSX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX.debug.xcconfig"; sourceTree = ""; }; 970C3DC91CBD79450026A240 /* MigrationPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MigrationPolicy.swift; path = CoreData/Migration/MigrationPolicy.swift; sourceTree = ""; }; + 970C61961D34243600087758 /* URLSessionDownloadTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = URLSessionDownloadTaskOperation.swift; path = Operations/URLSessionDownloadTaskOperation.swift; sourceTree = ""; }; + 970C61981D3429E400087758 /* ReachabilityCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityCondition.swift; sourceTree = ""; }; 971046301D19B96E002141C0 /* XapianSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XapianSearcher.h; path = Kiwix/libkiwix/XapianSearcher.h; sourceTree = ""; }; 971046311D19B96E002141C0 /* XapianSearcher.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = XapianSearcher.mm; path = Kiwix/libkiwix/XapianSearcher.mm; sourceTree = ""; }; 971046331D1C830C002141C0 /* SearchTuneController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchTuneController.swift; path = "Kiwix-iOS/Controller/Setting/SearchTuneController.swift"; sourceTree = SOURCE_ROOT; }; @@ -416,7 +337,6 @@ 971A107D1D022F74007FC62C /* Pic_P.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Pic_P.png; path = Kiwix/HelpDocuments/Pic_P.png; sourceTree = SOURCE_ROOT; }; 97254FDE1C2644560056950B /* ZIMMultiReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZIMMultiReader.swift; sourceTree = ""; }; 9734E54D1D289D060061C39B /* Welcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Welcome.storyboard; path = "Kiwix-iOS/Storyboard/Welcome.storyboard"; sourceTree = SOURCE_ROOT; }; - 973970EC1CB9A04C00350507 /* SearchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchOperation.swift; sourceTree = ""; }; 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; }; 973BCCF21CEB3FA400F10B44 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = "Kiwix-OSX/Assets.xcassets"; sourceTree = SOURCE_ROOT; }; @@ -470,57 +390,18 @@ 977998711C1E0B7900B1DD5E /* Article+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Article+CoreDataProperties.swift"; path = "Kiwix/CoreData/Article+CoreDataProperties.swift"; sourceTree = ""; }; 977998721C1E0B7900B1DD5E /* Language+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Language+CoreDataProperties.swift"; path = "Kiwix/CoreData/Language+CoreDataProperties.swift"; sourceTree = ""; }; 9779987A1C1E1C9600B1DD5E /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Kiwix/Extensions.swift; sourceTree = ""; }; + 9779A1C21D34225E0071EFAB /* AlertOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AlertOperation.swift; path = Operations/AlertOperation.swift; sourceTree = ""; }; + 9779A1C31D34225E0071EFAB /* GlobalOperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GlobalOperationQueue.swift; path = Operations/GlobalOperationQueue.swift; sourceTree = ""; }; + 9779A1C41D34225E0071EFAB /* NetworkObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkObserver.swift; path = Operations/NetworkObserver.swift; sourceTree = ""; }; + 9779A1C51D34225E0071EFAB /* RefreshLibraryOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RefreshLibraryOperation.swift; path = Operations/RefreshLibraryOperation.swift; sourceTree = ""; }; + 9779A1C61D34225E0071EFAB /* SearchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchOperation.swift; path = Operations/SearchOperation.swift; sourceTree = ""; }; + 9779A1C71D34225E0071EFAB /* UIOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIOperations.swift; path = Operations/UIOperations.swift; sourceTree = ""; }; 978C588E1C1CD86E0077AE47 /* Language.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Language.swift; path = Kiwix/CoreData/Language.swift; sourceTree = ""; }; 978C58901C1CD86E0077AE47 /* Book.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = Book.swift; path = Kiwix/CoreData/Book.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 978C58941C1CD86E0077AE47 /* Article.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Article.swift; path = Kiwix/CoreData/Article.swift; sourceTree = ""; }; 979C51511CECA9AF001707F2 /* StringTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringTools.swift; path = Kiwix/StringTools.swift; sourceTree = ""; }; 979C518B1CECAE4C001707F2 /* PreferenceWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferenceWindowController.swift; path = "Kiwix-OSX/Controllers/PreferenceWindowController.swift"; sourceTree = SOURCE_ROOT; }; 979CB60E1D04AD04005E1BA1 /* PreferenceTabController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferenceTabController.swift; path = Controllers/PreferenceTabController.swift; sourceTree = ""; }; - 979CB6121D05C44F005E1BA1 /* CalendarCapability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarCapability.swift; sourceTree = ""; }; - 979CB6131D05C44F005E1BA1 /* Capability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Capability.swift; sourceTree = ""; }; - 979CB6141D05C44F005E1BA1 /* HealthCapability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthCapability.swift; sourceTree = ""; }; - 979CB6151D05C44F005E1BA1 /* iCloudContainerCapability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iCloudContainerCapability.swift; sourceTree = ""; }; - 979CB6161D05C44F005E1BA1 /* LocationCapability-iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LocationCapability-iOS.swift"; sourceTree = ""; }; - 979CB6171D05C44F005E1BA1 /* LocationCapability-OSX.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LocationCapability-OSX.swift"; sourceTree = ""; }; - 979CB6181D05C44F005E1BA1 /* LocationCapability-tvOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LocationCapability-tvOS.swift"; sourceTree = ""; }; - 979CB6191D05C44F005E1BA1 /* PassbookCapability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCapability.swift; sourceTree = ""; }; - 979CB61A1D05C44F005E1BA1 /* PhotosCapability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosCapability.swift; sourceTree = ""; }; - 979CB61B1D05C44F005E1BA1 /* PushCapability-iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PushCapability-iOS.swift"; sourceTree = ""; }; - 979CB61C1D05C44F005E1BA1 /* PushCapability-OSX.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PushCapability-OSX.swift"; sourceTree = ""; }; - 979CB61D1D05C44F005E1BA1 /* UserNotificationCapability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotificationCapability.swift; sourceTree = ""; }; - 979CB61F1D05C44F005E1BA1 /* CalendarCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarCondition.swift; sourceTree = ""; }; - 979CB6201D05C44F005E1BA1 /* CloudCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudCondition.swift; sourceTree = ""; }; - 979CB6211D05C44F005E1BA1 /* HealthCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthCondition.swift; sourceTree = ""; }; - 979CB6221D05C44F005E1BA1 /* LocationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationCondition.swift; sourceTree = ""; }; - 979CB6231D05C44F005E1BA1 /* NegatedCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NegatedCondition.swift; sourceTree = ""; }; - 979CB6241D05C44F005E1BA1 /* NoCancelledDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoCancelledDependencies.swift; sourceTree = ""; }; - 979CB6251D05C44F005E1BA1 /* OperationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationCondition.swift; sourceTree = ""; }; - 979CB6261D05C44F005E1BA1 /* PassbookCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCondition.swift; sourceTree = ""; }; - 979CB6271D05C44F005E1BA1 /* PhotosCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosCondition.swift; sourceTree = ""; }; - 979CB6281D05C44F005E1BA1 /* ReachabilityCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityCondition.swift; sourceTree = ""; }; - 979CB6291D05C44F005E1BA1 /* RemoteNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteNotificationCondition.swift; sourceTree = ""; }; - 979CB62A1D05C44F005E1BA1 /* SilentCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SilentCondition.swift; sourceTree = ""; }; - 979CB62B1D05C44F005E1BA1 /* UserNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotificationCondition.swift; sourceTree = ""; }; - 979CB62D1D05C44F005E1BA1 /* ExclusivityController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExclusivityController.swift; sourceTree = ""; }; - 979CB62E1D05C44F005E1BA1 /* MutuallyExclusive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = ""; }; - 979CB6301D05C44F005E1BA1 /* BlockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockObserver.swift; sourceTree = ""; }; - 979CB6331D05C44F005E1BA1 /* NetworkObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = ""; }; - 979CB6351D05C44F005E1BA1 /* OperationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationObserver.swift; sourceTree = ""; }; - 979CB6371D05C44F005E1BA1 /* TimeoutObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeoutObserver.swift; sourceTree = ""; }; - 979CB69D1D05C520005E1BA1 /* AlertOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = ""; }; - 979CB69E1D05C520005E1BA1 /* BlockOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockOperation.swift; sourceTree = ""; }; - 979CB69F1D05C520005E1BA1 /* CKContainer+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKContainer+Operations.swift"; sourceTree = ""; }; - 979CB6A01D05C520005E1BA1 /* DelayOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelayOperation.swift; sourceTree = ""; }; - 979CB6A11D05C520005E1BA1 /* Dictionary+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Operations.swift"; sourceTree = ""; }; - 979CB6A21D05C520005E1BA1 /* GroupOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = ""; }; - 979CB6A31D05C520005E1BA1 /* LocationOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationOperation.swift; sourceTree = ""; }; - 979CB6A41D05C520005E1BA1 /* NSLock+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSLock+Operations.swift"; sourceTree = ""; }; - 979CB6A51D05C520005E1BA1 /* NSOperation+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSOperation+Operations.swift"; sourceTree = ""; }; - 979CB6A61D05C520005E1BA1 /* Operation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; - 979CB6A71D05C520005E1BA1 /* OperationErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationErrors.swift; sourceTree = ""; }; - 979CB6A81D05C520005E1BA1 /* OperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; - 979CB6A91D05C520005E1BA1 /* UIUserNotifications+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIUserNotifications+Operations.swift"; sourceTree = ""; }; - 979CB6AA1D05C520005E1BA1 /* URLSessionTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionTaskOperation.swift; sourceTree = ""; }; 979CB6C71D05CF37005E1BA1 /* SearchResultController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchResultController.swift; path = Controllers/SearchResultController.swift; sourceTree = ""; }; 979CB6C91D05D26E005E1BA1 /* WebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebViewController.swift; path = Controllers/WebViewController.swift; sourceTree = ""; }; 97A2AB881C1B80FF00052E74 /* Kiwix.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Kiwix.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -528,14 +409,12 @@ 97A2ABAA1C1B810000052E74 /* Kiwix-iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Kiwix-iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 97A7017E1D2C59CA00AAE2D8 /* GetStartedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GetStartedController.swift; path = "Kiwix-iOS/Controller/Welcome/GetStartedController.swift"; sourceTree = SOURCE_ROOT; }; 97A714091C274FCB00951244 /* DirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = ""; }; - 97B50C7E1CA1E4810010BD79 /* UIOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIOperations.swift; sourceTree = ""; }; 97BA32A31CEBC29500339A47 /* RootWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RootWindowController.swift; path = "Kiwix-OSX/Controllers/RootWindowController.swift"; sourceTree = SOURCE_ROOT; }; 97D452BB1D16FF010033666F /* RecentSearchCVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RecentSearchCVC.swift; path = "Kiwix-iOS/Controller/Search/RecentSearchCVC.swift"; sourceTree = SOURCE_ROOT; }; 97D452BD1D1723FF0033666F /* CollectionViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCells.swift; sourceTree = ""; }; 97D452C01D1871E70033666F /* SearchLocalBooksCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SearchLocalBooksCVC.swift; path = "Kiwix-iOS/Controller/Search/SearchLocalBooksCVC.swift"; sourceTree = SOURCE_ROOT; }; 97D452C11D1871E70033666F /* SearchTabController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SearchTabController.swift; path = "Kiwix-iOS/Controller/Search/SearchTabController.swift"; sourceTree = SOURCE_ROOT; }; 97D55EF51D2075180081B523 /* TableOfContentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsController.swift; path = "Kiwix-iOS/Controller/TableOfContentsController.swift"; sourceTree = SOURCE_ROOT; }; - 97DF23541CE807A1003E1E5A /* GlobalOperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalOperationQueue.swift; sourceTree = ""; }; 97E609EF1D103DED00EBCB9D /* Kiwix-iOSWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Kiwix-iOSWidget.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 97E609F01D103DED00EBCB9D /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; 97E609F31D103DED00EBCB9D /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; @@ -546,11 +425,8 @@ 97E850CA1D2DA5B300A9F688 /* About.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = About.html; path = Kiwix/HelpDocuments/About.html; sourceTree = SOURCE_ROOT; }; 97E891681CA976E90001CA32 /* FileManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FileManager.swift; path = Kiwix/FileManager.swift; sourceTree = ""; }; 97F03CE11D2440470040D26E /* Search.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Search.storyboard; path = "Kiwix-iOS/Storyboard/Search.storyboard"; sourceTree = SOURCE_ROOT; }; - B14E5F8A97964014F99EAD4E /* Pods-Kiwix-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS.debug.xcconfig"; sourceTree = ""; }; - B3B41D59F4B010C559B18D3D /* Pods-Kiwix-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS.release.xcconfig"; sourceTree = ""; }; - DBAF14EE0505901A1570F23F /* Pods_Kiwix_OSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Kiwix_OSX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - E0C40F7192D36BEA96D12C9C /* Pods-Kiwix-OSX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-OSX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX.debug.xcconfig"; sourceTree = ""; }; - E46C279B8DE7F234682956CD /* Pods-Kiwix.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix/Pods-Kiwix.debug.xcconfig"; sourceTree = ""; }; + DA0AB5D61F19BE37BD0AFA0A /* Pods-Kiwix-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kiwix-iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS.debug.xcconfig"; sourceTree = ""; }; + EC884ACBBA260AF741C4C4FE /* Pods_Kiwix_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Kiwix_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -571,7 +447,7 @@ 971187661CEB512F00B9909D /* libzim.0.dylib in Frameworks */, 9711875E1CEB512F00B9909D /* libicuio.55.dylib in Frameworks */, 971187601CEB512F00B9909D /* libiculx.55.dylib in Frameworks */, - AEFF409D8D5B53BC90700424 /* Pods_Kiwix_OSX.framework in Frameworks */, + EB1C0984A03359CD5CDC011C /* Pods_Kiwix_OSX.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -605,7 +481,7 @@ 9711874C1CEB512D00B9909D /* libicutu.a in Frameworks */, 9711874B1CEB512D00B9909D /* libicutest.a in Frameworks */, 9711874E1CEB512D00B9909D /* liblzma.a in Frameworks */, - BECDBCEF4720E3E86FE63989 /* Pods_Kiwix_iOS.framework in Frameworks */, + 7356F9FACBB84380CFC8F68F /* Pods_Kiwix_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -634,36 +510,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 784457131AE044F91977B64B /* Pods */ = { - isa = PBXGroup; - children = ( - E46C279B8DE7F234682956CD /* Pods-Kiwix.debug.xcconfig */, - 0A4169C595E811A386A7FC63 /* Pods-Kiwix.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; 931D277C156AE671D3F9EADA /* Frameworks */ = { isa = PBXGroup; children = ( - DBAF14EE0505901A1570F23F /* Pods_Kiwix_OSX.framework */, - 6CEA715EBFC96C75E73447A7 /* Pods_Kiwix_iOS.framework */, 97E609F01D103DED00EBCB9D /* NotificationCenter.framework */, + 198ECFA618CDD6B29CD462A0 /* Pods_Kiwix_OSX.framework */, + EC884ACBBA260AF741C4C4FE /* Pods_Kiwix_iOS.framework */, ); name = Frameworks; sourceTree = ""; }; - 9675A05DC9EAC986274652A8 /* Pods */ = { - isa = PBXGroup; - children = ( - E0C40F7192D36BEA96D12C9C /* Pods-Kiwix-OSX.debug.xcconfig */, - 86340F4A23677DE4554AE94B /* Pods-Kiwix-OSX.release.xcconfig */, - B14E5F8A97964014F99EAD4E /* Pods-Kiwix-iOS.debug.xcconfig */, - B3B41D59F4B010C559B18D3D /* Pods-Kiwix-iOS.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; 971186ED1CEB424800B9909D /* shared */ = { isa = PBXGroup; children = ( @@ -886,7 +742,6 @@ 973BCD1F1CEB412E00F10B44 /* Shared */ = { isa = PBXGroup; children = ( - 979CB6101D05C44F005E1BA1 /* OperationLib */, 97163D321CD7E79F008BE2D6 /* Help Docs */, 97E5712A1CA0525300FF4F1D /* Operation */, 9711872D1CEB507600B9909D /* CoreData */, @@ -953,7 +808,6 @@ 973BCD0A1CEB3FA500F10B44 /* Kiwix-OSXUITests */, 97A2ABA21C1B80FF00052E74 /* Kiwix-iOSTests */, 97A2ABAD1C1B810000052E74 /* Kiwix-iOSUITests */, - 784457131AE044F91977B64B /* Pods */, CFDF64EC6F9DB861DEEB91A0 /* Frameworks */, ); name = Others; @@ -1068,99 +922,6 @@ name = Properties; sourceTree = ""; }; - 979CB6101D05C44F005E1BA1 /* OperationLib */ = { - isa = PBXGroup; - children = ( - 979CB6111D05C44F005E1BA1 /* Capability */, - 979CB61E1D05C44F005E1BA1 /* Conditions */, - 979CB62C1D05C44F005E1BA1 /* Exclusive */, - 979CB62F1D05C44F005E1BA1 /* Observer */, - 979CB63A1D05C44F005E1BA1 /* Operations */, - ); - name = OperationLib; - path = Kiwix/OperationLib; - sourceTree = ""; - }; - 979CB6111D05C44F005E1BA1 /* Capability */ = { - isa = PBXGroup; - children = ( - 979CB6121D05C44F005E1BA1 /* CalendarCapability.swift */, - 979CB6131D05C44F005E1BA1 /* Capability.swift */, - 979CB6141D05C44F005E1BA1 /* HealthCapability.swift */, - 979CB6151D05C44F005E1BA1 /* iCloudContainerCapability.swift */, - 979CB6161D05C44F005E1BA1 /* LocationCapability-iOS.swift */, - 979CB6171D05C44F005E1BA1 /* LocationCapability-OSX.swift */, - 979CB6181D05C44F005E1BA1 /* LocationCapability-tvOS.swift */, - 979CB6191D05C44F005E1BA1 /* PassbookCapability.swift */, - 979CB61A1D05C44F005E1BA1 /* PhotosCapability.swift */, - 979CB61B1D05C44F005E1BA1 /* PushCapability-iOS.swift */, - 979CB61C1D05C44F005E1BA1 /* PushCapability-OSX.swift */, - 979CB61D1D05C44F005E1BA1 /* UserNotificationCapability.swift */, - ); - path = Capability; - sourceTree = ""; - }; - 979CB61E1D05C44F005E1BA1 /* Conditions */ = { - isa = PBXGroup; - children = ( - 979CB61F1D05C44F005E1BA1 /* CalendarCondition.swift */, - 979CB6201D05C44F005E1BA1 /* CloudCondition.swift */, - 979CB6211D05C44F005E1BA1 /* HealthCondition.swift */, - 979CB6221D05C44F005E1BA1 /* LocationCondition.swift */, - 979CB6231D05C44F005E1BA1 /* NegatedCondition.swift */, - 979CB6241D05C44F005E1BA1 /* NoCancelledDependencies.swift */, - 979CB6251D05C44F005E1BA1 /* OperationCondition.swift */, - 979CB6261D05C44F005E1BA1 /* PassbookCondition.swift */, - 979CB6271D05C44F005E1BA1 /* PhotosCondition.swift */, - 979CB6281D05C44F005E1BA1 /* ReachabilityCondition.swift */, - 979CB6291D05C44F005E1BA1 /* RemoteNotificationCondition.swift */, - 979CB62A1D05C44F005E1BA1 /* SilentCondition.swift */, - 979CB62B1D05C44F005E1BA1 /* UserNotificationCondition.swift */, - ); - path = Conditions; - sourceTree = ""; - }; - 979CB62C1D05C44F005E1BA1 /* Exclusive */ = { - isa = PBXGroup; - children = ( - 979CB62D1D05C44F005E1BA1 /* ExclusivityController.swift */, - 979CB62E1D05C44F005E1BA1 /* MutuallyExclusive.swift */, - ); - path = Exclusive; - sourceTree = ""; - }; - 979CB62F1D05C44F005E1BA1 /* Observer */ = { - isa = PBXGroup; - children = ( - 979CB6301D05C44F005E1BA1 /* BlockObserver.swift */, - 979CB6331D05C44F005E1BA1 /* NetworkObserver.swift */, - 979CB6351D05C44F005E1BA1 /* OperationObserver.swift */, - 979CB6371D05C44F005E1BA1 /* TimeoutObserver.swift */, - ); - path = Observer; - sourceTree = ""; - }; - 979CB63A1D05C44F005E1BA1 /* Operations */ = { - isa = PBXGroup; - children = ( - 979CB69D1D05C520005E1BA1 /* AlertOperation.swift */, - 979CB69E1D05C520005E1BA1 /* BlockOperation.swift */, - 979CB69F1D05C520005E1BA1 /* CKContainer+Operations.swift */, - 979CB6A01D05C520005E1BA1 /* DelayOperation.swift */, - 979CB6A11D05C520005E1BA1 /* Dictionary+Operations.swift */, - 979CB6A21D05C520005E1BA1 /* GroupOperation.swift */, - 979CB6A31D05C520005E1BA1 /* LocationOperation.swift */, - 979CB6A41D05C520005E1BA1 /* NSLock+Operations.swift */, - 979CB6A51D05C520005E1BA1 /* NSOperation+Operations.swift */, - 979CB6A61D05C520005E1BA1 /* Operation.swift */, - 979CB6A71D05C520005E1BA1 /* OperationErrors.swift */, - 979CB6A81D05C520005E1BA1 /* OperationQueue.swift */, - 979CB6A91D05C520005E1BA1 /* UIUserNotifications+Operations.swift */, - 979CB6AA1D05C520005E1BA1 /* URLSessionTaskOperation.swift */, - ); - path = Operations; - sourceTree = ""; - }; 97A2AB7F1C1B80FF00052E74 = { isa = PBXGroup; children = ( @@ -1169,8 +930,8 @@ 973BCCE41CEB3EDE00F10B44 /* iOS */, 97E609F21D103DED00EBCB9D /* iOSWidget */, 975334CD1CEB6A78007ED50B /* Others */, - 9675A05DC9EAC986274652A8 /* Pods */, 931D277C156AE671D3F9EADA /* Frameworks */, + F544A4E4357A6D33CF234C6B /* Pods */, ); sourceTree = ""; }; @@ -1256,10 +1017,14 @@ 97E5712A1CA0525300FF4F1D /* Operation */ = { isa = PBXGroup; children = ( - 97DF23541CE807A1003E1E5A /* GlobalOperationQueue.swift */, - 970103F91C6824FA00DC48F6 /* RefreshLibraryOperation.swift */, - 97B50C7E1CA1E4810010BD79 /* UIOperations.swift */, - 973970EC1CB9A04C00350507 /* SearchOperation.swift */, + 9779A1C21D34225E0071EFAB /* AlertOperation.swift */, + 9779A1C31D34225E0071EFAB /* GlobalOperationQueue.swift */, + 9779A1C41D34225E0071EFAB /* NetworkObserver.swift */, + 970C61981D3429E400087758 /* ReachabilityCondition.swift */, + 9779A1C51D34225E0071EFAB /* RefreshLibraryOperation.swift */, + 9779A1C61D34225E0071EFAB /* SearchOperation.swift */, + 9779A1C71D34225E0071EFAB /* UIOperations.swift */, + 970C61961D34243600087758 /* URLSessionDownloadTaskOperation.swift */, ); name = Operation; path = Kiwix; @@ -1282,11 +1047,21 @@ 971187A81CEB694400B9909D /* WebKit.framework */, 9711871D1CEB449A00B9909D /* libz.tbd */, 9711871B1CEB448400B9909D /* libz.tbd */, - 6693AC18F86E44ABA71C3201 /* Pods_Kiwix.framework */, ); name = Frameworks; sourceTree = ""; }; + F544A4E4357A6D33CF234C6B /* Pods */ = { + isa = PBXGroup; + children = ( + 6DCB0E958A1083CA248C5A12 /* Pods-Kiwix-OSX.debug.xcconfig */, + 69E75DFE2EF83272AC5B02BA /* Pods-Kiwix-OSX.release.xcconfig */, + DA0AB5D61F19BE37BD0AFA0A /* Pods-Kiwix-iOS.debug.xcconfig */, + 5B9300794E7F4EFE5B3E8F19 /* Pods-Kiwix-iOS.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1294,14 +1069,14 @@ isa = PBXNativeTarget; buildConfigurationList = 973BCD0E1CEB3FA500F10B44 /* Build configuration list for PBXNativeTarget "Kiwix-OSX" */; buildPhases = ( - 7C9B30F13CC8E5C60BDDB904 /* Check Pods Manifest.lock */, + 99B6C5B82907BC9766F6107C /* [CP] Check Pods Manifest.lock */, 973BCCE51CEB3FA400F10B44 /* Sources */, 973BCCE61CEB3FA400F10B44 /* Frameworks */, 973BCCE71CEB3FA400F10B44 /* Resources */, 971187211CEB4E6900B9909D /* CopyFiles */, - 6A54D44911BF3FD6B4D6B8BC /* Embed Pods Frameworks */, - BE28BFF3CC54843446BB8F2E /* Copy Pods Resources */, 97A4D7E31CF8C479006C731E /* ShellScript */, + 6386DDFA463FC9F4EBE2E419 /* [CP] Embed Pods Frameworks */, + CB1EEC1866DA4DFDA47B796E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1352,14 +1127,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97A2ABB31C1B810000052E74 /* Build configuration list for PBXNativeTarget "Kiwix-iOS" */; buildPhases = ( - 631D6CA2AC2D4ECC158B9734 /* Check Pods Manifest.lock */, + CDD21917A84F651D4CEFF569 /* [CP] Check Pods Manifest.lock */, 97A2AB841C1B80FF00052E74 /* Sources */, 97A2AB851C1B80FF00052E74 /* Frameworks */, 97A2AB861C1B80FF00052E74 /* Resources */, - A0BA34333D8534C017C21288 /* Embed Pods Frameworks */, - 0DCB994930DA1CBACABC8118 /* Copy Pods Resources */, 97A4D7E21CF8C318006C731E /* ShellScript */, 97E609FF1D103DED00EBCB9D /* Embed App Extensions */, + 7D32C417880EE3A4FFF4017C /* [CP] Embed Pods Frameworks */, + 20E4CC4E024923B9E858CC06 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1568,14 +1343,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0DCB994930DA1CBACABC8118 /* Copy Pods Resources */ = { + 20E4CC4E024923B9E858CC06 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1583,105 +1358,105 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 631D6CA2AC2D4ECC158B9734 /* Check Pods Manifest.lock */ = { + 6386DDFA463FC9F4EBE2E419 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 6A54D44911BF3FD6B4D6B8BC /* Embed Pods Frameworks */ = { + 7D32C417880EE3A4FFF4017C /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 7C9B30F13CC8E5C60BDDB904 /* Check Pods Manifest.lock */ = { + 97A4D7E21CF8C318006C731E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; - showEnvVarsInLog = 0; + shellScript = "#!/bin/bash\nbuildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$INFOPLIST_FILE\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\""; }; - 97A4D7E21CF8C318006C731E /* ShellScript */ = { + 97A4D7E31CF8C479006C731E /* ShellScript */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 8; + buildActionMask = 12; files = ( ); inputPaths = ( ); outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 1; + runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "#!/bin/bash\nbuildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$INFOPLIST_FILE\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\""; }; - 97A4D7E31CF8C479006C731E /* ShellScript */ = { + 99B6C5B82907BC9766F6107C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 12; + buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/bash\nbuildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$INFOPLIST_FILE\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\""; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; }; - A0BA34333D8534C017C21288 /* Embed Pods Frameworks */ = { + CB1EEC1866DA4DFDA47B796E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-iOS/Pods-Kiwix-iOS-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX-resources.sh\"\n"; showEnvVarsInLog = 0; }; - BE28BFF3CC54843446BB8F2E /* Copy Pods Resources */ = { + CDD21917A84F651D4CEFF569 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Kiwix-OSX/Pods-Kiwix-OSX-resources.sh\"\n"; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1693,14 +1468,8 @@ files = ( 9752279B1D020C00001D1DDE /* otherTools.cpp in Sources */, 975B912F1CEB9B0F00D13906 /* Preference.swift in Sources */, - 979CB6681D05C44F005E1BA1 /* OperationCondition.swift in Sources */, - 979CB6441D05C44F005E1BA1 /* CalendarCapability.swift in Sources */, 971187941CEB541A00B9909D /* Article.swift in Sources */, - 979CB6881D05C44F005E1BA1 /* TimeoutObserver.swift in Sources */, 9763A62B1CEBA4F9008A2718 /* ZIMMultiReaderAPI.swift in Sources */, - 979CB6AE1D05C520005E1BA1 /* BlockOperation.swift in Sources */, - 979CB66C1D05C44F005E1BA1 /* PhotosCondition.swift in Sources */, - 979CB65E1D05C44F005E1BA1 /* CloudCondition.swift in Sources */, 9752279D1D020C00001D1DDE /* pathTools.cpp in Sources */, 975227A91D020C2E001D1DDE /* myhtmlparse.cc in Sources */, 975227B01D021539001D1DDE /* IndexerController.swift in Sources */, @@ -1710,70 +1479,34 @@ 971187981CEB542500B9909D /* Language+CoreDataProperties.swift in Sources */, 9752279F1D020C00001D1DDE /* reader.cpp in Sources */, 97BA32A51CEBC36300339A47 /* RootWindowController.swift in Sources */, - 979CB64C1D05C44F005E1BA1 /* LocationCapability-iOS.swift in Sources */, - 979CB6541D05C44F005E1BA1 /* PhotosCapability.swift in Sources */, 979CB6CA1D05D26E005E1BA1 /* WebViewController.swift in Sources */, 9711878E1CEB541100B9909D /* Kiwix.xcdatamodeld in Sources */, 971187961CEB542500B9909D /* DownloadTask+CoreDataProperties.swift in Sources */, - 979CB64A1D05C44F005E1BA1 /* iCloudContainerCapability.swift in Sources */, - 979CB6C01D05C520005E1BA1 /* OperationErrors.swift in Sources */, 975334D21CEB6B01007ED50B /* LibraryController.swift in Sources */, 975227AC1D020D83001D1DDE /* ZimIndexer.mm in Sources */, - 979CB6461D05C44F005E1BA1 /* Capability.swift in Sources */, - 979CB6581D05C44F005E1BA1 /* PushCapability-OSX.swift in Sources */, 975227A11D020C00001D1DDE /* resourceTools.cpp in Sources */, - 979CB6701D05C44F005E1BA1 /* RemoteNotificationCondition.swift in Sources */, - 979CB6B61D05C520005E1BA1 /* GroupOperation.swift in Sources */, 975227A81D020C2E001D1DDE /* htmlparse.cc in Sources */, - 979CB67A1D05C44F005E1BA1 /* BlockObserver.swift in Sources */, 971187971CEB542500B9909D /* Article+CoreDataProperties.swift in Sources */, - 979CB6741D05C44F005E1BA1 /* UserNotificationCondition.swift in Sources */, 975227A31D020C00001D1DDE /* stringTools.cpp in Sources */, - 979CB6601D05C44F005E1BA1 /* HealthCondition.swift in Sources */, 977305361D0DFD110081B8F0 /* KiwixURLProtocol.swift in Sources */, - 979CB6721D05C44F005E1BA1 /* SilentCondition.swift in Sources */, - 979CB65A1D05C44F005E1BA1 /* UserNotificationCapability.swift in Sources */, - 979CB6B21D05C520005E1BA1 /* DelayOperation.swift in Sources */, - 979CB6761D05C44F005E1BA1 /* ExclusivityController.swift in Sources */, 9763A62A1CEBA4F9008A2718 /* ZIMMultiReader.swift in Sources */, - 979CB64E1D05C44F005E1BA1 /* LocationCapability-OSX.swift in Sources */, - 979CB6BC1D05C520005E1BA1 /* NSOperation+Operations.swift in Sources */, 979C518D1CECAE4C001707F2 /* PreferenceWindowController.swift in Sources */, - 979CB6C21D05C520005E1BA1 /* OperationQueue.swift in Sources */, - 979CB6561D05C44F005E1BA1 /* PushCapability-iOS.swift in Sources */, - 979CB6BE1D05C520005E1BA1 /* Operation.swift in Sources */, 9763A62C1CEBA4F9008A2718 /* DirectoryMonitor.swift in Sources */, 9711879B1CEB546C00B9909D /* CoreDataExtension.swift in Sources */, 979C51531CECA9AF001707F2 /* StringTools.swift in Sources */, 971187931CEB541A00B9909D /* DownloadTask.swift in Sources */, 9711878F1CEB541600B9909D /* 1.5.xcmappingmodel in Sources */, - 979CB65C1D05C44F005E1BA1 /* CalendarCondition.swift in Sources */, 9763A62D1CEBA524008A2718 /* FileManager.swift in Sources */, - 979CB6B81D05C520005E1BA1 /* LocationOperation.swift in Sources */, 973BCCEC1CEB3FA400F10B44 /* AppDelegate.swift in Sources */, 971187311CEB50FC00B9909D /* ZimReader.mm in Sources */, - 979CB66E1D05C44F005E1BA1 /* ReachabilityCondition.swift in Sources */, - 979CB6641D05C44F005E1BA1 /* NegatedCondition.swift in Sources */, 9763A6291CEB9E55008A2718 /* OSXExtensions.swift in Sources */, - 979CB6841D05C44F005E1BA1 /* OperationObserver.swift in Sources */, 975B90FF1CEB909900D13906 /* Extensions.swift in Sources */, 975227AE1D0213D3001D1DDE /* xapianIndexer.cpp in Sources */, - 979CB6B01D05C520005E1BA1 /* CKContainer+Operations.swift in Sources */, 971187911CEB541A00B9909D /* Book.swift in Sources */, - 979CB6C61D05C520005E1BA1 /* URLSessionTaskOperation.swift in Sources */, 971187901CEB541600B9909D /* MigrationPolicy.swift in Sources */, - 979CB6661D05C44F005E1BA1 /* NoCancelledDependencies.swift in Sources */, - 975B91011CEB984C00D13906 /* GlobalOperationQueue.swift in Sources */, - 979CB6781D05C44F005E1BA1 /* MutuallyExclusive.swift in Sources */, 971187951CEB542500B9909D /* Book+CoreDataProperties.swift in Sources */, - 979CB6621D05C44F005E1BA1 /* LocationCondition.swift in Sources */, 979CB60F1D04AD04005E1BA1 /* PreferenceTabController.swift in Sources */, - 979CB6B41D05C520005E1BA1 /* Dictionary+Operations.swift in Sources */, - 979CB6BA1D05C520005E1BA1 /* NSLock+Operations.swift in Sources */, - 975B91001CEB983E00D13906 /* RefreshLibraryOperation.swift in Sources */, 975227991D020C00001D1DDE /* indexer.cpp in Sources */, - 979CB6C41D05C520005E1BA1 /* UIUserNotifications+Operations.swift in Sources */, - 979CB6501D05C44F005E1BA1 /* LocationCapability-tvOS.swift in Sources */, 971187921CEB541A00B9909D /* Language.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1800,40 +1533,24 @@ files = ( 976AB2671CBD8B3D00B06EB0 /* 1.5.xcmappingmodel in Sources */, 971A10311D022AD5007FC62C /* RefreshHUD.swift in Sources */, - 973970ED1CB9A04C00350507 /* SearchOperation.swift in Sources */, - 979CB6BB1D05C520005E1BA1 /* NSOperation+Operations.swift in Sources */, - 979CB6AB1D05C520005E1BA1 /* AlertOperation.swift in Sources */, - 979CB6C31D05C520005E1BA1 /* UIUserNotifications+Operations.swift in Sources */, 971A102C1D022AD5007FC62C /* BarButtonItems.swift in Sources */, 971A10301D022AD5007FC62C /* LTBarButtonItem.swift in Sources */, 971A103F1D022C42007FC62C /* LibraryAutoRefreshTBVC.swift in Sources */, - 979CB67F1D05C44F005E1BA1 /* NetworkObserver.swift in Sources */, - 979CB6C51D05C520005E1BA1 /* URLSessionTaskOperation.swift in Sources */, - 979CB65B1D05C44F005E1BA1 /* CalendarCondition.swift in Sources */, 971A10721D022E74007FC62C /* KiwixURLProtocol.swift in Sources */, 971A10581D022DAD007FC62C /* LibraryDownloadTBVC.swift in Sources */, 97A7017F1D2C59CA00AAE2D8 /* GetStartedController.swift in Sources */, 979C51521CECA9AF001707F2 /* StringTools.swift in Sources */, + 9779A1CD1D34225E0071EFAB /* UIOperations.swift in Sources */, 9711878D1CEB541100B9909D /* Kiwix.xcdatamodeld in Sources */, - 979CB6731D05C44F005E1BA1 /* UserNotificationCondition.swift in Sources */, - 979CB66F1D05C44F005E1BA1 /* RemoteNotificationCondition.swift in Sources */, - 979CB66D1D05C44F005E1BA1 /* ReachabilityCondition.swift in Sources */, 9752279E1D020C00001D1DDE /* reader.cpp in Sources */, - 979CB6C11D05C520005E1BA1 /* OperationQueue.swift in Sources */, - 97DF23551CE807A1003E1E5A /* GlobalOperationQueue.swift in Sources */, - 979CB6711D05C44F005E1BA1 /* SilentCondition.swift in Sources */, 97D452BC1D16FF010033666F /* RecentSearchCVC.swift in Sources */, - 979CB6651D05C44F005E1BA1 /* NoCancelledDependencies.swift in Sources */, - 970103FB1C6824FA00DC48F6 /* RefreshLibraryOperation.swift in Sources */, 971A10651D022E0A007FC62C /* MainVC.swift in Sources */, 971A106F1D022E62007FC62C /* DownloadProgress.swift in Sources */, 971A102E1D022AD5007FC62C /* TableViewCells.swift in Sources */, - 979CB64F1D05C44F005E1BA1 /* LocationCapability-tvOS.swift in Sources */, - 979CB6591D05C44F005E1BA1 /* UserNotificationCapability.swift in Sources */, 97254FDF1C2644560056950B /* ZIMMultiReader.swift in Sources */, 971A105A1D022DAD007FC62C /* LibraryLocalTBVC.swift in Sources */, 97E60A021D10423A00EBCB9D /* ShadowView.swift in Sources */, - 979CB6BD1D05C520005E1BA1 /* Operation.swift in Sources */, + 9779A1CC1D34225E0071EFAB /* SearchOperation.swift in Sources */, 971A10671D022E0A007FC62C /* MainVCDelegates.swift in Sources */, 978C58981C1CD86E0077AE47 /* Book.swift in Sources */, 978C58961C1CD86E0077AE47 /* Language.swift in Sources */, @@ -1841,72 +1558,50 @@ 971A104A1D022CBE007FC62C /* SearchResultTBVC.swift in Sources */, 971046341D1C830C002141C0 /* SearchTuneController.swift in Sources */, 971A105B1D022DAD007FC62C /* LibraryOnlineTBVC.swift in Sources */, + 9779A1CB1D34225E0071EFAB /* RefreshLibraryOperation.swift in Sources */, 971A10321D022AD5007FC62C /* SearchBar.swift in Sources */, - 979CB65D1D05C44F005E1BA1 /* CloudCondition.swift in Sources */, - 979CB65F1D05C44F005E1BA1 /* HealthCondition.swift in Sources */, - 979CB6B51D05C520005E1BA1 /* GroupOperation.swift in Sources */, - 97B50C7F1CA1E4810010BD79 /* UIOperations.swift in Sources */, 977998741C1E0B7900B1DD5E /* Book+CoreDataProperties.swift in Sources */, - 979CB6871D05C44F005E1BA1 /* TimeoutObserver.swift in Sources */, 971A10661D022E0A007FC62C /* MainVCLoading.swift in Sources */, 977998751C1E0B7900B1DD5E /* DownloadTask+CoreDataProperties.swift in Sources */, - 979CB6531D05C44F005E1BA1 /* PhotosCapability.swift in Sources */, - 979CB6771D05C44F005E1BA1 /* MutuallyExclusive.swift in Sources */, 970C3DCA1CBD79450026A240 /* MigrationPolicy.swift in Sources */, 97E891691CA976E90001CA32 /* FileManager.swift in Sources */, 97E60A061D10504000EBCB9D /* LibraryBackupTBVC.swift in Sources */, + 9779A1CA1D34225E0071EFAB /* NetworkObserver.swift in Sources */, 974570F41C2DABB500680E43 /* ZIMMultiReaderAPI.swift in Sources */, 971A105C1D022DAD007FC62C /* LibraryTabBarController.swift in Sources */, 971A10461D022CB2007FC62C /* SearchController.swift in Sources */, 9779987B1C1E1C9600B1DD5E /* Extensions.swift in Sources */, - 979CB6551D05C44F005E1BA1 /* PushCapability-iOS.swift in Sources */, - 979CB6831D05C44F005E1BA1 /* OperationObserver.swift in Sources */, - 979CB6791D05C44F005E1BA1 /* BlockObserver.swift in Sources */, - 979CB6571D05C44F005E1BA1 /* PushCapability-OSX.swift in Sources */, 971A10401D022C42007FC62C /* LibraryUseCellularDataTBVC.swift in Sources */, 977998761C1E0B7900B1DD5E /* DownloadTask.swift in Sources */, - 979CB6671D05C44F005E1BA1 /* OperationCondition.swift in Sources */, 9752279C1D020C00001D1DDE /* pathTools.cpp in Sources */, 971A10381D022C15007FC62C /* WebViewController.swift in Sources */, 971046321D19B96E002141C0 /* XapianSearcher.mm in Sources */, - 979CB6AF1D05C520005E1BA1 /* CKContainer+Operations.swift in Sources */, - 979CB6431D05C44F005E1BA1 /* CalendarCapability.swift in Sources */, - 979CB6AD1D05C520005E1BA1 /* BlockOperation.swift in Sources */, - 979CB6B11D05C520005E1BA1 /* DelayOperation.swift in Sources */, 973C8D5C1C25F945007272F9 /* Preference.swift in Sources */, - 979CB6B91D05C520005E1BA1 /* NSLock+Operations.swift in Sources */, 975B90FE1CEB909100D13906 /* iOSExtensions.swift in Sources */, - 979CB66B1D05C44F005E1BA1 /* PhotosCondition.swift in Sources */, - 979CB6751D05C44F005E1BA1 /* ExclusivityController.swift in Sources */, 971A10521D022D9D007FC62C /* AppDelegate.swift in Sources */, - 979CB6BF1D05C520005E1BA1 /* OperationErrors.swift in Sources */, - 979CB6631D05C44F005E1BA1 /* NegatedCondition.swift in Sources */, 977998781C1E0B7900B1DD5E /* Language+CoreDataProperties.swift in Sources */, 97D452BE1D1723FF0033666F /* CollectionViewCells.swift in Sources */, - 979CB6B71D05C520005E1BA1 /* LocationOperation.swift in Sources */, 971A102F1D022AD5007FC62C /* Logo.swift in Sources */, - 979CB6451D05C44F005E1BA1 /* Capability.swift in Sources */, 97D55EF61D2075180081B523 /* TableOfContentsController.swift in Sources */, + 9779A1C81D34225E0071EFAB /* AlertOperation.swift in Sources */, 971A104B1D022CBE007FC62C /* SearchBooksVC.swift in Sources */, 977998771C1E0B7900B1DD5E /* Article+CoreDataProperties.swift in Sources */, 971187301CEB50FC00B9909D /* ZimReader.mm in Sources */, - 979CB6491D05C44F005E1BA1 /* iCloudContainerCapability.swift in Sources */, 971A103C1D022C2C007FC62C /* FontSizeTBVC.swift in Sources */, 971A103B1D022C2C007FC62C /* AdjustLayoutTBVC.swift in Sources */, 975227A21D020C00001D1DDE /* stringTools.cpp in Sources */, 9711879A1CEB546C00B9909D /* CoreDataExtension.swift in Sources */, + 970C61971D34243600087758 /* URLSessionDownloadTaskOperation.swift in Sources */, 97A7140A1C274FCB00951244 /* DirectoryMonitor.swift in Sources */, 971A10341D022AEC007FC62C /* BookmarkTBVC.swift in Sources */, 975227A01D020C00001D1DDE /* resourceTools.cpp in Sources */, - 979CB6B31D05C520005E1BA1 /* Dictionary+Operations.swift in Sources */, - 979CB64B1D05C44F005E1BA1 /* LocationCapability-iOS.swift in Sources */, - 979CB6611D05C44F005E1BA1 /* LocationCondition.swift in Sources */, 971A10601D022DF2007FC62C /* LanguageTBVC.swift in Sources */, 971A106A1D022E15007FC62C /* BookmarkHUDVC.swift in Sources */, 971A106C1D022E50007FC62C /* Utilities.swift in Sources */, 971A10591D022DAD007FC62C /* LibraryLocalBookDetailTBVC.swift in Sources */, - 979CB64D1D05C44F005E1BA1 /* LocationCapability-OSX.swift in Sources */, 971A10431D022C54007FC62C /* SettingTBVC.swift in Sources */, + 9779A1C91D34225E0071EFAB /* GlobalOperationQueue.swift in Sources */, + 970C61991D3429E400087758 /* ReachabilityCondition.swift in Sources */, 978C589C1C1CD86E0077AE47 /* Article.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1984,7 +1679,7 @@ /* Begin XCBuildConfiguration section */ 973BCD0F1CEB3FA500F10B44 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E0C40F7192D36BEA96D12C9C /* Pods-Kiwix-OSX.debug.xcconfig */; + baseConfigurationReference = 6DCB0E958A1083CA248C5A12 /* Pods-Kiwix-OSX.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -2015,7 +1710,7 @@ }; 973BCD101CEB3FA500F10B44 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 86340F4A23677DE4554AE94B /* Pods-Kiwix-OSX.release.xcconfig */; + baseConfigurationReference = 69E75DFE2EF83272AC5B02BA /* Pods-Kiwix-OSX.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -2194,7 +1889,7 @@ }; 97A2ABB41C1B810000052E74 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B14E5F8A97964014F99EAD4E /* Pods-Kiwix-iOS.debug.xcconfig */; + baseConfigurationReference = DA0AB5D61F19BE37BD0AFA0A /* Pods-Kiwix-iOS.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -2228,7 +1923,7 @@ }; 97A2ABB51C1B810000052E74 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B3B41D59F4B010C559B18D3D /* Pods-Kiwix-iOS.release.xcconfig */; + baseConfigurationReference = 5B9300794E7F4EFE5B3E8F19 /* Pods-Kiwix-iOS.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; diff --git a/Kiwix/GlobalOperationQueue.swift b/Kiwix/GlobalOperationQueue.swift deleted file mode 100644 index 6e04a291f..000000000 --- a/Kiwix/GlobalOperationQueue.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// GlobalOperationQueue.swift -// Kiwix -// -// Created by Chris Li on 5/14/16. -// Copyright © 2016 Chris. All rights reserved. -// - -class GlobalOperationQueue: OperationQueue { - static let sharedInstance = GlobalOperationQueue() - - var isRefreshingLibrary: Bool { - return getOperation(String(RefreshLibraryOperation)) != nil - } -} diff --git a/Kiwix/OperationLib/Capability/CalendarCapability.swift b/Kiwix/OperationLib/Capability/CalendarCapability.swift deleted file mode 100755 index 88967fa07..000000000 --- a/Kiwix/OperationLib/Capability/CalendarCapability.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// CalendarCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if !os(tvOS) - -import Foundation -import EventKit - -private let SharedEventStore = EKEventStore() - -extension EKEntityType: CapabilityType { - public static var name: String { return "EKEntityType" } - - public func requestStatus(completion: CapabilityStatus -> Void) { - let status = EKEventStore.authorizationStatusForEntityType(self) - switch status { - case .Authorized: completion(.Authorized) - case .Denied: completion(.Denied) - case .Restricted: completion(.NotAvailable) - case .NotDetermined: completion(.NotDetermined) - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - SharedEventStore.requestAccessToEntityType(self) { granted, error in - if granted { - completion(.Authorized) - } else if let error = error { - completion(.Error(error)) - } else { - completion(.NotAvailable) - } - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Capability/Capability.swift b/Kiwix/OperationLib/Capability/Capability.swift deleted file mode 100755 index 5ca7e79c7..000000000 --- a/Kiwix/OperationLib/Capability/Capability.swift +++ /dev/null @@ -1,131 +0,0 @@ -// -// Capability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -import Foundation - -public enum CapabilityErrorCode: Int { - public static var domain = "CapabilityErrors" - - case NotDetermined - case NotAvailable - case Denied -} - -public enum CapabilityStatus { - /// The capability has not been requested yet - case NotDetermined - - /// The capability has been requested and approved - case Authorized - - /// The capability has been requested but was denied by the user - case Denied - - /// The capability is not available (perhaps due to restrictions, or lack of support) - case NotAvailable - - /// There was an error requesting the status of the capability - case Error(NSError) -} - -public protocol CapabilityType { - static var name: String { get } - - /// Retrieve the status of the capability. - /// This method is called from the main queue. - func requestStatus(completion: CapabilityStatus -> Void) - - /// Request authorization for the capability. - /// This method is called from the main queue, and only if the - /// capability's status is "NotDetermined" - func authorize(completion: CapabilityStatus -> Void) -} - -/// A condition for verifying and/or requesting a certain capability -public struct Capability: OperationCondition { - - public static var name: String { return "Capability<\(C.name)>" } - public static var isMutuallyExclusive: Bool { return true } - - private let capability: C - private let shouldRequest: Bool - - public init(_ capability: C, requestIfNecessary: Bool = true) { - self.capability = capability - self.shouldRequest = requestIfNecessary - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - guard shouldRequest == true else { return nil } - return AuthorizeCapability(capability: capability) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - dispatch_async(dispatch_get_main_queue()) { - self.capability.requestStatus { status in - if let error = status.error { - let conditionError = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - NSUnderlyingErrorKey: error - ]) - completion(.Failed(conditionError)) - } else { - completion(.Satisfied) - } - } - } - } -} - -private class AuthorizeCapability: Operation { - private let capability: C - - init(capability: C) { - self.capability = capability - super.init() - addCondition(AlertPresentation()) - addCondition(MutuallyExclusive()) - } - - private override func execute() { - dispatch_async(dispatch_get_main_queue()) { - self.capability.requestStatus { status in - switch status { - case .NotDetermined: self.requestAuthorization() - default: self.finishWithError(status.error) - } - } - } - } - - private func requestAuthorization() { - dispatch_async(dispatch_get_main_queue()) { - self.capability.authorize { status in - self.finishWithError(status.error) - } - } - } -} - -private extension NSError { - convenience init(capabilityErrorCode: CapabilityErrorCode) { - self.init(domain: CapabilityErrorCode.domain, code: capabilityErrorCode.rawValue, userInfo: [:]) - } -} - -private extension CapabilityStatus { - private var error: NSError? { - switch self { - case .NotDetermined: return NSError(capabilityErrorCode: .NotDetermined) - case .Authorized: return nil - case .Denied: return NSError(capabilityErrorCode: .Denied) - case .NotAvailable: return NSError(capabilityErrorCode: .NotAvailable) - case .Error(let e): return e - } - } -} diff --git a/Kiwix/OperationLib/Capability/HealthCapability.swift b/Kiwix/OperationLib/Capability/HealthCapability.swift deleted file mode 100755 index 9d2df55bb..000000000 --- a/Kiwix/OperationLib/Capability/HealthCapability.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// HealthCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(iOS) || os(watchOS) - -import Foundation -import HealthKit - -public struct Health: CapabilityType { - public static let name = "Health" - - private let readTypes: Set - private let writeTypes: Set - - public init(typesToRead: Set, typesToWrite: Set) { - self.readTypes = typesToRead - self.writeTypes = typesToWrite - } - - public func requestStatus(completion: CapabilityStatus -> Void) { - guard HKHealthStore.isHealthDataAvailable() else { - completion(.NotAvailable) - return - } - - let notDeterminedTypes = writeTypes.filter { SharedHealthStore.authorizationStatusForType($0) == .NotDetermined } - if notDeterminedTypes.isEmpty == false { - completion(.NotDetermined) - return - } - - let deniedTypes = writeTypes.filter { SharedHealthStore.authorizationStatusForType($0) == .SharingDenied } - if deniedTypes.isEmpty == false { - completion(.Denied) - return - } - - // if we get here, then every write type has been authorized - // there's no way to know if we have read permissions, - // so the best we can do is see if we've ever asked for authorization - - let unrequestedReadTypes = readTypes.subtract(requestedReadTypes) - - if unrequestedReadTypes.isEmpty == false { - completion(.NotDetermined) - return - } - - // if we get here, then there was nothing to request for reading or writing - // thus, everything is authorized - completion(.Authorized) - } - - public func authorize(completion: CapabilityStatus -> Void) { - guard HKHealthStore.isHealthDataAvailable() else { - completion(.NotAvailable) - return - } - - // make a note that we've requested these types before - requestedReadTypes.unionInPlace(readTypes) - - // This method is smart enough to not re-prompt for access if it has already been granted. - SharedHealthStore.requestAuthorizationToShareTypes(writeTypes, readTypes: readTypes) { _, error in - if let error = error { - completion(.Error(error)) - } else { - self.requestStatus(completion) - } - } - } - -} - -/** - HealthKit does not report on whether or not you're allowed to read certain data types. - Instead, we'll keep track of which types we've already request to read. If a new request - comes along for a type that's not in here, we know that we'll need to re-prompt for - permission to read that particular type. -*/ -private var requestedReadTypes = Set() -private let SharedHealthStore = HKHealthStore() - -#endif diff --git a/Kiwix/OperationLib/Capability/LocationCapability-OSX.swift b/Kiwix/OperationLib/Capability/LocationCapability-OSX.swift deleted file mode 100755 index aaaba960e..000000000 --- a/Kiwix/OperationLib/Capability/LocationCapability-OSX.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// LocationCapability-OSX.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(OSX) - -import Foundation -import CoreLocation - -public struct Location: CapabilityType { - public static let name = "Location" - - public init() { } - - public func requestStatus(completion: CapabilityStatus -> Void) { - guard CLLocationManager.locationServicesEnabled() else { - completion(.NotAvailable) - return - } - - let actual = CLLocationManager.authorizationStatus() - - switch actual { - case .NotDetermined: completion(.NotDetermined) - case .Restricted: completion(.NotAvailable) - case .Denied: completion(.Denied) - case .Authorized: completion(.Authorized) - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - Authorizer.authorize(completion) - } -} - -private let Authorizer = LocationAuthorizer() - -private class LocationAuthorizer: NSObject, CLLocationManagerDelegate { - - private let manager = CLLocationManager() - private var completion: (CapabilityStatus -> Void)? - - override init() { - super.init() - manager.delegate = self - } - - func authorize(completion: CapabilityStatus -> Void) { - guard self.completion == nil else { - fatalError("Attempting to authorize location when a request is already in-flight") - } - self.completion = completion - - let key = "NSLocationUsageDescription" - manager.startUpdatingLocation() - - // This is helpful when developing an app. - assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist") - } - - @objc func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { - if let completion = self.completion where manager == self.manager { - self.completion = nil - - switch status { - case .Authorized: - completion(.Authorized) - case .Denied: - completion(.Denied) - case .Restricted: - completion(.NotAvailable) - case .NotDetermined: - self.completion = completion - manager.startUpdatingLocation() - manager.stopUpdatingLocation() - } - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Capability/LocationCapability-iOS.swift b/Kiwix/OperationLib/Capability/LocationCapability-iOS.swift deleted file mode 100755 index e8f2ed760..000000000 --- a/Kiwix/OperationLib/Capability/LocationCapability-iOS.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// LocationCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(iOS) || os(watchOS) - -import Foundation -import CoreLocation - -public enum Location: CapabilityType { - public static let name = "Location" - - case WhenInUse - case Always - - public func requestStatus(completion: CapabilityStatus -> Void) { - guard CLLocationManager.locationServicesEnabled() else { - completion(.NotAvailable) - return - } - - let actual = CLLocationManager.authorizationStatus() - - switch actual { - case .NotDetermined: completion(.NotDetermined) - case .Restricted: completion(.NotAvailable) - case .Denied: completion(.Denied) - case .AuthorizedAlways: completion(.Authorized) - case .AuthorizedWhenInUse: - if self == .WhenInUse { - completion(.Authorized) - } else { - // the user wants .Always, but has .WhenInUse - // return .NotDetermined so that we can prompt to upgrade the permission - completion(.NotDetermined) - } - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - Authorizer.authorize(self, completion: completion) - } -} - -private let Authorizer = LocationAuthorizer() - -private class LocationAuthorizer: NSObject, CLLocationManagerDelegate { - - private let manager = CLLocationManager() - private var completion: (CapabilityStatus -> Void)? - private var kind = Location.WhenInUse - - override init() { - super.init() - manager.delegate = self - } - - func authorize(kind: Location, completion: CapabilityStatus -> Void) { - guard self.completion == nil else { - fatalError("Attempting to authorize location when a request is already in-flight") - } - self.completion = completion - self.kind = kind - - let key: String - switch kind { - case .WhenInUse: - key = "NSLocationWhenInUseUsageDescription" - manager.requestWhenInUseAuthorization() - - case .Always: - key = "NSLocationAlwaysUsageDescription" - manager.requestAlwaysAuthorization() - } - - // This is helpful when developing an app. - assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist") - } - - @objc func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { - if let completion = self.completion where manager == self.manager && status != .NotDetermined { - self.completion = nil - - switch status { - case .AuthorizedAlways: - completion(.Authorized) - case .AuthorizedWhenInUse: - completion(kind == .WhenInUse ? .Authorized : .Denied) - case .Denied: - completion(.Denied) - case .Restricted: - completion(.NotAvailable) - case .NotDetermined: - fatalError("Unreachable due to the if statement, but included to keep clang happy") - } - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Capability/LocationCapability-tvOS.swift b/Kiwix/OperationLib/Capability/LocationCapability-tvOS.swift deleted file mode 100755 index 75c5ff350..000000000 --- a/Kiwix/OperationLib/Capability/LocationCapability-tvOS.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// LocationCapability-tvOS.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(tvOS) - -import Foundation -import CoreLocation - -public struct Location: CapabilityType { - public static let name = "Location" - - public init() { } - - public func requestStatus(completion: CapabilityStatus -> Void) { - guard CLLocationManager.locationServicesEnabled() else { - completion(.NotAvailable) - return - } - - let actual = CLLocationManager.authorizationStatus() - - switch actual { - case .NotDetermined: completion(.NotDetermined) - case .Restricted: completion(.NotAvailable) - case .Denied: completion(.Denied) - case .AuthorizedWhenInUse: completion(.Authorized) - case .AuthorizedAlways: - fatalError(".Always should be unavailable on tvOS") - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - Authorizer.authorize(completion) - } -} - -private let Authorizer = LocationAuthorizer() - -private class LocationAuthorizer: NSObject, CLLocationManagerDelegate { - - private let manager = CLLocationManager() - private var completion: (CapabilityStatus -> Void)? - - override init() { - super.init() - manager.delegate = self - } - - func authorize(completion: CapabilityStatus -> Void) { - guard self.completion == nil else { - fatalError("Attempting to authorize location when a request is already in-flight") - } - self.completion = completion - - let key = "NSLocationWhenInUseUsageDescription" - manager.requestWhenInUseAuthorization() - - // This is helpful when developing an app. - assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist") - } - - @objc func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { - if let completion = self.completion where manager == self.manager && status != .NotDetermined { - self.completion = nil - - switch status { - case .AuthorizedWhenInUse: - completion(.Authorized) - case .Denied: - completion(.Denied) - case .Restricted: - completion(.NotAvailable) - case .AuthorizedAlways: - fatalError(".Always should be unavailable on tvOS") - case .NotDetermined: - fatalError("Unreachable due to the if statement, but included to keep clang happy") - } - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Capability/PassbookCapability.swift b/Kiwix/OperationLib/Capability/PassbookCapability.swift deleted file mode 100755 index 9da77d388..000000000 --- a/Kiwix/OperationLib/Capability/PassbookCapability.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// PassbookCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(iOS) - -import Foundation -import PassKit - -public enum Passbook: CapabilityType { - public static let name = "Passbook" - - case ViewPasses - case AddPasses - - public func requestStatus(completion: CapabilityStatus -> Void) { - switch self { - case .ViewPasses: - if PKPassLibrary.isPassLibraryAvailable() { - completion(.Authorized) - } else { - completion(.NotAvailable) - } - case .AddPasses: - if PKAddPassesViewController.canAddPasses() { - completion(.Authorized) - } else { - completion(.NotAvailable) - } - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - // Since requestStatus() never returns .NotDetermined, this method should never be called - fatalError("This should never be invoked") - } -} - -#endif diff --git a/Kiwix/OperationLib/Capability/PhotosCapability.swift b/Kiwix/OperationLib/Capability/PhotosCapability.swift deleted file mode 100755 index b2c4a5187..000000000 --- a/Kiwix/OperationLib/Capability/PhotosCapability.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// PhotosCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(iOS) - -import Foundation -import Photos - -public struct Photos: CapabilityType { - public static let name = "Photos" - - public init() { } - - public func requestStatus(completion: CapabilityStatus -> Void) { - let status = PHPhotoLibrary.authorizationStatus() - switch status { - case .Authorized: completion(.Authorized) - case .Denied: completion(.Denied) - case .Restricted: completion(.NotAvailable) - case .NotDetermined: completion(.NotDetermined) - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - PHPhotoLibrary.requestAuthorization { status in - switch status { - case .Authorized: completion(.Authorized) - case .Denied: completion(.Denied) - case .Restricted: completion(.NotAvailable) - case .NotDetermined: completion(.NotDetermined) - } - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Capability/PushCapability-OSX.swift b/Kiwix/OperationLib/Capability/PushCapability-OSX.swift deleted file mode 100755 index 7d108495f..000000000 --- a/Kiwix/OperationLib/Capability/PushCapability-OSX.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// PushCapability-OSX.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(OSX) - -import Cocoa - -public struct Push: CapabilityType { - - public static func didReceiveToken(token: NSData) { - authorizer.completeAuthorization(token, error: nil) - } - - public static func didFailRegistration(error: NSError) { - authorizer.completeAuthorization(nil, error: error) - } - - public static let name = "Push" - - private let types: NSRemoteNotificationType - - public init(types: NSRemoteNotificationType) { - self.types = types - } - - public func requestStatus(completion: CapabilityStatus -> Void) { - if let _ = authorizer.token { - completion(.Authorized) - } else { - completion(.NotDetermined) - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - authorizer.authorize(types, completion: completion) - } - -} - -private let authorizer = PushAuthorizer() - -private class PushAuthorizer { - - var token: NSData? - var completion: (CapabilityStatus -> Void)? - - func authorize(types: NSRemoteNotificationType, completion: CapabilityStatus -> Void) { - guard self.completion == nil else { - fatalError("Cannot request push authorization while a request is already in progress") - } - - self.completion = completion - NSApplication.sharedApplication().registerForRemoteNotificationTypes(types) - } - - private func completeAuthorization(token: NSData?, error: NSError?) { - self.token = token - - guard let completion = self.completion else { return } - self.completion = nil - - if let _ = self.token { - completion(.Authorized) - } else if let error = error { - completion(.Error(error)) - } else { - completion(.NotDetermined) - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Capability/PushCapability-iOS.swift b/Kiwix/OperationLib/Capability/PushCapability-iOS.swift deleted file mode 100755 index b00e85182..000000000 --- a/Kiwix/OperationLib/Capability/PushCapability-iOS.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// PushCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(iOS) - -import UIKit - -public struct Push: CapabilityType { - - public static func didReceiveToken(token: NSData) { - authorizer.completeAuthorization(token, error: nil) - } - - public static func didFailRegistration(error: NSError) { - authorizer.completeAuthorization(nil, error: error) - } - - public static let name = "Push" - - public init(application: UIApplication) { - if authorizer.application == nil { - authorizer.application = application - } - } - - public func requestStatus(completion: CapabilityStatus -> Void) { - if let _ = authorizer.token { - completion(.Authorized) - } else { - completion(.NotDetermined) - } - } - - public func authorize(completion: CapabilityStatus -> Void) { - authorizer.authorize(completion) - } - -} - -private let authorizer = PushAuthorizer() - -private class PushAuthorizer { - - var application: UIApplication? - var token: NSData? - var completion: (CapabilityStatus -> Void)? - - func authorize(completion: CapabilityStatus -> Void) { - guard self.completion == nil else { - fatalError("Cannot request push authorization while a request is already in progress") - } - - self.completion = completion - - guard let application = application else { - fatalError("An application has not yet been configured, so this won't work") - } - - application.registerForRemoteNotifications() - } - - private func completeAuthorization(token: NSData?, error: NSError?) { - self.token = token - - guard let completion = self.completion else { return } - self.completion = nil - - if let _ = self.token { - completion(.Authorized) - } else if let error = error { - completion(.Error(error)) - } else { - completion(.NotDetermined) - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Capability/UserNotificationCapability.swift b/Kiwix/OperationLib/Capability/UserNotificationCapability.swift deleted file mode 100755 index 79b554cfb..000000000 --- a/Kiwix/OperationLib/Capability/UserNotificationCapability.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// UserNotificationCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if os(iOS) - -import UIKit - -public struct UserNotification: CapabilityType { - - public static let name = "UserNotificaton" - - public static func didRegisterUserSettings() { - authorizer.completeAuthorization() - } - - public enum Behavior { - case Replace - case Merge - } - - private let settings: UIUserNotificationSettings - private let behavior: Behavior - - public init(settings: UIUserNotificationSettings, behavior: Behavior = .Merge, application: UIApplication) { - self.settings = settings - self.behavior = behavior - - if authorizer._application == nil { - authorizer.application = application - } - } - - public func requestStatus(completion: CapabilityStatus -> Void) { - let registered = authorizer.areSettingsRegistered(settings) - completion(registered ? .Authorized : .NotDetermined) - } - - public func authorize(completion: CapabilityStatus -> Void) { - let settings: UIUserNotificationSettings - - switch behavior { - case .Replace: - settings = self.settings - case .Merge: - let current = authorizer.application.currentUserNotificationSettings() - settings = current?.settingsByMerging(self.settings) ?? self.settings - } - - authorizer.authorize(settings, completion: completion) - } - -} - -private let authorizer = UserNotificationAuthorizer() - -private class UserNotificationAuthorizer { - - var _application: UIApplication? - var application: UIApplication { - set { - _application = newValue - } - get { - guard let application = _application else { - fatalError("Application not yet configured. Results would be undefined.") - } - - return application - } - } - var completion: (CapabilityStatus -> Void)? - var settings: UIUserNotificationSettings? - - func areSettingsRegistered(settings: UIUserNotificationSettings) -> Bool { - let current = application.currentUserNotificationSettings() - - return current?.contains(settings) ?? false - } - - func authorize(settings: UIUserNotificationSettings, completion: CapabilityStatus -> Void) { - guard self.completion == nil else { - fatalError("Cannot request push authorization while a request is already in progress") - } - guard self.settings == nil else { - fatalError("Cannot request push authorization while a request is already in progress") - } - - self.completion = completion - self.settings = settings - - application.registerUserNotificationSettings(settings) - } - - private func completeAuthorization() { - - guard let completion = self.completion else { return } - guard let settings = self.settings else { return } - - self.completion = nil - self.settings = nil - - let registered = areSettingsRegistered(settings) - completion(registered ? .Authorized : .Denied) - } - -} - -#endif diff --git a/Kiwix/OperationLib/Capability/iCloudContainerCapability.swift b/Kiwix/OperationLib/Capability/iCloudContainerCapability.swift deleted file mode 100755 index 483e21d19..000000000 --- a/Kiwix/OperationLib/Capability/iCloudContainerCapability.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// CloudCapability.swift -// PSOperations -// -// Created by Dev Team on 10/4/15. -// Copyright © 2015 Pluralsight. All rights reserved. -// - -#if !os(watchOS) - -import Foundation -import CloudKit - -public struct iCloudContainer: CapabilityType { - - public static let name = "iCloudContainer" - - private let container: CKContainer - private let permissions: CKApplicationPermissions - - public init(container: CKContainer, permissions: CKApplicationPermissions = []) { - self.container = container - self.permissions = permissions - } - - public func requestStatus(completion: CapabilityStatus -> Void) { - verifyAccountStatus(container, permission: permissions, shouldRequest: false, completion: completion) - } - - public func authorize(completion: CapabilityStatus -> Void) { - verifyAccountStatus(container, permission: permissions, shouldRequest: true, completion: completion) - } - -} - -private func verifyAccountStatus(container: CKContainer, permission: CKApplicationPermissions, shouldRequest: Bool, completion: CapabilityStatus -> Void) { - container.accountStatusWithCompletionHandler { accountStatus, accountError in - switch accountStatus { - case .NoAccount: completion(.NotAvailable) - case .Restricted: completion(.NotAvailable) - case .CouldNotDetermine: - let error = accountError ?? NSError(domain: CKErrorDomain, code: CKErrorCode.NotAuthenticated.rawValue, userInfo: nil) - completion(.Error(error)) - case .Available: - if permission != [] { - verifyPermission(container, permission: permission, shouldRequest: shouldRequest, completion: completion) - } else { - completion(.Authorized) - } - } - } -} - -private func verifyPermission(container: CKContainer, permission: CKApplicationPermissions, shouldRequest: Bool, completion: CapabilityStatus -> Void) { - container.statusForApplicationPermission(permission) { permissionStatus, permissionError in - switch permissionStatus { - case .InitialState: - if shouldRequest { - requestPermission(container, permission: permission, completion: completion) - } else { - completion(.NotDetermined) - } - case .Denied: completion(.Denied) - case .Granted: completion(.Authorized) - case .CouldNotComplete: - let error = permissionError ?? NSError(domain: CKErrorDomain, code: CKErrorCode.PermissionFailure.rawValue, userInfo: nil) - completion(.Error(error)) - } - } -} - -private func requestPermission(container: CKContainer, permission: CKApplicationPermissions, completion: CapabilityStatus -> Void) { - dispatch_async(dispatch_get_main_queue()) { - container.requestApplicationPermission(permission) { requestStatus, requestError in - switch requestStatus { - case .InitialState: completion(.NotDetermined) - case .Denied: completion(.Denied) - case .Granted: completion(.Authorized) - case .CouldNotComplete: - let error = requestError ?? NSError(domain: CKErrorDomain, code: CKErrorCode.PermissionFailure.rawValue, userInfo: nil) - completion(.Error(error)) - } - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/CalendarCondition.swift b/Kiwix/OperationLib/Conditions/CalendarCondition.swift deleted file mode 100755 index 8df14482a..000000000 --- a/Kiwix/OperationLib/Conditions/CalendarCondition.swift +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if !os(tvOS) - - import EventKit - - /// A condition for verifying access to the user's calendar. - - @available(*, deprecated, message="use Capability(EKEntityType....) instead") - - public struct CalendarCondition: OperationCondition { - - public static let name = "Calendar" - static let entityTypeKey = "EKEntityType" - public static let isMutuallyExclusive = false - - public let entityType: EKEntityType - - public init(entityType: EKEntityType) { - self.entityType = entityType - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return CalendarPermissionOperation(entityType: entityType) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - switch EKEventStore.authorizationStatusForEntityType(entityType) { - case .Authorized: - completion(.Satisfied) - - default: - // We are not authorized to access entities of this type. - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.entityTypeKey: entityType.rawValue - ]) - - completion(.Failed(error)) - } - } - } - - /** - `EKEventStore` takes a while to initialize, so we should create - one and then keep it around for future use, instead of creating - a new one every time a `CalendarPermissionOperation` runs. - */ - private let SharedEventStore = EKEventStore() - - /** - A private `Operation` that will request access to the user's Calendar/Reminders, - if it has not already been granted. - */ - class CalendarPermissionOperation: Operation { - let entityType: EKEntityType - - init(entityType: EKEntityType) { - self.entityType = entityType - super.init() - addCondition(AlertPresentation()) - } - - override func execute() { - let status = EKEventStore.authorizationStatusForEntityType(entityType) - - switch status { - case .NotDetermined: - dispatch_async(dispatch_get_main_queue()) { - SharedEventStore.requestAccessToEntityType(self.entityType) { granted, error in - self.finish() - } - } - - default: - finish() - } - } - - } - -#endif diff --git a/Kiwix/OperationLib/Conditions/CloudCondition.swift b/Kiwix/OperationLib/Conditions/CloudCondition.swift deleted file mode 100755 index 92055f64a..000000000 --- a/Kiwix/OperationLib/Conditions/CloudCondition.swift +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if !os(watchOS) - -import CloudKit - -/// A condition describing that the operation requires access to a specific CloudKit container. -@available(*, deprecated, message="use Capability(iCloudContainer(...)) instead") - -public struct CloudContainerCondition: OperationCondition { - - public static let name = "CloudContainer" - static let containerKey = "CKContainer" - - /* - CloudKit has no problem handling multiple operations at the same time - so we will allow operations that use CloudKit to be concurrent with each - other. - */ - public static let isMutuallyExclusive = false - - let container: CKContainer // this is the container to which you need access. - - let permission: CKApplicationPermissions - - /** - - parameter container: the `CKContainer` to which you need access. - - parameter permission: the `CKApplicationPermissions` you need for the - container. This parameter has a default value of `[]`, which would get - you anonymized read/write access. - */ - public init(container: CKContainer, permission: CKApplicationPermissions = []) { - self.container = container - self.permission = permission - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return CloudKitPermissionOperation(container: container, permission: permission) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - container.verifyPermission(permission, requestingIfNecessary: false) { error in - if let error = error { - let conditionError = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.containerKey: self.container, - NSUnderlyingErrorKey: error - ]) - - completion(.Failed(conditionError)) - } - else { - completion(.Satisfied) - } - } - } -} - -/** - This operation asks the user for permission to use CloudKit, if necessary. - If permission has already been granted, this operation will quickly finish. -*/ -class CloudKitPermissionOperation: Operation { - let container: CKContainer - let permission: CKApplicationPermissions - - init(container: CKContainer, permission: CKApplicationPermissions) { - self.container = container - self.permission = permission - super.init() - - if permission != [] { - /* - Requesting non-zero permissions means that this potentially presents - an alert, so it should not run at the same time as anything else - that presents an alert. - */ - addCondition(AlertPresentation()) - } - } - - override func execute() { - container.verifyPermission(permission, requestingIfNecessary: true) { error in - self.finishWithError(error) - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/HealthCondition.swift b/Kiwix/OperationLib/Conditions/HealthCondition.swift deleted file mode 100755 index 43afb4d42..000000000 --- a/Kiwix/OperationLib/Conditions/HealthCondition.swift +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if os(iOS) - -import HealthKit -import UIKit - -/** - A condition to indicate an `Operation` requires access to the user's health - data. -*/ - -@available(*, deprecated, message="use Capability(Health(...)) instead") -public struct HealthCondition: OperationCondition { - public static let name = "Health" - static let healthDataAvailable = "HealthDataAvailable" - static let unauthorizedShareTypesKey = "UnauthorizedShareTypes" - public static let isMutuallyExclusive = false - - let shareTypes: Set - let readTypes: Set - - /** - The designated initializer. - - - parameter typesToWrite: An array of `HKSampleType` objects, indicating - the kinds of data you wish to save to HealthKit. - - - parameter typesToRead: An array of `HKSampleType` objects, indicating - the kinds of data you wish to read from HealthKit. - */ - public init(typesToWrite: Set, typesToRead: Set) { - shareTypes = typesToWrite - readTypes = typesToRead - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - if !HKHealthStore.isHealthDataAvailable() { - return nil - } - - if shareTypes.isEmpty && readTypes.isEmpty { - return nil - } - - return HealthPermissionOperation(shareTypes: shareTypes, readTypes: readTypes) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - if !HKHealthStore.isHealthDataAvailable() { - failed(shareTypes, completion: completion) - return - } - - let store = HKHealthStore() - /* - Note that we cannot check to see if access to the "typesToRead" - has been granted or not, as that is sensitive data. For example, - a person with diabetes may choose to not allow access to Blood Glucose - data, and the fact that this request has denied is itself an indicator - that the user may have diabetes. - - Thus, we can only check to see if we've been given permission to - write data to HealthKit. - */ - let unauthorizedShareTypes = shareTypes.filter { shareType in - return store.authorizationStatusForType(shareType) != .SharingAuthorized - } - - if !unauthorizedShareTypes.isEmpty { - failed(Set(unauthorizedShareTypes), completion: completion) - } - else { - completion(.Satisfied) - } - } - - // Break this out in to its own method so we don't clutter up the evaluate... method. - private func failed(unauthorizedShareTypes: Set, completion: OperationConditionResult -> Void) { - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.healthDataAvailable: HKHealthStore.isHealthDataAvailable(), - self.dynamicType.unauthorizedShareTypesKey: unauthorizedShareTypes - ]) - - completion(.Failed(error)) - } -} - -/** - A private `Operation` that will request access to the user's health data, if - it has not already been granted. -*/ -class HealthPermissionOperation: Operation { - let shareTypes: Set - let readTypes: Set - - init(shareTypes: Set, readTypes: Set) { - self.shareTypes = shareTypes - self.readTypes = readTypes - - super.init() - - addCondition(MutuallyExclusive()) - addCondition(MutuallyExclusive()) - addCondition(AlertPresentation()) - } - - override func execute() { - dispatch_async(dispatch_get_main_queue()) { - let store = HKHealthStore() - /* - This method is smart enough to not re-prompt for access if access - has already been granted. - */ - - store.requestAuthorizationToShareTypes(self.shareTypes, readTypes: self.readTypes) { completed, error in - self.finish() - } - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/LocationCondition.swift b/Kiwix/OperationLib/Conditions/LocationCondition.swift deleted file mode 100755 index 10c10ca54..000000000 --- a/Kiwix/OperationLib/Conditions/LocationCondition.swift +++ /dev/null @@ -1,172 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if !os(OSX) - -import CoreLocation - -/// A condition for verifying access to the user's location. -@available(*, deprecated, message="use Capability(Location...) instead") - -public struct LocationCondition: OperationCondition { - /** - Declare a new enum instead of using `CLAuthorizationStatus`, because that - enum has more case values than are necessary for our purposes. - */ - public enum Usage { - case WhenInUse - #if !os(tvOS) - case Always - #endif - } - - public static let name = "Location" - static let locationServicesEnabledKey = "CLLocationServicesEnabled" - static let authorizationStatusKey = "CLAuthorizationStatus" - public static let isMutuallyExclusive = false - - let usage: Usage - - public init(usage: Usage) { - self.usage = usage - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return LocationPermissionOperation(usage: usage) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - let enabled = CLLocationManager.locationServicesEnabled() - let actual = CLLocationManager.authorizationStatus() - - var error: NSError? - - // There are several factors to consider when evaluating this condition - switch (enabled, usage, actual) { - case (true, _, .AuthorizedAlways): - // The service is enabled, and we have "Always" permission -> condition satisfied. - break - - case (true, .WhenInUse, .AuthorizedWhenInUse): - /* - The service is enabled, and we have and need "WhenInUse" - permission -> condition satisfied. - */ - break - - default: - /* - Anything else is an error. Maybe location services are disabled, - or maybe we need "Always" permission but only have "WhenInUse", - or maybe access has been restricted or denied, - or maybe access hasn't been request yet. - - The last case would happen if this condition were wrapped in a `SilentCondition`. - */ - error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.locationServicesEnabledKey: enabled, - self.dynamicType.authorizationStatusKey: Int(actual.rawValue) - ]) - } - - if let error = error { - completion(.Failed(error)) - } - else { - completion(.Satisfied) - } - } -} - -/** - A private `Operation` that will request permission to access the user's location, - if permission has not already been granted. - */ -class LocationPermissionOperation: Operation { - let usage: LocationCondition.Usage - var manager: CLLocationManager? - - init(usage: LocationCondition.Usage) { - self.usage = usage - super.init() - /* - This is an operation that potentially presents an alert so it should - be mutually exclusive with anything else that presents an alert. - */ - addCondition(AlertPresentation()) - } - - override func execute() { - /* - Not only do we need to handle the "Not Determined" case, but we also - need to handle the "upgrade" (.WhenInUse -> .Always) case. - */ - - #if os(tvOS) - switch (CLLocationManager.authorizationStatus(), usage) { - case (.NotDetermined, _): - dispatch_async(dispatch_get_main_queue()) { - self.requestPermission() - } - - default: - finish() - } - #else - switch (CLLocationManager.authorizationStatus(), usage) { - case (.NotDetermined, _), (.AuthorizedWhenInUse, .Always): - dispatch_async(dispatch_get_main_queue()) { - self.requestPermission() - } - - default: - finish() - } - #endif - } - - private func requestPermission() { - manager = CLLocationManager() - manager?.delegate = self - - let key: String - - #if os(tvOS) - switch usage { - case .WhenInUse: - key = "NSLocationWhenInUseUsageDescription" - manager?.requestWhenInUseAuthorization() - } - #else - switch usage { - case .WhenInUse: - key = "NSLocationWhenInUseUsageDescription" - manager?.requestWhenInUseAuthorization() - - case .Always: - key = "NSLocationAlwaysUsageDescription" - manager?.requestAlwaysAuthorization() - } - #endif - - // This is helpful when developing the app. - assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist") - } - -} - -extension LocationPermissionOperation: CLLocationManagerDelegate { - @objc func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { - if manager == self.manager && executing && status != .NotDetermined { - finish() - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/NegatedCondition.swift b/Kiwix/OperationLib/Conditions/NegatedCondition.swift deleted file mode 100755 index cd12b2539..000000000 --- a/Kiwix/OperationLib/Conditions/NegatedCondition.swift +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -The file shows how to make an OperationCondition that composes another OperationCondition. -*/ - -import Foundation - -/** - A simple condition that negates the evaluation of another condition. - This is useful (for example) if you want to only execute an operation if the - network is NOT reachable. -*/ -public struct NegatedCondition: OperationCondition { - public static var name: String { - return "Not<\(T.name)>" - } - - static var negatedConditionKey: String { - return "NegatedCondition" - } - - public static var isMutuallyExclusive: Bool { - return T.isMutuallyExclusive - } - - let condition: T - - public init(condition: T) { - self.condition = condition - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return condition.dependencyForOperation(operation) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - condition.evaluateForOperation(operation) { result in - switch result { - case .Failed(_): - // If the composed condition failed, then this one succeeded. - completion(.Satisfied) - case .Satisfied: - // If the composed condition succeeded, then this one failed. - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.negatedConditionKey: self.condition.dynamicType.name - ]) - - completion(.Failed(error)) - } - } - } -} diff --git a/Kiwix/OperationLib/Conditions/NoCancelledDependencies.swift b/Kiwix/OperationLib/Conditions/NoCancelledDependencies.swift deleted file mode 100755 index e1744d905..000000000 --- a/Kiwix/OperationLib/Conditions/NoCancelledDependencies.swift +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -import Foundation - -/** - A condition that specifies that every dependency must have succeeded. - If any dependency was cancelled, the target operation will be cancelled as - well. -*/ -public struct NoCancelledDependencies: OperationCondition { - public static let name = "NoCancelledDependencies" - static let cancelledDependenciesKey = "CancelledDependencies" - public static let isMutuallyExclusive = false - - public init() { - // No op. - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return nil - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - // Verify that all of the dependencies executed. - let cancelled = operation.dependencies.filter { $0.cancelled } - - if !cancelled.isEmpty { - // At least one dependency was cancelled; the condition was not satisfied. - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.cancelledDependenciesKey: cancelled - ]) - - completion(.Failed(error)) - } - else { - completion(.Satisfied) - } - } -} diff --git a/Kiwix/OperationLib/Conditions/OperationCondition.swift b/Kiwix/OperationLib/Conditions/OperationCondition.swift deleted file mode 100755 index b6b17c83b..000000000 --- a/Kiwix/OperationLib/Conditions/OperationCondition.swift +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file contains the fundamental logic relating to Operation conditions. -*/ - -import Foundation - -public let OperationConditionKey = "OperationCondition" - -/** - A protocol for defining conditions that must be satisfied in order for an - operation to begin execution. -*/ -public protocol OperationCondition { - /** - The name of the condition. This is used in userInfo dictionaries of `.ConditionFailed` - errors as the value of the `OperationConditionKey` key. - */ - static var name: String { get } - - /** - Specifies whether multiple instances of the conditionalized operation may - be executing simultaneously. - */ - static var isMutuallyExclusive: Bool { get } - - /** - Some conditions may have the ability to satisfy the condition if another - operation is executed first. Use this method to return an operation that - (for example) asks for permission to perform the operation - - - parameter operation: The `Operation` to which the Condition has been added. - - returns: An `NSOperation`, if a dependency should be automatically added. Otherwise, `nil`. - - note: Only a single operation may be returned as a dependency. If you - find that you need to return multiple operations, then you should be - expressing that as multiple conditions. Alternatively, you could return - a single `GroupOperation` that executes multiple operations internally. - */ - func dependencyForOperation(operation: Operation) -> NSOperation? - - /// Evaluate the condition, to see if it has been satisfied or not. - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) -} - -/** - An enum to indicate whether an `OperationCondition` was satisfied, or if it - failed with an error. -*/ -public enum OperationConditionResult { - case Satisfied - case Failed(NSError) - - var error: NSError? { - switch self { - case .Failed(let error): - return error - default: - return nil - } - } -} - -func ==(lhs: OperationConditionResult, rhs: OperationConditionResult) -> Bool { - switch (lhs, rhs) { - case (.Satisfied, .Satisfied): - return true - case (.Failed(let lError), .Failed(let rError)) where lError == rError: - return true - default: - return false - } -} - - -// MARK: Evaluate Conditions - -struct OperationConditionEvaluator { - static func evaluate(conditions: [OperationCondition], operation: Operation, completion: [NSError] -> Void) { - // Check conditions. - let conditionGroup = dispatch_group_create() - - var results = [OperationConditionResult?](count: conditions.count, repeatedValue: nil) - - // Ask each condition to evaluate and store its result in the "results" array. - for (index, condition) in conditions.enumerate() { - dispatch_group_enter(conditionGroup) - condition.evaluateForOperation(operation) { result in - results[index] = result - dispatch_group_leave(conditionGroup) - } - } - - // After all the conditions have evaluated, this block will execute. - dispatch_group_notify(conditionGroup, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { - // Aggregate the errors that occurred, in order. - var failures = results.flatMap { $0?.error } - - /* - If any of the conditions caused this operation to be cancelled, - check for that. - */ - if operation.cancelled { - failures.append(NSError(code: .ConditionFailed)) - } - - completion(failures) - } - } -} diff --git a/Kiwix/OperationLib/Conditions/PassbookCondition.swift b/Kiwix/OperationLib/Conditions/PassbookCondition.swift deleted file mode 100755 index 4acff65cc..000000000 --- a/Kiwix/OperationLib/Conditions/PassbookCondition.swift +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if os(iOS) - -import PassKit - -/// A condition for verifying that Passbook exists and is accessible. -@available(*, deprecated, message="use Capability(Passbook....) instead") - -public struct PassbookCondition: OperationCondition { - - public static let name = "Passbook" - public static let isMutuallyExclusive = false - - public init() { } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - /* - There's nothing you can do to make Passbook available if it's not - on your device. - */ - return nil - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - if PKPassLibrary.isPassLibraryAvailable() { - completion(.Satisfied) - } - else { - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name - ]) - - completion(.Failed(error)) - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/PhotosCondition.swift b/Kiwix/OperationLib/Conditions/PhotosCondition.swift deleted file mode 100755 index cd63c83a3..000000000 --- a/Kiwix/OperationLib/Conditions/PhotosCondition.swift +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if os(iOS) - -import Photos - -/// A condition for verifying access to the user's Photos library. -@available(*, deprecated, message="use Capability(Photos()) instead") - -public struct PhotosCondition: OperationCondition { - - public static let name = "Photos" - public static let isMutuallyExclusive = false - - public init() { } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return PhotosPermissionOperation() - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - switch PHPhotoLibrary.authorizationStatus() { - case .Authorized: - completion(.Satisfied) - - default: - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name - ]) - - completion(.Failed(error)) - } - } -} - -/** - A private `Operation` that will request access to the user's Photos, if it - has not already been granted. -*/ -class PhotosPermissionOperation: Operation { - override init() { - super.init() - - addCondition(AlertPresentation()) - } - - override func execute() { - switch PHPhotoLibrary.authorizationStatus() { - case .NotDetermined: - dispatch_async(dispatch_get_main_queue()) { - PHPhotoLibrary.requestAuthorization { status in - self.finish() - } - } - - default: - finish() - } - } - -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/RemoteNotificationCondition.swift b/Kiwix/OperationLib/Conditions/RemoteNotificationCondition.swift deleted file mode 100755 index f0087eadf..000000000 --- a/Kiwix/OperationLib/Conditions/RemoteNotificationCondition.swift +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if os(iOS) - -import UIKit - -private let RemoteNotificationQueue = OperationQueue() -private let RemoteNotificationName = "RemoteNotificationPermissionNotification" - -private enum RemoteRegistrationResult { - case Token(NSData) - case Error(NSError) -} - -/// A condition for verifying that the app has the ability to receive push notifications. -@available(*, deprecated, message="use Capability(Push(...)) instead") - -public struct RemoteNotificationCondition: OperationCondition { - public static let name = "RemoteNotification" - public static let isMutuallyExclusive = false - - static func didReceiveNotificationToken(token: NSData) { - NSNotificationCenter.defaultCenter().postNotificationName(RemoteNotificationName, object: nil, userInfo: [ - "token": token - ]) - } - - static func didFailToRegister(error: NSError) { - NSNotificationCenter.defaultCenter().postNotificationName(RemoteNotificationName, object: nil, userInfo: [ - "error": error - ]) - } - - let application: UIApplication - - public init(application: UIApplication) { - self.application = application - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return RemoteNotificationPermissionOperation(application: application, handler: { _ in }) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - /* - Since evaluation requires executing an operation, use a private operation - queue. - */ - RemoteNotificationQueue.addOperation(RemoteNotificationPermissionOperation(application: application) { result in - switch result { - case .Token(_): - completion(.Satisfied) - - case .Error(let underlyingError): - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - NSUnderlyingErrorKey: underlyingError - ]) - - completion(.Failed(error)) - } - }) - } -} - -/** - A private `Operation` to request a push notification token from the `UIApplication`. - - - note: This operation is used for *both* the generated dependency **and** - condition evaluation, since there is no "easy" way to retrieve the push - notification token other than to ask for it. - - - note: This operation requires you to call either `RemoteNotificationCondition.didReceiveNotificationToken(_:)` or - `RemoteNotificationCondition.didFailToRegister(_:)` in the appropriate - `UIApplicationDelegate` method, as shown in the `AppDelegate.swift` file. -*/ -class RemoteNotificationPermissionOperation: Operation { - let application: UIApplication - private let handler: RemoteRegistrationResult -> Void - - private init(application: UIApplication, handler: RemoteRegistrationResult -> Void) { - self.application = application - self.handler = handler - - super.init() - - /* - This operation cannot run at the same time as any other remote notification - permission operation. - */ - addCondition(MutuallyExclusive()) - } - - override func execute() { - dispatch_async(dispatch_get_main_queue()) { - let notificationCenter = NSNotificationCenter.defaultCenter() - - notificationCenter.addObserver(self, selector: #selector(RemoteNotificationPermissionOperation.didReceiveResponse(_:)), name: RemoteNotificationName, object: nil) - - self.application.registerForRemoteNotifications() - } - } - - @objc func didReceiveResponse(notification: NSNotification) { - NSNotificationCenter.defaultCenter().removeObserver(self) - - let userInfo = notification.userInfo - - if let token = userInfo?["token"] as? NSData { - handler(.Token(token)) - } - else if let error = userInfo?["error"] as? NSError { - handler(.Error(error)) - } - else { - fatalError("Received a notification without a token and without an error.") - } - - finish() - } -} - -#endif diff --git a/Kiwix/OperationLib/Conditions/SilentCondition.swift b/Kiwix/OperationLib/Conditions/SilentCondition.swift deleted file mode 100755 index c195ee245..000000000 --- a/Kiwix/OperationLib/Conditions/SilentCondition.swift +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -The file shows how to make an OperationCondition that composes another OperationCondition. -*/ - -import Foundation - -/** - A simple condition that causes another condition to not enqueue its dependency. - This is useful (for example) when you want to verify that you have access to - the user's location, but you do not want to prompt them for permission if you - do not already have it. -*/ -public -struct SilentCondition: OperationCondition { - let condition: T - - public static var name: String { - return "Silent<\(T.name)>" - } - - public static var isMutuallyExclusive: Bool { - return T.isMutuallyExclusive - } - - public init(condition: T) { - self.condition = condition - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - // Returning nil means we will never a dependency to be generated. - return nil - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - condition.evaluateForOperation(operation, completion: completion) - } -} diff --git a/Kiwix/OperationLib/Conditions/UserNotificationCondition.swift b/Kiwix/OperationLib/Conditions/UserNotificationCondition.swift deleted file mode 100755 index 3bf560f3f..000000000 --- a/Kiwix/OperationLib/Conditions/UserNotificationCondition.swift +++ /dev/null @@ -1,124 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -#if os(iOS) - -import UIKit - -/** - A condition for verifying that we can present alerts to the user via - `UILocalNotification` and/or remote notifications. -*/ -@available(*, deprecated, message="use Capability(UserNotification(...)) instead") - -public struct UserNotificationCondition: OperationCondition { - - public enum Behavior { - /// Merge the new `UIUserNotificationSettings` with the `currentUserNotificationSettings`. - case Merge - - /// Replace the `currentUserNotificationSettings` with the new `UIUserNotificationSettings`. - case Replace - } - - public static let name = "UserNotification" - static let currentSettings = "CurrentUserNotificationSettings" - static let desiredSettings = "DesiredUserNotificationSettigns" - public static let isMutuallyExclusive = false - - let settings: UIUserNotificationSettings - let application: UIApplication - let behavior: Behavior - - /** - The designated initializer. - - - parameter settings: The `UIUserNotificationSettings` you wish to be - registered. - - - parameter application: The `UIApplication` on which the `settings` should - be registered. - - - parameter behavior: The way in which the `settings` should be applied - to the `application`. By default, this value is `.Merge`, which means - that the `settings` will be combined with the existing settings on the - `application`. You may also specify `.Replace`, which means the `settings` - will overwrite the exisiting settings. - */ - public init(settings: UIUserNotificationSettings, application: UIApplication, behavior: Behavior = .Merge) { - self.settings = settings - self.application = application - self.behavior = behavior - } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return UserNotificationPermissionOperation(settings: settings, application: application, behavior: behavior) - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - let result: OperationConditionResult - - let current = application.currentUserNotificationSettings() - - switch (current, settings) { - case (let current?, let settings) where current.contains(settings): - result = .Satisfied - - default: - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.currentSettings: current ?? NSNull(), - self.dynamicType.desiredSettings: settings - ]) - - result = .Failed(error) - } - - completion(result) - } -} - -/** - A private `Operation` subclass to register a `UIUserNotificationSettings` - object with a `UIApplication`, prompting the user for permission if necessary. -*/ -private class UserNotificationPermissionOperation: Operation { - let settings: UIUserNotificationSettings - let application: UIApplication - let behavior: UserNotificationCondition.Behavior - - init(settings: UIUserNotificationSettings, application: UIApplication, behavior: UserNotificationCondition.Behavior) { - self.settings = settings - self.application = application - self.behavior = behavior - - super.init() - - addCondition(AlertPresentation()) - } - - override func execute() { - dispatch_async(dispatch_get_main_queue()) { - let current = self.application.currentUserNotificationSettings() - - let settingsToRegister: UIUserNotificationSettings - - switch (current, self.behavior) { - case (let currentSettings?, .Merge): - settingsToRegister = currentSettings.settingsByMerging(self.settings) - - default: - settingsToRegister = self.settings - } - - self.application.registerUserNotificationSettings(settingsToRegister) - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Exclusive/ExclusivityController.swift b/Kiwix/OperationLib/Exclusive/ExclusivityController.swift deleted file mode 100755 index 53530633c..000000000 --- a/Kiwix/OperationLib/Exclusive/ExclusivityController.swift +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -The file contains the code to automatically set up dependencies between mutually exclusive operations. -*/ - -import Foundation - -/** - `ExclusivityController` is a singleton to keep track of all the in-flight - `Operation` instances that have declared themselves as requiring mutual exclusivity. - We use a singleton because mutual exclusivity must be enforced across the entire - app, regardless of the `OperationQueue` on which an `Operation` was executed. -*/ -class ExclusivityController { - static let sharedExclusivityController = ExclusivityController() - - private let serialQueue = dispatch_queue_create("Operations.ExclusivityController", DISPATCH_QUEUE_SERIAL) - private var operations: [String: [Operation]] = [:] - - private init() { - /* - A private initializer effectively prevents any other part of the app - from accidentally creating an instance. - */ - } - - /// Registers an operation as being mutually exclusive - func addOperation(operation: Operation, categories: [String]) { - /* - This needs to be a synchronous operation. - If this were async, then we might not get around to adding dependencies - until after the operation had already begun, which would be incorrect. - */ - dispatch_sync(serialQueue) { - for category in categories { - self.noqueue_addOperation(operation, category: category) - } - } - } - - /// Unregisters an operation from being mutually exclusive. - func removeOperation(operation: Operation, categories: [String]) { - dispatch_async(serialQueue) { - for category in categories { - self.noqueue_removeOperation(operation, category: category) - } - } - } - - - // MARK: Operation Management - - private func noqueue_addOperation(operation: Operation, category: String) { - var operationsWithThisCategory = operations[category] ?? [] - - if let last = operationsWithThisCategory.last { - operation.addDependency(last) - } - - operationsWithThisCategory.append(operation) - - operations[category] = operationsWithThisCategory - } - - private func noqueue_removeOperation(operation: Operation, category: String) { - let matchingOperations = operations[category] - - if var operationsWithThisCategory = matchingOperations, - let index = operationsWithThisCategory.indexOf(operation) { - - operationsWithThisCategory.removeAtIndex(index) - operations[category] = operationsWithThisCategory - } - } - -} diff --git a/Kiwix/OperationLib/Exclusive/MutuallyExclusive.swift b/Kiwix/OperationLib/Exclusive/MutuallyExclusive.swift deleted file mode 100755 index ed761691d..000000000 --- a/Kiwix/OperationLib/Exclusive/MutuallyExclusive.swift +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows an example of implementing the OperationCondition protocol. -*/ - -import Foundation - -/// A generic condition for describing kinds of operations that may not execute concurrently. -public struct MutuallyExclusive: OperationCondition { - public static var name: String { - return "MutuallyExclusive<\(T.self)>" - } - - public static var isMutuallyExclusive: Bool { - return true - } - - public init() { } - - public func dependencyForOperation(operation: Operation) -> NSOperation? { - return nil - } - - public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - completion(.Satisfied) - } -} - -/** - The purpose of this enum is to simply provide a non-constructible - type to be used with `MutuallyExclusive`. -*/ -public enum Alert { } - -/// A condition describing that the targeted operation may present an alert. -public typealias AlertPresentation = MutuallyExclusive diff --git a/Kiwix/OperationLib/Observer/BlockObserver.swift b/Kiwix/OperationLib/Observer/BlockObserver.swift deleted file mode 100755 index e9f237d49..000000000 --- a/Kiwix/OperationLib/Observer/BlockObserver.swift +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows how to implement the OperationObserver protocol. -*/ - -import Foundation - -/** - The `BlockObserver` is a way to attach arbitrary blocks to significant events - in an `Operation`'s lifecycle. -*/ -public struct BlockObserver: OperationObserver { - // MARK: Properties - - private let startHandler: (Operation -> Void)? - private let cancelHandler: (Operation -> Void)? - private let produceHandler: ((Operation, NSOperation) -> Void)? - private let finishHandler: ((Operation, [NSError]) -> Void)? - - public init(startHandler: (Operation -> Void)? = nil, cancelHandler: (Operation -> Void)? = nil, produceHandler: ((Operation, NSOperation) -> Void)? = nil, finishHandler: ((Operation, [NSError]) -> Void)? = nil) { - self.startHandler = startHandler - self.cancelHandler = cancelHandler - self.produceHandler = produceHandler - self.finishHandler = finishHandler - } - - // MARK: OperationObserver - - public func operationDidStart(operation: Operation) { - startHandler?(operation) - } - - public func operationDidCancel(operation: Operation) { - cancelHandler?(operation) - } - - public func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { - produceHandler?(operation, newOperation) - } - - public func operationDidFinish(operation: Operation, errors: [NSError]) { - finishHandler?(operation, errors) - } -} diff --git a/Kiwix/OperationLib/Observer/OperationObserver.swift b/Kiwix/OperationLib/Observer/OperationObserver.swift deleted file mode 100755 index 78886e891..000000000 --- a/Kiwix/OperationLib/Observer/OperationObserver.swift +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file defines the OperationObserver protocol. -*/ - -import Foundation - -/** - The protocol that types may implement if they wish to be notified of significant - operation lifecycle events. -*/ -public protocol OperationObserver { - - /// Invoked immediately prior to the `Operation`'s `execute()` method. - func operationDidStart(operation: Operation) - - /// Invoked immediately after the first time the `Operation`'s `cancel()` method is called - func operationDidCancel(operation: Operation) - - /// Invoked when `Operation.produceOperation(_:)` is executed. - func operation(operation: Operation, didProduceOperation newOperation: NSOperation) - - /** - Invoked as an `Operation` finishes, along with any errors produced during - execution (or readiness evaluation). - */ - func operationDidFinish(operation: Operation, errors: [NSError]) - -} diff --git a/Kiwix/OperationLib/Observer/TimeoutObserver.swift b/Kiwix/OperationLib/Observer/TimeoutObserver.swift deleted file mode 100755 index 02de26104..000000000 --- a/Kiwix/OperationLib/Observer/TimeoutObserver.swift +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows how to implement the OperationObserver protocol. -*/ - -import Foundation - -/** - `TimeoutObserver` is a way to make an `Operation` automatically time out and - cancel after a specified time interval. -*/ -public struct TimeoutObserver: OperationObserver { - // MARK: Properties - - static let timeoutKey = "Timeout" - - private let timeout: NSTimeInterval - - // MARK: Initialization - - public init(timeout: NSTimeInterval) { - self.timeout = timeout - } - - // MARK: OperationObserver - - public func operationDidStart(operation: Operation) { - // When the operation starts, queue up a block to cause it to time out. - let when = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * Double(NSEC_PER_SEC))) - - dispatch_after(when, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { - /* - Cancel the operation if it hasn't finished and hasn't already - been cancelled. - */ - if !operation.finished && !operation.cancelled { - let error = NSError(code: .ExecutionFailed, userInfo: [ - self.dynamicType.timeoutKey: self.timeout - ]) - - operation.cancelWithError(error) - } - } - } - - public func operationDidCancel(operation: Operation) { - // No op. - } - - public func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { - // No op. - } - - public func operationDidFinish(operation: Operation, errors: [NSError]) { - // No op. - } -} diff --git a/Kiwix/OperationLib/Operations/BlockOperation.swift b/Kiwix/OperationLib/Operations/BlockOperation.swift deleted file mode 100755 index 53c4c6892..000000000 --- a/Kiwix/OperationLib/Operations/BlockOperation.swift +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This code shows how to create a simple subclass of Operation. -*/ - -import Foundation - -/// A closure type that takes a closure as its parameter. -public typealias OperationBlock = (Void -> Void) -> Void - -/// A sublcass of `Operation` to execute a closure. -public class BlockOperation: Operation { - private let block: OperationBlock? - - /** - The designated initializer. - - - parameter block: The closure to run when the operation executes. This - closure will be run on an arbitrary queue. The parameter passed to the - block **MUST** be invoked by your code, or else the `BlockOperation` - will never finish executing. If this parameter is `nil`, the operation - will immediately finish. - */ - public init(block: OperationBlock? = nil) { - self.block = block - super.init() - } - - /** - A convenience initializer to execute a block on the main queue. - - - parameter mainQueueBlock: The block to execute on the main queue. Note - that this block does not have a "continuation" block to execute (unlike - the designated initializer). The operation will be automatically ended - after the `mainQueueBlock` is executed. - */ - public convenience init(mainQueueBlock: dispatch_block_t) { - self.init(block: { continuation in - dispatch_async(dispatch_get_main_queue()) { - mainQueueBlock() - continuation() - } - }) - } - - override public func execute() { - if let block = block { - block { - self.finish() - } - } else { - finish() - } - } -} diff --git a/Kiwix/OperationLib/Operations/CKContainer+Operations.swift b/Kiwix/OperationLib/Operations/CKContainer+Operations.swift deleted file mode 100755 index 19c5d056a..000000000 --- a/Kiwix/OperationLib/Operations/CKContainer+Operations.swift +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -A convenient extension to CloudKit.CKContainer. -*/ - -#if !os(watchOS) - -import CloudKit - -extension CKContainer { - /** - Verify that the current user has certain permissions for the `CKContainer`, - and potentially requesting the permission if necessary. - - - parameter permission: The permissions to be verified on the container. - - - parameter shouldRequest: If this value is `true` and the user does not - have the passed `permission`, then the user will be prompted for it. - - - parameter completion: A closure that will be executed after verification - completes. The `NSError` passed in to the closure is the result of either - retrieving the account status, or requesting permission, if either - operation fails. If the verification was successful, this value will - be `nil`. - */ - func verifyPermission(permission: CKApplicationPermissions, requestingIfNecessary shouldRequest: Bool = false, completion: NSError? -> Void) { - verifyAccountStatus(self, permission: permission, shouldRequest: shouldRequest, completion: completion) - } -} - -/** - Make these helper functions instead of helper methods, so we don't pollute - `CKContainer`. -*/ -private func verifyAccountStatus(container: CKContainer, permission: CKApplicationPermissions, shouldRequest: Bool, completion: NSError? -> Void) { - container.accountStatusWithCompletionHandler { accountStatus, accountError in - if accountStatus == .Available { - if permission != CKApplicationPermissions() { - verifyPermission(container, permission: permission, shouldRequest: shouldRequest, completion: completion) - } - else { - completion(nil) - } - } - else { - let error = accountError ?? NSError(domain: CKErrorDomain, code: CKErrorCode.NotAuthenticated.rawValue, userInfo: nil) - completion(error) - } - } -} - -private func verifyPermission(container: CKContainer, permission: CKApplicationPermissions, shouldRequest: Bool, completion: NSError? -> Void) { - container.statusForApplicationPermission(permission) { permissionStatus, permissionError in - if permissionStatus == .Granted { - completion(nil) - } - else if permissionStatus == .InitialState && shouldRequest { - requestPermission(container, permission: permission, completion: completion) - } - else { - let error = permissionError ?? NSError(domain: CKErrorDomain, code: CKErrorCode.PermissionFailure.rawValue, userInfo: nil) - completion(error) - } - } -} - -private func requestPermission(container: CKContainer, permission: CKApplicationPermissions, completion: NSError? -> Void) { - dispatch_async(dispatch_get_main_queue()) { - container.requestApplicationPermission(permission) { requestStatus, requestError in - if requestStatus == .Granted { - completion(nil) - } - else { - let error = requestError ?? NSError(domain: CKErrorDomain, code: CKErrorCode.PermissionFailure.rawValue, userInfo: nil) - completion(error) - } - } - } -} - -#endif diff --git a/Kiwix/OperationLib/Operations/DelayOperation.swift b/Kiwix/OperationLib/Operations/DelayOperation.swift deleted file mode 100755 index 89da28aa9..000000000 --- a/Kiwix/OperationLib/Operations/DelayOperation.swift +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows how to make an operation that efficiently waits. -*/ - -import Foundation - -/** - `DelayOperation` is an `Operation` that will simply wait for a given time - interval, or until a specific `NSDate`. - - It is important to note that this operation does **not** use the `sleep()` - function, since that is inefficient and blocks the thread on which it is called. - Instead, this operation uses `dispatch_after` to know when the appropriate amount - of time has passed. - - If the interval is negative, or the `NSDate` is in the past, then this operation - immediately finishes. -*/ -public class DelayOperation: Operation { - // MARK: Types - - private enum Delay { - case Interval(NSTimeInterval) - case Date(NSDate) - } - - // MARK: Properties - - private let delay: Delay - - // MARK: Initialization - - public init(interval: NSTimeInterval) { - delay = .Interval(interval) - super.init() - } - - public init(until date: NSDate) { - delay = .Date(date) - super.init() - } - - override public func execute() { - let interval: NSTimeInterval - - // Figure out how long we should wait for. - switch delay { - case .Interval(let theInterval): - interval = theInterval - - case .Date(let date): - interval = date.timeIntervalSinceNow - } - - guard interval > 0 else { - finish() - return - } - - let when = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC))) - dispatch_after(when, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { - // If we were cancelled, then finish() has already been called. - if !self.cancelled { - self.finish() - } - } - } -} diff --git a/Kiwix/OperationLib/Operations/Dictionary+Operations.swift b/Kiwix/OperationLib/Operations/Dictionary+Operations.swift deleted file mode 100755 index 8ec8d8001..000000000 --- a/Kiwix/OperationLib/Operations/Dictionary+Operations.swift +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -A convenient extension to Swift.Dictionary. -*/ - -extension Dictionary { - /** - It's not uncommon to want to turn a sequence of values into a dictionary, - where each value is keyed by some unique identifier. This initializer will - do that. - - - parameter sequence: The sequence to be iterated - - - parameter keyer: The closure that will be executed for each element in - the `sequence`. The return value of this closure, if there is one, will - be used as the key for the value in the `Dictionary`. If the closure - returns `nil`, then the value will be omitted from the `Dictionary`. - */ - init(sequence: Sequence, keyMapper: Value -> Key?) { - self.init() - - for item in sequence { - if let key = keyMapper(item) { - self[key] = item - } - } - } -} diff --git a/Kiwix/OperationLib/Operations/GroupOperation.swift b/Kiwix/OperationLib/Operations/GroupOperation.swift deleted file mode 100755 index 2282429f2..000000000 --- a/Kiwix/OperationLib/Operations/GroupOperation.swift +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file shows how operations can be composed together to form new operations. -*/ - -import Foundation - -/** - A subclass of `Operation` that executes zero or more operations as part of its - own execution. This class of operation is very useful for abstracting several - smaller operations into a larger operation. As an example, the `GetEarthquakesOperation` - is composed of both a `DownloadEarthquakesOperation` and a `ParseEarthquakesOperation`. - - Additionally, `GroupOperation`s are useful if you establish a chain of dependencies, - but part of the chain may "loop". For example, if you have an operation that - requires the user to be authenticated, you may consider putting the "login" - operation inside a group operation. That way, the "login" operation may produce - subsequent operations (still within the outer `GroupOperation`) that will all - be executed before the rest of the operations in the initial chain of operations. -*/ -public class GroupOperation: Operation { - private let internalQueue = OperationQueue() - private let startingOperation = NSBlockOperation(block: {}) - private let finishingOperation = NSBlockOperation(block: {}) - - private var aggregatedErrors = [NSError]() - - public convenience init(operations: NSOperation...) { - self.init(operations: operations) - } - - public init(operations: [NSOperation]) { - super.init() - - internalQueue.suspended = true - internalQueue.delegate = self - internalQueue.addOperation(startingOperation) - - for operation in operations { - internalQueue.addOperation(operation) - } - } - - override public func cancel() { - internalQueue.cancelAllOperations() - internalQueue.suspended = false - super.cancel() - } - - override public func execute() { - internalQueue.suspended = false - internalQueue.addOperation(finishingOperation) - } - - public func addOperation(operation: NSOperation) { - internalQueue.addOperation(operation) - } - - /** - Note that some part of execution has produced an error. - Errors aggregated through this method will be included in the final array - of errors reported to observers and to the `finished(_:)` method. - */ - public final func aggregateError(error: NSError) { - aggregatedErrors.append(error) - } - - public func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) { - // For use by subclassers. - } -} - -extension GroupOperation: OperationQueueDelegate { - final public func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) { - assert(!finishingOperation.finished && !finishingOperation.executing, "cannot add new operations to a group after the group has completed") - - /* - Some operation in this group has produced a new operation to execute. - We want to allow that operation to execute before the group completes, - so we'll make the finishing operation dependent on this newly-produced operation. - */ - if operation !== finishingOperation { - finishingOperation.addDependency(operation) - } - - /* - All operations should be dependent on the "startingOperation". - This way, we can guarantee that the conditions for other operations - will not evaluate until just before the operation is about to run. - Otherwise, the conditions could be evaluated at any time, even - before the internal operation queue is unsuspended. - */ - if operation !== startingOperation { - operation.addDependency(startingOperation) - } - - } - - final public func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) { - aggregatedErrors.appendContentsOf(errors) - - if operation === finishingOperation { - internalQueue.suspended = true - finish(aggregatedErrors) - } - else if operation !== startingOperation { - operationDidFinish(operation, withErrors: errors) - } - } -} diff --git a/Kiwix/OperationLib/Operations/LocationOperation.swift b/Kiwix/OperationLib/Operations/LocationOperation.swift deleted file mode 100755 index 5f35db0d2..000000000 --- a/Kiwix/OperationLib/Operations/LocationOperation.swift +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -Shows how to retrieve the user's location with an operation. -*/ - -#if !os(OSX) - -import Foundation -import CoreLocation - -/** - `LocationOperation` is an `Operation` subclass to do a "one-shot" request to - get the user's current location, with a desired accuracy. This operation will - prompt for `WhenInUse` location authorization, if the app does not already - have it. - */ -public class LocationOperation: Operation, CLLocationManagerDelegate { - // MARK: Properties - - private let accuracy: CLLocationAccuracy - private var manager: CLLocationManager? - private let handler: CLLocation -> Void - - // MARK: Initialization - - public init(accuracy: CLLocationAccuracy, locationHandler: CLLocation -> Void) { - self.accuracy = accuracy - self.handler = locationHandler - super.init() - #if !os(tvOS) - addCondition(Capability(Location.WhenInUse)) - #else - addCondition(Capability(Location())) - #endif - addCondition(MutuallyExclusive()) - addObserver(BlockObserver(cancelHandler: { [weak self] _ in - dispatch_async(dispatch_get_main_queue()) { - self?.stopLocationUpdates() - } - })) - } - - override public func execute() { - dispatch_async(dispatch_get_main_queue()) { - /* - `CLLocationManager` needs to be created on a thread with an active - run loop, so for simplicity we do this on the main queue. - */ - let manager = CLLocationManager() - manager.desiredAccuracy = self.accuracy - manager.delegate = self - - manager.requestLocation() -// if #available(iOS 9.0, *) { -// manager.requestLocation() -// } else { -// #if !os(tvOS) && !os(watchOS) -// manager.startUpdatingLocation() -// #endif -// } - - self.manager = manager - } - } - - private func stopLocationUpdates() { - manager?.stopUpdatingLocation() - manager = nil - } - - // MARK: CLLocationManagerDelegate - - public func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - if let location = locations.last where location.horizontalAccuracy <= accuracy { - stopLocationUpdates() - handler(location) - finish() - } - } - - public func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { - stopLocationUpdates() - finishWithError(error) - } -} - -#endif diff --git a/Kiwix/OperationLib/Operations/NSLock+Operations.swift b/Kiwix/OperationLib/Operations/NSLock+Operations.swift deleted file mode 100755 index 438709754..000000000 --- a/Kiwix/OperationLib/Operations/NSLock+Operations.swift +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright (C) 2015 Apple Inc. All Rights Reserved. - See LICENSE.txt for this sample’s licensing information - - Abstract: - An extension to NSLock to simplify executing critical code. -*/ - -import Foundation - -extension NSLock { - func withCriticalScope(@noescape block: Void -> T) -> T { - lock() - let value = block() - unlock() - return value - } -} - -extension NSRecursiveLock { - func withCriticalScope(@noescape block: Void -> T) -> T { - lock() - let value = block() - unlock() - return value - } -} \ No newline at end of file diff --git a/Kiwix/OperationLib/Operations/NSOperation+Operations.swift b/Kiwix/OperationLib/Operations/NSOperation+Operations.swift deleted file mode 100755 index e5c44a787..000000000 --- a/Kiwix/OperationLib/Operations/NSOperation+Operations.swift +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -A convenient extension to Foundation.NSOperation. -*/ - -import Foundation - -extension NSOperation { - /** - Add a completion block to be executed after the `NSOperation` enters the - "finished" state. - */ - func addCompletionBlock(block: Void -> Void) { - if let existing = completionBlock { - /* - If we already have a completion block, we construct a new one by - chaining them together. - */ - completionBlock = { - existing() - block() - } - } - else { - completionBlock = block - } - } - - /// Add multiple depdendencies to the operation. - func addDependencies(dependencies: [NSOperation]) { - for dependency in dependencies { - addDependency(dependency) - } - } -} diff --git a/Kiwix/OperationLib/Operations/Operation.swift b/Kiwix/OperationLib/Operations/Operation.swift deleted file mode 100755 index 4c28381a2..000000000 --- a/Kiwix/OperationLib/Operations/Operation.swift +++ /dev/null @@ -1,412 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file contains the foundational subclass of NSOperation. -*/ - -import Foundation - -/** - The subclass of `NSOperation` from which all other operations should be derived. - This class adds both Conditions and Observers, which allow the operation to define - extended readiness requirements, as well as notify many interested parties - about interesting operation state changes -*/ -public class Operation: NSOperation { - - // use the KVO mechanism to indicate that changes to "state" affect other properties as well - class func keyPathsForValuesAffectingIsReady() -> Set { - return ["state", "cancelledState"] - } - - class func keyPathsForValuesAffectingIsExecuting() -> Set { - return ["state"] - } - - class func keyPathsForValuesAffectingIsFinished() -> Set { - return ["state"] - } - - class func keyPathsForValuesAffectingIsCancelled() -> Set { - return ["cancelledState"] - } - - // MARK: State Management - - private enum State: Int, Comparable { - /// The initial state of an `Operation`. - case Initialized - - /// The `Operation` is ready to begin evaluating conditions. - case Pending - - /// The `Operation` is evaluating conditions. - case EvaluatingConditions - - /** - The `Operation`'s conditions have all been satisfied, and it is ready - to execute. - */ - case Ready - - /// The `Operation` is executing. - case Executing - - /** - Execution of the `Operation` has finished, but it has not yet notified - the queue of this. - */ - case Finishing - - /// The `Operation` has finished executing. - case Finished - - func canTransitionToState(target: State, operationIsCancelled cancelled: Bool) -> Bool { - switch (self, target) { - case (.Initialized, .Pending): - return true - case (.Pending, .EvaluatingConditions): - return true - case (.Pending, .Finishing) where cancelled: - return true - case (.Pending, .Ready) where cancelled: - return true - case (.EvaluatingConditions, .Ready): - return true - case (.Ready, .Executing): - return true - case (.Ready, .Finishing): - return true - case (.Executing, .Finishing): - return true - case (.Finishing, .Finished): - return true - default: - return false - } - } - } - - /** - Indicates that the Operation can now begin to evaluate readiness conditions, - if appropriate. - */ - func didEnqueue() { - state = .Pending - } - - /// Private storage for the `state` property that will be KVO observed. - private var _state = State.Initialized - - /// A lock to guard reads and writes to the `_state` property - private let stateLock = NSLock() - - private var state: State { - get { - return stateLock.withCriticalScope { - _state - } - } - - set(newState) { - /* - It's important to note that the KVO notifications are NOT called from inside - the lock. If they were, the app would deadlock, because in the middle of - calling the `didChangeValueForKey()` method, the observers try to access - properties like "isReady" or "isFinished". Since those methods also - acquire the lock, then we'd be stuck waiting on our own lock. It's the - classic definition of deadlock. - */ - willChangeValueForKey("state") - - stateLock.withCriticalScope { Void -> Void in - guard _state != .Finished else { - return - } - - assert(_state.canTransitionToState(newState, operationIsCancelled: cancelled), "Performing invalid state transition.") - _state = newState - } - - didChangeValueForKey("state") - } - } - - private let readyLock = NSRecursiveLock() - - // Here is where we extend our definition of "readiness". - override public var ready: Bool { - - var _ready = false - - readyLock.withCriticalScope { - switch state { - - case .Initialized: - // If the operation has been cancelled, "isReady" should return true - _ready = cancelled - - case .Pending: - // If the operation has been cancelled, "isReady" should return true - guard !cancelled else { - state = .Ready - _ready = true - return - } - - // If super isReady, conditions can be evaluated - if super.ready { - evaluateConditions() - } - - // Until conditions have been evaluated, "isReady" returns false - _ready = false - - case .Ready: - _ready = super.ready || cancelled - - default: - _ready = false - } - - } - - return _ready - } - - public var userInitiated: Bool { - get { - return qualityOfService == .UserInitiated - } - - set { - assert(state < .Executing, "Cannot modify userInitiated after execution has begun.") - - qualityOfService = newValue ? .UserInitiated : .Default - } - } - - override public var executing: Bool { - return state == .Executing - } - - override public var finished: Bool { - return state == .Finished - } - - var _cancelled = false { - willSet { - willChangeValueForKey("cancelledState") - } - - didSet { - didChangeValueForKey("cancelledState") - if _cancelled != oldValue && _cancelled == true { - - for observer in observers { - observer.operationDidCancel(self) - } - - } - } - } - - override public var cancelled: Bool { - return _cancelled - } - - - private func evaluateConditions() { - assert(state == .Pending && !cancelled, "evaluateConditions() was called out-of-order") - - state = .EvaluatingConditions - - guard conditions.count > 0 else { - state = .Ready - return - } - - OperationConditionEvaluator.evaluate(conditions, operation: self) { failures in - if !failures.isEmpty { - self.cancelWithErrors(failures) - } - - //We must preceed to have the operation exit the queue - self.state = .Ready - } - } - - // MARK: Observers and Conditions - - private(set) var conditions = [OperationCondition]() - - public func addCondition(condition: OperationCondition) { - assert(state < .EvaluatingConditions, "Cannot modify conditions after execution has begun.") - - conditions.append(condition) - } - - private(set) var observers = [OperationObserver]() - - public func addObserver(observer: OperationObserver) { - assert(state < .Executing, "Cannot modify observers after execution has begun.") - - observers.append(observer) - } - - override public func addDependency(operation: NSOperation) { - assert(state <= .Executing, "Dependencies cannot be modified after execution has begun.") - - super.addDependency(operation) - } - - // MARK: Execution and Cancellation - - override final public func start() { - // NSOperation.start() contains important logic that shouldn't be bypassed. - super.start() - - // If the operation has been cancelled, we still need to enter the "Finished" state. - if cancelled { - finish() - } - } - - override final public func main() { - assert(state == .Ready, "This operation must be performed on an operation queue.") - - if _internalErrors.isEmpty && !cancelled { - state = .Executing - - for observer in observers { - observer.operationDidStart(self) - } - - execute() - } - else { - finish() - } - } - - /** - `execute()` is the entry point of execution for all `Operation` subclasses. - If you subclass `Operation` and wish to customize its execution, you would - do so by overriding the `execute()` method. - - At some point, your `Operation` subclass must call one of the "finish" - methods defined below; this is how you indicate that your operation has - finished its execution, and that operations dependent on yours can re-evaluate - their readiness state. - */ - public func execute() { - print("\(self.dynamicType) must override `execute()`.") - - finish() - } - - private var _internalErrors = [NSError]() - override public func cancel() { - if finished { - return - } - - _cancelled = true - - if state > .Ready { - finish() - } - } - - public func cancelWithErrors(errors: [NSError]) { - _internalErrors += errors - cancel() - } - - public func cancelWithError(error: NSError) { - cancelWithErrors([error]) - } - - public final func produceOperation(operation: NSOperation) { - for observer in observers { - observer.operation(self, didProduceOperation: operation) - } - } - - // MARK: Finishing - - /** - Most operations may finish with a single error, if they have one at all. - This is a convenience method to simplify calling the actual `finish()` - method. This is also useful if you wish to finish with an error provided - by the system frameworks. As an example, see `DownloadEarthquakesOperation` - for how an error from an `NSURLSession` is passed along via the - `finishWithError()` method. - */ - public final func finishWithError(error: NSError?) { - if let error = error { - finish([error]) - } - else { - finish() - } - } - - /** - A private property to ensure we only notify the observers once that the - operation has finished. - */ - private var hasFinishedAlready = false - public final func finish(errors: [NSError] = []) { - if !hasFinishedAlready { - hasFinishedAlready = true - state = .Finishing - - let combinedErrors = _internalErrors + errors - finished(combinedErrors) - - for observer in observers { - observer.operationDidFinish(self, errors: combinedErrors) - } - - state = .Finished - } - } - - /** - Subclasses may override `finished(_:)` if they wish to react to the operation - finishing with errors. For example, the `LoadModelOperation` implements - this method to potentially inform the user about an error when trying to - bring up the Core Data stack. - */ - public func finished(errors: [NSError]) { - // No op. - } - - override public func waitUntilFinished() { - /* - Waiting on operations is almost NEVER the right thing to do. It is - usually superior to use proper locking constructs, such as `dispatch_semaphore_t` - or `dispatch_group_notify`, or even `NSLocking` objects. Many developers - use waiting when they should instead be chaining discrete operations - together using dependencies. - - To reinforce this idea, invoking `waitUntilFinished()` will crash your - app, as incentive for you to find a more appropriate way to express - the behavior you're wishing to create. - */ - fatalError("Waiting on operations is an anti-pattern. Remove this ONLY if you're absolutely sure there is No Other Way™.") - } - -} - -// Simple operator functions to simplify the assertions used above. -private func <(lhs: Operation.State, rhs: Operation.State) -> Bool { - return lhs.rawValue < rhs.rawValue -} - -private func ==(lhs: Operation.State, rhs: Operation.State) -> Bool { - return lhs.rawValue == rhs.rawValue -} diff --git a/Kiwix/OperationLib/Operations/OperationErrors.swift b/Kiwix/OperationLib/Operations/OperationErrors.swift deleted file mode 100755 index 5717c8222..000000000 --- a/Kiwix/OperationLib/Operations/OperationErrors.swift +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file defines the error codes and convenience functions for interacting with Operation-related errors. -*/ - -import Foundation - -public let OperationErrorDomain = "OperationErrors" - -public enum OperationErrorCode: Int { - case ConditionFailed = 1 - case ExecutionFailed = 2 - - // Error that should be reported to user - case NetworkError - case ServerNameInvalid - case AuthError - case AccessRevoked - case Unreachable - case LackOfValue - case UnexpectedError -} - -public extension NSError { - convenience init(code: OperationErrorCode, userInfo: [NSObject: AnyObject]? = nil) { - self.init(domain: OperationErrorDomain, code: code.rawValue, userInfo: userInfo) - } -} - -// This makes it easy to compare an `NSError.code` to an `OperationErrorCode`. -public func ==(lhs: Int, rhs: OperationErrorCode) -> Bool { - return lhs == rhs.rawValue -} - -public func ==(lhs: OperationErrorCode, rhs: Int) -> Bool { - return lhs.rawValue == rhs -} diff --git a/Kiwix/OperationLib/Operations/OperationQueue.swift b/Kiwix/OperationLib/Operations/OperationQueue.swift deleted file mode 100755 index 60cf50a86..000000000 --- a/Kiwix/OperationLib/Operations/OperationQueue.swift +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -This file contains an NSOperationQueue subclass. -*/ - -import Foundation - -/** - The delegate of an `OperationQueue` can respond to `Operation` lifecycle - events by implementing these methods. - - In general, implementing `OperationQueueDelegate` is not necessary; you would - want to use an `OperationObserver` instead. However, there are a couple of - situations where using `OperationQueueDelegate` can lead to simpler code. - For example, `GroupOperation` is the delegate of its own internal - `OperationQueue` and uses it to manage dependencies. -*/ -@objc public protocol OperationQueueDelegate: NSObjectProtocol { - optional func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) - optional func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) -} - -/** - `OperationQueue` is an `NSOperationQueue` subclass that implements a large - number of "extra features" related to the `Operation` class: - - - Notifying a delegate of all operation completion - - Extracting generated dependencies from operation conditions - - Setting up dependencies to enforce mutual exclusivity -*/ -public class OperationQueue: NSOperationQueue { - private let opsQueue = dispatch_queue_create("com.advancedOperations", DISPATCH_QUEUE_SERIAL) - private var ops: Set = Set() - - public weak var delegate: OperationQueueDelegate? - - override public func addOperation(operation: NSOperation) { - addOpToSet(operation) - if let op = operation as? Operation { - - // Set up a `BlockObserver` to invoke the `OperationQueueDelegate` method. - let delegate = BlockObserver( - startHandler: nil, - produceHandler: { [weak self] in - self?.addOperation($1) - }, - finishHandler: { [weak self] in - if let q = self { - q.delegate?.operationQueue?(q, operationDidFinish: $0, withErrors: $1) - q.removeOpFromSet($0) - } - } - ) - op.addObserver(delegate) - - // Extract any dependencies needed by this operation. - let dependencies = op.conditions.flatMap { - $0.dependencyForOperation(op) - } - - for dependency in dependencies { - op.addDependency(dependency) - - self.addOperation(dependency) - } - - /* - With condition dependencies added, we can now see if this needs - dependencies to enforce mutual exclusivity. - */ - let concurrencyCategories: [String] = op.conditions.flatMap { condition in - if !condition.dynamicType.isMutuallyExclusive { return nil } - - return "\(condition.dynamicType)" - } - - if !concurrencyCategories.isEmpty { - // Set up the mutual exclusivity dependencies. - let exclusivityController = ExclusivityController.sharedExclusivityController - - exclusivityController.addOperation(op, categories: concurrencyCategories) - - op.addObserver(BlockObserver { operation, _ in - exclusivityController.removeOperation(operation, categories: concurrencyCategories) - }) - } - } - else { - /* - For regular `NSOperation`s, we'll manually call out to the queue's - delegate we don't want to just capture "operation" because that - would lead to the operation strongly referencing itself and that's - the pure definition of a memory leak. - */ - operation.addCompletionBlock { [weak self, weak operation] in - guard let queue = self, let operation = operation else { return } - queue.delegate?.operationQueue?(queue, operationDidFinish: operation, withErrors: []) - queue.removeOpFromSet(operation) - } - } - - delegate?.operationQueue?(self, willAddOperation: operation) - super.addOperation(operation) - - /* - Indicate to the operation that we've finished our extra work on it - and it's now it a state where it can proceed with evaluating conditions, - if appropriate. - */ - if let op = operation as? Operation { - op.didEnqueue() - } - } - - override public func addOperations(ops: [NSOperation], waitUntilFinished wait: Bool) { - /* - The base implementation of this method does not call `addOperation()`, - so we'll call it ourselves. - */ - for operation in ops { - addOperation(operation) - } - - if wait { - for operation in ops { - operation.waitUntilFinished() - } - } - } - - func addOpToSet(op: NSOperation) { - dispatch_async(opsQueue) { - self.ops.insert(op) - } - } - - func removeOpFromSet(op: NSOperation) { - dispatch_async(opsQueue) { - self.ops.remove(op) - } - } -} - -extension OperationQueue { - func getOperation(name: String) -> Operation? { - for operation in operations { - guard let operation = operation as? Operation else {continue} - guard operation.name == name else {continue} - return operation - } - return nil - } -} diff --git a/Kiwix/OperationLib/Operations/UIUserNotifications+Operations.swift b/Kiwix/OperationLib/Operations/UIUserNotifications+Operations.swift deleted file mode 100755 index cc1af71be..000000000 --- a/Kiwix/OperationLib/Operations/UIUserNotifications+Operations.swift +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -A convenient extension to UIKit.UIUserNotificationSettings. -*/ - -#if os(iOS) - -import UIKit - -extension UIUserNotificationSettings { - /// Check to see if one Settings object is a superset of another Settings object. - func contains(settings: UIUserNotificationSettings) -> Bool { - // our types must contain all of the other types - if !types.contains(settings.types) { - return false - } - - let otherCategories = settings.categories ?? [] - let myCategories = categories ?? [] - - return myCategories.isSupersetOf(otherCategories) - } - - /** - Merge two Settings objects together. `UIUserNotificationCategories` with - the same identifier are considered equal. - */ - func settingsByMerging(settings: UIUserNotificationSettings) -> UIUserNotificationSettings { - let mergedTypes = types.union(settings.types) - - let myCategories = categories ?? [] - var existingCategoriesByIdentifier = Dictionary(sequence: myCategories) { $0.identifier } - - let newCategories = settings.categories ?? [] - let newCategoriesByIdentifier = Dictionary(sequence: newCategories) { $0.identifier } - - for (newIdentifier, newCategory) in newCategoriesByIdentifier { - existingCategoriesByIdentifier[newIdentifier] = newCategory - } - - let mergedCategories = Set(existingCategoriesByIdentifier.values) - return UIUserNotificationSettings(forTypes: mergedTypes, categories: mergedCategories) - } -} - -#endif diff --git a/Kiwix/OperationLib/Operations/URLSessionTaskOperation.swift b/Kiwix/OperationLib/Operations/URLSessionTaskOperation.swift deleted file mode 100755 index 108982006..000000000 --- a/Kiwix/OperationLib/Operations/URLSessionTaskOperation.swift +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright (C) 2015 Apple Inc. All Rights Reserved. -See LICENSE.txt for this sample’s licensing information - -Abstract: -Shows how to lift operation-like objects in to the NSOperation world. -*/ - -import Foundation - -private var URLSessionTaskOperationKVOContext = 0 - -/** - `URLSessionTaskOperation` is an `Operation` that lifts an `NSURLSessionTask` - into an operation. - - Note that this operation does not participate in any of the delegate callbacks \ - of an `NSURLSession`, but instead uses Key-Value-Observing to know when the - task has been completed. It also does not get notified about any errors that - occurred during execution of the task. - - An example usage of `URLSessionTaskOperation` can be seen in the `DownloadEarthquakesOperation`. -*/ -public class URLSessionTaskOperation: Operation { - let task: NSURLSessionTask - var produceResumeDataWhenCancel: Bool - - private var observerRemoved = false - private let stateLock = NSLock() - - public init(task: NSURLSessionTask, produceResumeDataWhenCancel: Bool = false) { - assert(task.state == .Suspended, "Tasks must be suspended.") - self.task = task - self.produceResumeDataWhenCancel = produceResumeDataWhenCancel - super.init() - - addObserver(BlockObserver(cancelHandler: { _ in - if let task = task as? NSURLSessionDownloadTask { - guard produceResumeDataWhenCancel else {return} - task.cancelByProducingResumeData({ (data) -> Void in - }) - } else { - task.cancel() - } - })) - } - - override public func execute() { - assert(task.state == .Suspended, "Task was resumed by something other than \(self).") - - task.addObserver(self, forKeyPath: "state", options: NSKeyValueObservingOptions(), context: &URLSessionTaskOperationKVOContext) - - task.resume() - } - - override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { - guard context == &URLSessionTaskOperationKVOContext else { return } - - stateLock.withCriticalScope { - if object === task && keyPath == "state" && !observerRemoved { - switch task.state { - case .Completed: - finish() - fallthrough - case .Canceling: - observerRemoved = true - task.removeObserver(self, forKeyPath: "state") - default: - return - } - } - } - } -} diff --git a/Kiwix/OperationLib/Operations/AlertOperation.swift b/Kiwix/Operations/AlertOperation.swift similarity index 99% rename from Kiwix/OperationLib/Operations/AlertOperation.swift rename to Kiwix/Operations/AlertOperation.swift index 6932fcd0f..31ba58b68 100644 --- a/Kiwix/OperationLib/Operations/AlertOperation.swift +++ b/Kiwix/Operations/AlertOperation.swift @@ -7,6 +7,7 @@ This file shows how to present an alert as part of an operation. */ import UIKit +import PSOperations class AlertOperation: Operation { // MARK: Properties diff --git a/Kiwix/Operations/GlobalOperationQueue.swift b/Kiwix/Operations/GlobalOperationQueue.swift new file mode 100644 index 000000000..3a942197e --- /dev/null +++ b/Kiwix/Operations/GlobalOperationQueue.swift @@ -0,0 +1,43 @@ +// +// GlobalOperationQueue.swift +// Kiwix +// +// Created by Chris Li on 5/14/16. +// Copyright © 2016 Chris. All rights reserved. +// + +import PSOperations + +class GlobalOperationQueue: OperationQueue { + static let sharedInstance = GlobalOperationQueue() + +// var isRefreshingLibrary: Bool { +// return getOperation(String(RefreshLibraryOperation)) != nil +// } +} + +public enum OperationErrorCode: Int { + case ConditionFailed = 1 + case ExecutionFailed = 2 + + // Error that should be reported to user + case NetworkError + case ServerNameInvalid + case AuthError + case AccessRevoked + case Unreachable + case LackOfValue + case UnexpectedError +} + +extension OperationQueue { + // Oneday should be replaced with ExclusivityController + func getOperation(id: String) -> Operation? { + for operation in operations { + guard operation.name == id else {continue} + guard let operation = operation as? Operation else {continue} + return operation + } + return nil + } +} diff --git a/Kiwix/OperationLib/Observer/NetworkObserver.swift b/Kiwix/Operations/NetworkObserver.swift similarity index 99% rename from Kiwix/OperationLib/Observer/NetworkObserver.swift rename to Kiwix/Operations/NetworkObserver.swift index b84e40330..9b5d4a511 100644 --- a/Kiwix/OperationLib/Observer/NetworkObserver.swift +++ b/Kiwix/Operations/NetworkObserver.swift @@ -7,6 +7,7 @@ Contains the code to manage the visibility of the network activity indicator */ import UIKit +import PSOperations /** An `OperationObserver` that will cause the network activity indicator to appear diff --git a/Kiwix/RefreshLibraryOperation.swift b/Kiwix/Operations/RefreshLibraryOperation.swift similarity index 99% rename from Kiwix/RefreshLibraryOperation.swift rename to Kiwix/Operations/RefreshLibraryOperation.swift index ac7b624d9..b5750521e 100644 --- a/Kiwix/RefreshLibraryOperation.swift +++ b/Kiwix/Operations/RefreshLibraryOperation.swift @@ -7,6 +7,7 @@ // import CoreData +import PSOperations class RefreshLibraryOperation: GroupOperation { diff --git a/Kiwix/SearchOperation.swift b/Kiwix/Operations/SearchOperation.swift similarity index 99% rename from Kiwix/SearchOperation.swift rename to Kiwix/Operations/SearchOperation.swift index 1408b7faa..185677e8d 100644 --- a/Kiwix/SearchOperation.swift +++ b/Kiwix/Operations/SearchOperation.swift @@ -7,6 +7,7 @@ // import UIKit +import PSOperations class SearchOperation: GroupOperation { let completionHandler: ([SearchResult]) -> Void diff --git a/Kiwix/UIOperations.swift b/Kiwix/Operations/UIOperations.swift similarity index 99% rename from Kiwix/UIOperations.swift rename to Kiwix/Operations/UIOperations.swift index 7d929594a..12116952a 100644 --- a/Kiwix/UIOperations.swift +++ b/Kiwix/Operations/UIOperations.swift @@ -7,6 +7,7 @@ // import UIKit +import PSOperations // MARK: - Alerts diff --git a/Kiwix/Operations/URLSessionDownloadTaskOperation.swift b/Kiwix/Operations/URLSessionDownloadTaskOperation.swift new file mode 100644 index 000000000..a605fa617 --- /dev/null +++ b/Kiwix/Operations/URLSessionDownloadTaskOperation.swift @@ -0,0 +1,31 @@ +// +// URLSessionDownloadTaskOperation.swift +// Kiwix +// +// Created by Chris Li on 7/11/16. +// Copyright © 2016 Chris. All rights reserved. +// + +import UIKit +import PSOperations + +public class URLSessionDownloadTaskOperation: URLSessionTaskOperation { + let task: NSURLSessionDownloadTask + var produceResumeData: Bool + + public init(task: NSURLSessionDownloadTask, produceResumeData: Bool) { + assert(task.state == .Suspended, "Tasks must be suspended.") + self.task = task + self.produceResumeData = produceResumeData + super.init(task: task) + + addObserver(BlockObserver(cancelHandler: { _ in + if produceResumeData { + task.cancelByProducingResumeData({ (data) in + }) + } else { + task.cancel() + } + })) + } +} diff --git a/Kiwix/OperationLib/Conditions/ReachabilityCondition.swift b/Kiwix/ReachabilityCondition.swift similarity index 97% rename from Kiwix/OperationLib/Conditions/ReachabilityCondition.swift rename to Kiwix/ReachabilityCondition.swift index 83279b8e8..91ebbcef1 100644 --- a/Kiwix/OperationLib/Conditions/ReachabilityCondition.swift +++ b/Kiwix/ReachabilityCondition.swift @@ -8,6 +8,7 @@ This file shows an example of implementing the OperationCondition protocol. import Foundation import SystemConfiguration +import PSOperations /** This is a condition that performs a very high-level reachability check. @@ -39,7 +40,7 @@ struct ReachabilityCondition: OperationCondition { else { let userInfo = ["title": "Network Error", "message": "Unable connecting to the internet. Please check your connection."] - let error = NSError(code: .NetworkError, userInfo: userInfo) + let error = NSError(code: .ConditionFailed, userInfo: userInfo) completion(.Failed(error)) } } @@ -73,8 +74,6 @@ private class ReachabilityController { } } } else { - // completionHandler(false) - // Test for general internet connectibility dispatch_async(reachabilityQueue) { var zeroAddress = sockaddr_in() diff --git a/Kiwix/ZimMultiReader.swift b/Kiwix/ZimMultiReader.swift index 53e16bb07..de62e6980 100644 --- a/Kiwix/ZimMultiReader.swift +++ b/Kiwix/ZimMultiReader.swift @@ -7,6 +7,7 @@ // import CoreData +import PSOperations class ZIMMultiReader: NSObject, DirectoryMonitorDelegate { diff --git a/Podfile b/Podfile index 65ce8c349..164a54a2b 100644 --- a/Podfile +++ b/Podfile @@ -8,6 +8,7 @@ platform :ios, '9.0' pod 'DateTools' pod 'DZNEmptyDataSet' pod 'SwiftyUserDefaults' +pod 'PSOperations', '~> 2.3' end diff --git a/Podfile.lock b/Podfile.lock index f2637c2d4..052eb28b1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,16 +1,29 @@ PODS: - DateTools (1.7.0) - DZNEmptyDataSet (1.8) + - PSOperations (2.3.0): + - PSOperations/Core (= 2.3.0) + - PSOperations/Health (= 2.3.0) + - PSOperations/Passbook (= 2.3.0) + - PSOperations/Core (2.3.0) + - PSOperations/Health (2.3.0): + - PSOperations/Core + - PSOperations/Passbook (2.3.0): + - PSOperations/Core - SwiftyUserDefaults (2.2.0) DEPENDENCIES: - DateTools - DZNEmptyDataSet + - PSOperations (~> 2.3) - SwiftyUserDefaults SPEC CHECKSUMS: DateTools: 53288ee8b905fdc75897a1e6b5cc0144b14cba60 DZNEmptyDataSet: d2351b2e8daefa40433ef292e246e21f6be31a7b + PSOperations: 81a4d50a4efe96f6d090dc0e7eb45d9fef79ff45 SwiftyUserDefaults: e7c8197a9d4068702e7f2406a968cae413b8de27 -COCOAPODS: 0.39.0 +PODFILE CHECKSUM: 6fb3df3215e86292ba3af103a30d0553a9f49e94 + +COCOAPODS: 1.0.1 From 12873155663ea9835b4b9f1ae433f4dd0e11e45e Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 11 Jul 2016 16:38:40 -0400 Subject: [PATCH 04/54] Fix Network activity indicator retain count --- Kiwix-iOS/Controller/LibraryOnlineTBVC.swift | 8 ++++++ Kiwix/Operations/NetworkObserver.swift | 8 +----- .../Operations/RefreshLibraryOperation.swift | 26 ------------------- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/Kiwix-iOS/Controller/LibraryOnlineTBVC.swift b/Kiwix-iOS/Controller/LibraryOnlineTBVC.swift index 948363230..ae848170e 100644 --- a/Kiwix-iOS/Controller/LibraryOnlineTBVC.swift +++ b/Kiwix-iOS/Controller/LibraryOnlineTBVC.swift @@ -76,6 +76,14 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega } func startRefresh(invokedAutomatically invokedAutomatically: Bool) { + if invokedAutomatically { + let libraryIsOld: Bool = { + guard let lastLibraryRefreshTime = Preference.libraryLastRefreshTime else {return true} + return -lastLibraryRefreshTime.timeIntervalSinceNow > Preference.libraryRefreshInterval + }() + guard libraryIsOld else {return} + } + let refreshOperation = RefreshLibraryOperation(invokedAutomatically: invokedAutomatically) { (errors) in defer { NSOperationQueue.mainQueue().addOperationWithBlock({ diff --git a/Kiwix/Operations/NetworkObserver.swift b/Kiwix/Operations/NetworkObserver.swift index 9b5d4a511..60084e34e 100644 --- a/Kiwix/Operations/NetworkObserver.swift +++ b/Kiwix/Operations/NetworkObserver.swift @@ -29,20 +29,14 @@ struct NetworkObserver: OperationObserver { func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { } func operationDidFinish(operation: Operation, errors: [NSError]) { - // If an op is cancelled, operationDidStart will not be called print("NetworkObserver: \(operation.name ?? "Unknown") operation did finish") - guard !operation.cancelled else {return} - dispatch_async(dispatch_get_main_queue()) { // Decrement the network indicator's "reference count". NetworkIndicatorController.sharedIndicatorController.networkActivityDidEnd() } } - func operationDidCancel(operation: Operation) { - print("NetworkObserver: \(operation.name ?? "Unknown") operation did cancel") - } - + func operationDidCancel(operation: Operation) { } } /// A singleton to manage a visual "reference count" on the network activity indicator. diff --git a/Kiwix/Operations/RefreshLibraryOperation.swift b/Kiwix/Operations/RefreshLibraryOperation.swift index b5750521e..415f82673 100644 --- a/Kiwix/Operations/RefreshLibraryOperation.swift +++ b/Kiwix/Operations/RefreshLibraryOperation.swift @@ -38,7 +38,6 @@ class RefreshLibraryOperation: GroupOperation { if invokedAutomatically { addCondition(AllowAutoRefreshCondition()) - addCondition(LibraryIsOldCondition()) } addOperation(fetchOperation) @@ -129,31 +128,6 @@ class ParseLibraryOperation: Operation, NSXMLParserDelegate { } } -private struct LibraryIsOldCondition: OperationCondition { - static let name = "LibraryIsOld" - static let isMutuallyExclusive = false - - init() {} - - func dependencyForOperation(operation: Operation) -> NSOperation? { - return nil - } - - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - let libraryIsOld: Bool = { - guard let lastLibraryRefreshTime = Preference.libraryLastRefreshTime else {return true} - return -lastLibraryRefreshTime.timeIntervalSinceNow > Preference.libraryRefreshInterval - }() - - if libraryIsOld { - completion(.Satisfied) - } else { - let error = NSError(code: .ConditionFailed, userInfo: [OperationConditionKey: self.dynamicType.name]) - completion(.Failed(error)) - } - } -} - private struct AllowAutoRefreshCondition: OperationCondition { static let name = "LibraryAllowAutoRefresh" static let isMutuallyExclusive = false From 3c3175bc63a6778f4dda52920cf80367c9cc87ef Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 11 Jul 2016 16:53:42 -0400 Subject: [PATCH 05/54] Update git ignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3641bab65..93d58b16b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ Pods Kiwix/libkiwix/C&C++ Kiwix/libkiwix/include Kiwix/libkiwix/shared -Kiwix/libkiwix/static \ No newline at end of file +Kiwix/libkiwix/static +Kiwix/libkiwix/iOS +Kiwix/libkiwix/macOS From 7ae208c113091b69537d671222949b65f2c7009c Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 11 Jul 2016 16:55:49 -0400 Subject: [PATCH 06/54] library search path change --- Kiwix.xcodeproj/project.pbxproj | 84 +++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index d4feafef4..8af5c2473 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -17,17 +17,6 @@ 9711871E1CEB449A00B9909D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711871D1CEB449A00B9909D /* libz.tbd */; }; 971187301CEB50FC00B9909D /* ZimReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9711872F1CEB50FC00B9909D /* ZimReader.mm */; }; 971187311CEB50FC00B9909D /* ZimReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9711872F1CEB50FC00B9909D /* ZimReader.mm */; }; - 971187461CEB512D00B9909D /* libicudata.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711873B1CEB512D00B9909D /* libicudata.a */; }; - 971187471CEB512D00B9909D /* libicui18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711873C1CEB512D00B9909D /* libicui18n.a */; }; - 971187481CEB512D00B9909D /* libicuio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711873D1CEB512D00B9909D /* libicuio.a */; }; - 971187491CEB512D00B9909D /* libicule.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711873E1CEB512D00B9909D /* libicule.a */; }; - 9711874A1CEB512D00B9909D /* libiculx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9711873F1CEB512D00B9909D /* libiculx.a */; }; - 9711874B1CEB512D00B9909D /* libicutest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187401CEB512D00B9909D /* libicutest.a */; }; - 9711874C1CEB512D00B9909D /* libicutu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187411CEB512D00B9909D /* libicutu.a */; }; - 9711874D1CEB512D00B9909D /* libicuuc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187421CEB512D00B9909D /* libicuuc.a */; }; - 9711874E1CEB512D00B9909D /* liblzma.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187431CEB512D00B9909D /* liblzma.a */; }; - 9711874F1CEB512D00B9909D /* libxapian.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187441CEB512D00B9909D /* libxapian.a */; }; - 971187501CEB512D00B9909D /* libzim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187451CEB512D00B9909D /* libzim.a */; }; 9711875C1CEB512F00B9909D /* libicudata.55.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187511CEB512F00B9909D /* libicudata.55.dylib */; }; 9711875D1CEB512F00B9909D /* libicui18n.55.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187521CEB512F00B9909D /* libicui18n.55.dylib */; }; 9711875E1CEB512F00B9909D /* libicuio.55.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 971187531CEB512F00B9909D /* libicuio.55.dylib */; }; @@ -114,6 +103,17 @@ 973BCD1A1CEB402900F10B44 /* KiwixTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BCD181CEB402900F10B44 /* KiwixTests.swift */; }; 973BCD1E1CEB403700F10B44 /* SnapshotAutomation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973BCD1C1CEB403700F10B44 /* SnapshotAutomation.swift */; }; 973C8D5C1C25F945007272F9 /* Preference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973C8D5B1C25F945007272F9 /* Preference.swift */; }; + 973DD40F1D343F2F009D45DB /* libicudata.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4041D343F2F009D45DB /* libicudata.a */; }; + 973DD4101D343F2F009D45DB /* libicui18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4051D343F2F009D45DB /* libicui18n.a */; }; + 973DD4111D343F2F009D45DB /* libicuio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4061D343F2F009D45DB /* libicuio.a */; }; + 973DD4121D343F2F009D45DB /* libicule.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4071D343F2F009D45DB /* libicule.a */; }; + 973DD4131D343F2F009D45DB /* libiculx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4081D343F2F009D45DB /* libiculx.a */; }; + 973DD4141D343F2F009D45DB /* libicutest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD4091D343F2F009D45DB /* libicutest.a */; }; + 973DD4151D343F2F009D45DB /* libicutu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40A1D343F2F009D45DB /* libicutu.a */; }; + 973DD4161D343F2F009D45DB /* libicuuc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40B1D343F2F009D45DB /* libicuuc.a */; }; + 973DD4171D343F2F009D45DB /* liblzma.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40C1D343F2F009D45DB /* liblzma.a */; }; + 973DD4181D343F2F009D45DB /* libxapian.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40D1D343F2F009D45DB /* libxapian.a */; }; + 973DD4191D343F2F009D45DB /* libzim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40E1D343F2F009D45DB /* libzim.a */; }; 974570F41C2DABB500680E43 /* ZIMMultiReaderAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974570F31C2DABB500680E43 /* ZIMMultiReaderAPI.swift */; }; 975227821D020560001D1DDE /* Indexer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227811D020560001D1DDE /* Indexer.storyboard */; }; 975227991D020C00001D1DDE /* indexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 975227921D020C00001D1DDE /* indexer.cpp */; settings = {COMPILER_FLAGS = "-w"; }; }; @@ -352,6 +352,17 @@ 973BCD1B1CEB403700F10B44 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Kiwix-iOSUITests/Info.plist"; sourceTree = SOURCE_ROOT; }; 973BCD1C1CEB403700F10B44 /* SnapshotAutomation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotAutomation.swift; path = "Kiwix-iOSUITests/SnapshotAutomation.swift"; sourceTree = SOURCE_ROOT; }; 973C8D5B1C25F945007272F9 /* Preference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Preference.swift; path = Kiwix/Preference.swift; sourceTree = ""; }; + 973DD4041D343F2F009D45DB /* libicudata.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicudata.a; path = Kiwix/libkiwix/iOS/libicudata.a; sourceTree = ""; }; + 973DD4051D343F2F009D45DB /* libicui18n.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicui18n.a; path = Kiwix/libkiwix/iOS/libicui18n.a; sourceTree = ""; }; + 973DD4061D343F2F009D45DB /* libicuio.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicuio.a; path = Kiwix/libkiwix/iOS/libicuio.a; sourceTree = ""; }; + 973DD4071D343F2F009D45DB /* libicule.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicule.a; path = Kiwix/libkiwix/iOS/libicule.a; sourceTree = ""; }; + 973DD4081D343F2F009D45DB /* libiculx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libiculx.a; path = Kiwix/libkiwix/iOS/libiculx.a; sourceTree = ""; }; + 973DD4091D343F2F009D45DB /* libicutest.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicutest.a; path = Kiwix/libkiwix/iOS/libicutest.a; sourceTree = ""; }; + 973DD40A1D343F2F009D45DB /* libicutu.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicutu.a; path = Kiwix/libkiwix/iOS/libicutu.a; sourceTree = ""; }; + 973DD40B1D343F2F009D45DB /* libicuuc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicuuc.a; path = Kiwix/libkiwix/iOS/libicuuc.a; sourceTree = ""; }; + 973DD40C1D343F2F009D45DB /* liblzma.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblzma.a; path = Kiwix/libkiwix/iOS/liblzma.a; sourceTree = ""; }; + 973DD40D1D343F2F009D45DB /* libxapian.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libxapian.a; path = Kiwix/libkiwix/iOS/libxapian.a; sourceTree = ""; }; + 973DD40E1D343F2F009D45DB /* libzim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libzim.a; path = Kiwix/libkiwix/iOS/libzim.a; sourceTree = ""; }; 974570F31C2DABB500680E43 /* ZIMMultiReaderAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZIMMultiReaderAPI.swift; sourceTree = ""; }; 97497B5A1D07487000ECD691 /* indexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = indexer.h; path = "Kiwix/libkiwix/C&C++/indexer.h"; sourceTree = ""; }; 97497B5B1D07487000ECD691 /* resourceTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = resourceTools.h; path = "Kiwix/libkiwix/C&C++/resourceTools.h"; sourceTree = ""; }; @@ -469,18 +480,18 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 971187491CEB512D00B9909D /* libicule.a in Frameworks */, + 973DD40F1D343F2F009D45DB /* libicudata.a in Frameworks */, + 973DD4101D343F2F009D45DB /* libicui18n.a in Frameworks */, + 973DD4111D343F2F009D45DB /* libicuio.a in Frameworks */, + 973DD4121D343F2F009D45DB /* libicule.a in Frameworks */, + 973DD4131D343F2F009D45DB /* libiculx.a in Frameworks */, + 973DD4141D343F2F009D45DB /* libicutest.a in Frameworks */, + 973DD4151D343F2F009D45DB /* libicutu.a in Frameworks */, + 973DD4161D343F2F009D45DB /* libicuuc.a in Frameworks */, + 973DD4171D343F2F009D45DB /* liblzma.a in Frameworks */, + 973DD4181D343F2F009D45DB /* libxapian.a in Frameworks */, + 973DD4191D343F2F009D45DB /* libzim.a in Frameworks */, 9711871E1CEB449A00B9909D /* libz.tbd in Frameworks */, - 971187501CEB512D00B9909D /* libzim.a in Frameworks */, - 971187461CEB512D00B9909D /* libicudata.a in Frameworks */, - 971187471CEB512D00B9909D /* libicui18n.a in Frameworks */, - 971187481CEB512D00B9909D /* libicuio.a in Frameworks */, - 9711874A1CEB512D00B9909D /* libiculx.a in Frameworks */, - 9711874F1CEB512D00B9909D /* libxapian.a in Frameworks */, - 9711874D1CEB512D00B9909D /* libicuuc.a in Frameworks */, - 9711874C1CEB512D00B9909D /* libicutu.a in Frameworks */, - 9711874B1CEB512D00B9909D /* libicutest.a in Frameworks */, - 9711874E1CEB512D00B9909D /* liblzma.a in Frameworks */, 7356F9FACBB84380CFC8F68F /* Pods_Kiwix_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -513,6 +524,17 @@ 931D277C156AE671D3F9EADA /* Frameworks */ = { isa = PBXGroup; children = ( + 973DD4041D343F2F009D45DB /* libicudata.a */, + 973DD4051D343F2F009D45DB /* libicui18n.a */, + 973DD4061D343F2F009D45DB /* libicuio.a */, + 973DD4071D343F2F009D45DB /* libicule.a */, + 973DD4081D343F2F009D45DB /* libiculx.a */, + 973DD4091D343F2F009D45DB /* libicutest.a */, + 973DD40A1D343F2F009D45DB /* libicutu.a */, + 973DD40B1D343F2F009D45DB /* libicuuc.a */, + 973DD40C1D343F2F009D45DB /* liblzma.a */, + 973DD40D1D343F2F009D45DB /* libxapian.a */, + 973DD40E1D343F2F009D45DB /* libzim.a */, 97E609F01D103DED00EBCB9D /* NotificationCenter.framework */, 198ECFA618CDD6B29CD462A0 /* Pods_Kiwix_OSX.framework */, EC884ACBBA260AF741C4C4FE /* Pods_Kiwix_iOS.framework */, @@ -756,6 +778,17 @@ name = Shared; sourceTree = ""; }; + 973DD4031D343F09009D45DB /* Basic */ = { + isa = PBXGroup; + children = ( + 9779A1C21D34225E0071EFAB /* AlertOperation.swift */, + 9779A1C41D34225E0071EFAB /* NetworkObserver.swift */, + 970C61981D3429E400087758 /* ReachabilityCondition.swift */, + 9779A1C71D34225E0071EFAB /* UIOperations.swift */, + ); + name = Basic; + sourceTree = ""; + }; 9749A1B21C430653000F2D1E /* Bookmark */ = { isa = PBXGroup; children = ( @@ -1017,13 +1050,10 @@ 97E5712A1CA0525300FF4F1D /* Operation */ = { isa = PBXGroup; children = ( - 9779A1C21D34225E0071EFAB /* AlertOperation.swift */, + 973DD4031D343F09009D45DB /* Basic */, 9779A1C31D34225E0071EFAB /* GlobalOperationQueue.swift */, - 9779A1C41D34225E0071EFAB /* NetworkObserver.swift */, - 970C61981D3429E400087758 /* ReachabilityCondition.swift */, 9779A1C51D34225E0071EFAB /* RefreshLibraryOperation.swift */, 9779A1C61D34225E0071EFAB /* SearchOperation.swift */, - 9779A1C71D34225E0071EFAB /* UIOperations.swift */, 970C61961D34243600087758 /* URLSessionDownloadTaskOperation.swift */, ); name = Operation; @@ -1912,6 +1942,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Kiwix/libkiwix/static", + "$(PROJECT_DIR)/Kiwix/libkiwix/iOS", ); PRODUCT_BUNDLE_IDENTIFIER = self.Kiwix; PRODUCT_NAME = Kiwix; @@ -1946,6 +1977,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Kiwix/libkiwix/static", + "$(PROJECT_DIR)/Kiwix/libkiwix/iOS", ); PRODUCT_BUNDLE_IDENTIFIER = self.Kiwix; PRODUCT_NAME = Kiwix; From 5c97a4337c06b6f16c65f0e4b702c261af371213 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 11 Jul 2016 17:17:45 -0400 Subject: [PATCH 07/54] Refactor --- Kiwix-iOS/AppDelegate.swift | 2 +- Kiwix-iOS/Controller/LibraryLocalTBVC.swift | 2 +- Kiwix-iOS/Controller/MainVC.swift | 2 +- Kiwix-iOS/Controller/MainVCDelegates.swift | 2 +- Kiwix-iOS/Controller/MainVCLoading.swift | 4 +- Kiwix-iOS/Controller/SearchResultTBVC.swift | 4 +- Kiwix-iOS/Model/KiwixURLProtocol.swift | 6 +- Kiwix.xcodeproj/project.pbxproj | 42 +++--- Kiwix/Operations/SearchOperation.swift | 2 +- Kiwix/ZIMMultiReaderAPI.swift | 60 -------- .../DirectoryMonitor.swift | 0 .../ExtensionAndTypealias.swift | 32 +++++ Kiwix/ZimMultiReader/SearchResult.swift | 62 ++++++++ .../{ => ZimMultiReader}/ZimMultiReader.swift | 132 +++++++----------- 14 files changed, 179 insertions(+), 173 deletions(-) delete mode 100644 Kiwix/ZIMMultiReaderAPI.swift rename Kiwix/{ => ZimMultiReader}/DirectoryMonitor.swift (100%) create mode 100644 Kiwix/ZimMultiReader/ExtensionAndTypealias.swift create mode 100644 Kiwix/ZimMultiReader/SearchResult.swift rename Kiwix/{ => ZimMultiReader}/ZimMultiReader.swift (63%) diff --git a/Kiwix-iOS/AppDelegate.swift b/Kiwix-iOS/AppDelegate.swift index 8c44fb14b..480e43193 100644 --- a/Kiwix-iOS/AppDelegate.swift +++ b/Kiwix-iOS/AppDelegate.swift @@ -57,7 +57,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate { func applicationDidBecomeActive(application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(AppDelegate.recordActiveSession), userInfo: nil, repeats: false) - ZIMMultiReader.sharedInstance.rescan() + ZimMultiReader.sharedInstance.scan() } func applicationWillTerminate(application: UIApplication) { diff --git a/Kiwix-iOS/Controller/LibraryLocalTBVC.swift b/Kiwix-iOS/Controller/LibraryLocalTBVC.swift index 3bcb03111..b689fe275 100644 --- a/Kiwix-iOS/Controller/LibraryLocalTBVC.swift +++ b/Kiwix-iOS/Controller/LibraryLocalTBVC.swift @@ -186,7 +186,7 @@ class LibraryLocalTBVC: UITableViewController, NSFetchedResultsControllerDelegat let delete = UITableViewRowAction(style: .Destructive, title: LocalizedStrings.delete) { (action, indexPath) -> Void in guard let book = self.fetchedResultController.objectAtIndexPath(indexPath) as? Book else {return} self.managedObjectContext.performBlock({ () -> Void in - if let id = book.id, let zimURL = ZIMMultiReader.sharedInstance.readers[id]?.fileURL { + if let id = book.id, let zimURL = ZimMultiReader.sharedInstance.readers[id]?.fileURL { FileManager.removeItem(atURL: zimURL) let indexFolderURL = zimURL.URLByAppendingPathExtension("idx") diff --git a/Kiwix-iOS/Controller/MainVC.swift b/Kiwix-iOS/Controller/MainVC.swift index 87486eb7d..a71c33e8d 100644 --- a/Kiwix-iOS/Controller/MainVC.swift +++ b/Kiwix-iOS/Controller/MainVC.swift @@ -45,7 +45,7 @@ class MainVC: UIViewController { navigationItem.titleView = searchBar searchBar.delegate = self - ZIMMultiReader.sharedInstance.delegate = self + ZimMultiReader.sharedInstance.delegate = self NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: "webViewNotInjectJavascriptToAdjustPageLayout", options: .New, context: context) NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: "webViewZoomScale", options: .New, context: context) diff --git a/Kiwix-iOS/Controller/MainVCDelegates.swift b/Kiwix-iOS/Controller/MainVCDelegates.swift index f92a3c997..7dc5a329c 100644 --- a/Kiwix-iOS/Controller/MainVCDelegates.swift +++ b/Kiwix-iOS/Controller/MainVCDelegates.swift @@ -45,7 +45,7 @@ extension MainVC: LPTBarButtonItemDelegate, TableOfContentsDelegate, ZimMultiRea // MARK: - ZimMultiReaderDelegate - func firstBookAdded(id: ZIMID) { + func firstBookAdded(id: ZimID) { loadMainPage(id) } diff --git a/Kiwix-iOS/Controller/MainVCLoading.swift b/Kiwix-iOS/Controller/MainVCLoading.swift index 1e1d13896..1254c6fd7 100644 --- a/Kiwix-iOS/Controller/MainVCLoading.swift +++ b/Kiwix-iOS/Controller/MainVCLoading.swift @@ -19,8 +19,8 @@ extension MainVC { webView.loadRequest(request) } - func loadMainPage(id: ZIMID) { - guard let reader = ZIMMultiReader.sharedInstance.readers[id] else {return} + func loadMainPage(id: ZimID) { + guard let reader = ZimMultiReader.sharedInstance.readers[id] else {return} let mainPageURLString = reader.mainPageURL() let mainPageURL = NSURL.kiwixURLWithZimFileid(id, contentURLString: mainPageURLString) load(mainPageURL) diff --git a/Kiwix-iOS/Controller/SearchResultTBVC.swift b/Kiwix-iOS/Controller/SearchResultTBVC.swift index 2002f14c8..2a94fb3f5 100644 --- a/Kiwix-iOS/Controller/SearchResultTBVC.swift +++ b/Kiwix-iOS/Controller/SearchResultTBVC.swift @@ -120,7 +120,7 @@ class SearchResultTBVC: UIViewController, UITableViewDataSource, UITableViewDele tableView.reloadData() return } - ZIMMultiReader.sharedInstance.searchQueue.cancelAllOperations() + ZimMultiReader.sharedInstance.searchQueue.cancelAllOperations() let operation = SearchOperation(searchTerm: searchText) { (results) in self.searchResults = results self.tableView.reloadData() @@ -128,7 +128,7 @@ class SearchResultTBVC: UIViewController, UITableViewDataSource, UITableViewDele self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: true) } } - ZIMMultiReader.sharedInstance.searchQueue.addOperation(operation) + ZimMultiReader.sharedInstance.searchQueue.addOperation(operation) } } diff --git a/Kiwix-iOS/Model/KiwixURLProtocol.swift b/Kiwix-iOS/Model/KiwixURLProtocol.swift index 4deb8e705..fb5f7fa87 100644 --- a/Kiwix-iOS/Model/KiwixURLProtocol.swift +++ b/Kiwix-iOS/Model/KiwixURLProtocol.swift @@ -21,7 +21,7 @@ class KiwixURLProtocol: NSURLProtocol { override func startLoading() { if let id = self.request.URL?.host, let contentURLString = self.request.URL?.path?.stringByRemovingPercentEncoding { - if let dataDic = ZIMMultiReader.sharedInstance.data(id, contentURLString: contentURLString), + if let dataDic = ZimMultiReader.sharedInstance.data(id, contentURLString: contentURLString), data = dataDic["data"] as? NSData, mimeType = dataDic["mime"] as? String, dataLength = dataDic["length"]?.integerValue { @@ -53,14 +53,14 @@ extension NSURL { } class func kiwixURLWithZimFileid(id: String, articleTitle: String) -> NSURL? { - guard let contentURLString = ZIMMultiReader.sharedInstance.pageURLString(articleTitle, bookid: id) else { + guard let contentURLString = ZimMultiReader.sharedInstance.pageURLString(articleTitle, bookid: id) else { print("ZimMultiReader cannot get pageURLString from \(articleTitle) in book \(id)") return nil } return NSURL.kiwixURLWithZimFileid(id, contentURLString: contentURLString) } - convenience init?(id: ZIMID, contentURLString: String) { + convenience init?(id: ZimID, contentURLString: String) { guard let escapedContentURLString = contentURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLPathAllowedCharacterSet()) else {return nil} let baseURLString = "kiwix://" + id self.init(string: escapedContentURLString, relativeToURL: NSURL(string: baseURLString)) diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 8af5c2473..e1246ea4f 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -94,7 +94,6 @@ 971A107F1D022F74007FC62C /* ImportBookLearnMore.html in Resources */ = {isa = PBXBuildFile; fileRef = 971A107B1D022F74007FC62C /* ImportBookLearnMore.html */; }; 971A10801D022F74007FC62C /* Pic_I.png in Resources */ = {isa = PBXBuildFile; fileRef = 971A107C1D022F74007FC62C /* Pic_I.png */; }; 971A10811D022F74007FC62C /* Pic_P.png in Resources */ = {isa = PBXBuildFile; fileRef = 971A107D1D022F74007FC62C /* Pic_P.png */; }; - 97254FDF1C2644560056950B /* ZIMMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97254FDE1C2644560056950B /* ZIMMultiReader.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 */; }; @@ -114,7 +113,10 @@ 973DD4171D343F2F009D45DB /* liblzma.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40C1D343F2F009D45DB /* liblzma.a */; }; 973DD4181D343F2F009D45DB /* libxapian.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40D1D343F2F009D45DB /* libxapian.a */; }; 973DD4191D343F2F009D45DB /* libzim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40E1D343F2F009D45DB /* libzim.a */; }; - 974570F41C2DABB500680E43 /* ZIMMultiReaderAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974570F31C2DABB500680E43 /* ZIMMultiReaderAPI.swift */; }; + 973DD41D1D34428F009D45DB /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD41A1D34428F009D45DB /* DirectoryMonitor.swift */; }; + 973DD41E1D34428F009D45DB /* ZimMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD41B1D34428F009D45DB /* ZimMultiReader.swift */; }; + 973DD4211D34434C009D45DB /* SearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD4201D34434C009D45DB /* SearchResult.swift */; }; + 973DD4231D3443A3009D45DB /* ExtensionAndTypealias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD4221D3443A3009D45DB /* ExtensionAndTypealias.swift */; }; 975227821D020560001D1DDE /* Indexer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227811D020560001D1DDE /* Indexer.storyboard */; }; 975227991D020C00001D1DDE /* indexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 975227921D020C00001D1DDE /* indexer.cpp */; settings = {COMPILER_FLAGS = "-w"; }; }; 9752279B1D020C00001D1DDE /* otherTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 975227931D020C00001D1DDE /* otherTools.cpp */; }; @@ -145,9 +147,6 @@ 975B90FF1CEB909900D13906 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779987A1C1E1C9600B1DD5E /* Extensions.swift */; }; 975B912F1CEB9B0F00D13906 /* Preference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973C8D5B1C25F945007272F9 /* Preference.swift */; }; 9763A6291CEB9E55008A2718 /* OSXExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9763A6281CEB9E55008A2718 /* OSXExtensions.swift */; }; - 9763A62A1CEBA4F9008A2718 /* ZIMMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97254FDE1C2644560056950B /* ZIMMultiReader.swift */; }; - 9763A62B1CEBA4F9008A2718 /* ZIMMultiReaderAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974570F31C2DABB500680E43 /* ZIMMultiReaderAPI.swift */; }; - 9763A62C1CEBA4F9008A2718 /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A714091C274FCB00951244 /* DirectoryMonitor.swift */; }; 9763A62D1CEBA524008A2718 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97E891681CA976E90001CA32 /* FileManager.swift */; }; 976AB2671CBD8B3D00B06EB0 /* 1.5.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 976AB2661CBD8B3D00B06EB0 /* 1.5.xcmappingmodel */; }; 977305361D0DFD110081B8F0 /* KiwixURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 971A10711D022E74007FC62C /* KiwixURLProtocol.swift */; }; @@ -173,7 +172,6 @@ 979CB6C81D05CF37005E1BA1 /* SearchResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6C71D05CF37005E1BA1 /* SearchResultController.swift */; }; 979CB6CA1D05D26E005E1BA1 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 979CB6C91D05D26E005E1BA1 /* WebViewController.swift */; }; 97A7017F1D2C59CA00AAE2D8 /* GetStartedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A7017E1D2C59CA00AAE2D8 /* GetStartedController.swift */; }; - 97A7140A1C274FCB00951244 /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A714091C274FCB00951244 /* DirectoryMonitor.swift */; }; 97BA32A51CEBC36300339A47 /* RootWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97BA32A31CEBC29500339A47 /* RootWindowController.swift */; }; 97D452BC1D16FF010033666F /* RecentSearchCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D452BB1D16FF010033666F /* RecentSearchCVC.swift */; }; 97D452BE1D1723FF0033666F /* CollectionViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D452BD1D1723FF0033666F /* CollectionViewCells.swift */; }; @@ -335,7 +333,6 @@ 971A107B1D022F74007FC62C /* ImportBookLearnMore.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = ImportBookLearnMore.html; path = Kiwix/HelpDocuments/ImportBookLearnMore.html; sourceTree = SOURCE_ROOT; }; 971A107C1D022F74007FC62C /* Pic_I.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Pic_I.png; path = Kiwix/HelpDocuments/Pic_I.png; sourceTree = SOURCE_ROOT; }; 971A107D1D022F74007FC62C /* Pic_P.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Pic_P.png; path = Kiwix/HelpDocuments/Pic_P.png; sourceTree = SOURCE_ROOT; }; - 97254FDE1C2644560056950B /* ZIMMultiReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZIMMultiReader.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; }; @@ -363,7 +360,10 @@ 973DD40C1D343F2F009D45DB /* liblzma.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblzma.a; path = Kiwix/libkiwix/iOS/liblzma.a; sourceTree = ""; }; 973DD40D1D343F2F009D45DB /* libxapian.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libxapian.a; path = Kiwix/libkiwix/iOS/libxapian.a; sourceTree = ""; }; 973DD40E1D343F2F009D45DB /* libzim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libzim.a; path = Kiwix/libkiwix/iOS/libzim.a; sourceTree = ""; }; - 974570F31C2DABB500680E43 /* ZIMMultiReaderAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZIMMultiReaderAPI.swift; sourceTree = ""; }; + 973DD41A1D34428F009D45DB /* DirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DirectoryMonitor.swift; path = ZimMultiReader/DirectoryMonitor.swift; sourceTree = ""; }; + 973DD41B1D34428F009D45DB /* ZimMultiReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ZimMultiReader.swift; path = ZimMultiReader/ZimMultiReader.swift; sourceTree = ""; }; + 973DD4201D34434C009D45DB /* SearchResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchResult.swift; path = ZimMultiReader/SearchResult.swift; sourceTree = ""; }; + 973DD4221D3443A3009D45DB /* ExtensionAndTypealias.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ExtensionAndTypealias.swift; path = ZimMultiReader/ExtensionAndTypealias.swift; sourceTree = ""; }; 97497B5A1D07487000ECD691 /* indexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = indexer.h; path = "Kiwix/libkiwix/C&C++/indexer.h"; sourceTree = ""; }; 97497B5B1D07487000ECD691 /* resourceTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = resourceTools.h; path = "Kiwix/libkiwix/C&C++/resourceTools.h"; sourceTree = ""; }; 97497B5C1D074FB800ECD691 /* htmlparse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = htmlparse.h; path = "Kiwix/libkiwix/C&C++/xapian/htmlparse.h"; sourceTree = ""; }; @@ -419,7 +419,6 @@ 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; }; 97A7017E1D2C59CA00AAE2D8 /* GetStartedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GetStartedController.swift; path = "Kiwix-iOS/Controller/Welcome/GetStartedController.swift"; sourceTree = SOURCE_ROOT; }; - 97A714091C274FCB00951244 /* DirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = ""; }; 97BA32A31CEBC29500339A47 /* RootWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RootWindowController.swift; path = "Kiwix-OSX/Controllers/RootWindowController.swift"; sourceTree = SOURCE_ROOT; }; 97D452BB1D16FF010033666F /* RecentSearchCVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RecentSearchCVC.swift; path = "Kiwix-iOS/Controller/Search/RecentSearchCVC.swift"; sourceTree = SOURCE_ROOT; }; 97D452BD1D1723FF0033666F /* CollectionViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCells.swift; sourceTree = ""; }; @@ -706,14 +705,15 @@ path = View; sourceTree = ""; }; - 97254FDD1C26442F0056950B /* ZIMMultiReader */ = { + 97254FDD1C26442F0056950B /* ZimMultiReader */ = { isa = PBXGroup; children = ( - 97254FDE1C2644560056950B /* ZIMMultiReader.swift */, - 974570F31C2DABB500680E43 /* ZIMMultiReaderAPI.swift */, - 97A714091C274FCB00951244 /* DirectoryMonitor.swift */, + 973DD41B1D34428F009D45DB /* ZimMultiReader.swift */, + 973DD4201D34434C009D45DB /* SearchResult.swift */, + 973DD4221D3443A3009D45DB /* ExtensionAndTypealias.swift */, + 973DD41A1D34428F009D45DB /* DirectoryMonitor.swift */, ); - name = ZIMMultiReader; + name = ZimMultiReader; path = Kiwix; sourceTree = ""; }; @@ -773,7 +773,7 @@ 971A10711D022E74007FC62C /* KiwixURLProtocol.swift */, 973C8D5B1C25F945007272F9 /* Preference.swift */, 979C51511CECA9AF001707F2 /* StringTools.swift */, - 97254FDD1C26442F0056950B /* ZIMMultiReader */, + 97254FDD1C26442F0056950B /* ZimMultiReader */, ); name = Shared; sourceTree = ""; @@ -1499,7 +1499,6 @@ 9752279B1D020C00001D1DDE /* otherTools.cpp in Sources */, 975B912F1CEB9B0F00D13906 /* Preference.swift in Sources */, 971187941CEB541A00B9909D /* Article.swift in Sources */, - 9763A62B1CEBA4F9008A2718 /* ZIMMultiReaderAPI.swift in Sources */, 9752279D1D020C00001D1DDE /* pathTools.cpp in Sources */, 975227A91D020C2E001D1DDE /* myhtmlparse.cc in Sources */, 975227B01D021539001D1DDE /* IndexerController.swift in Sources */, @@ -1519,9 +1518,7 @@ 971187971CEB542500B9909D /* Article+CoreDataProperties.swift in Sources */, 975227A31D020C00001D1DDE /* stringTools.cpp in Sources */, 977305361D0DFD110081B8F0 /* KiwixURLProtocol.swift in Sources */, - 9763A62A1CEBA4F9008A2718 /* ZIMMultiReader.swift in Sources */, 979C518D1CECAE4C001707F2 /* PreferenceWindowController.swift in Sources */, - 9763A62C1CEBA4F9008A2718 /* DirectoryMonitor.swift in Sources */, 9711879B1CEB546C00B9909D /* CoreDataExtension.swift in Sources */, 979C51531CECA9AF001707F2 /* StringTools.swift in Sources */, 971187931CEB541A00B9909D /* DownloadTask.swift in Sources */, @@ -1561,6 +1558,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 973DD41D1D34428F009D45DB /* DirectoryMonitor.swift in Sources */, 976AB2671CBD8B3D00B06EB0 /* 1.5.xcmappingmodel in Sources */, 971A10311D022AD5007FC62C /* RefreshHUD.swift in Sources */, 971A102C1D022AD5007FC62C /* BarButtonItems.swift in Sources */, @@ -1568,6 +1566,7 @@ 971A103F1D022C42007FC62C /* LibraryAutoRefreshTBVC.swift in Sources */, 971A10721D022E74007FC62C /* KiwixURLProtocol.swift in Sources */, 971A10581D022DAD007FC62C /* LibraryDownloadTBVC.swift in Sources */, + 973DD4231D3443A3009D45DB /* ExtensionAndTypealias.swift in Sources */, 97A7017F1D2C59CA00AAE2D8 /* GetStartedController.swift in Sources */, 979C51521CECA9AF001707F2 /* StringTools.swift in Sources */, 9779A1CD1D34225E0071EFAB /* UIOperations.swift in Sources */, @@ -1577,7 +1576,6 @@ 971A10651D022E0A007FC62C /* MainVC.swift in Sources */, 971A106F1D022E62007FC62C /* DownloadProgress.swift in Sources */, 971A102E1D022AD5007FC62C /* TableViewCells.swift in Sources */, - 97254FDF1C2644560056950B /* ZIMMultiReader.swift in Sources */, 971A105A1D022DAD007FC62C /* LibraryLocalTBVC.swift in Sources */, 97E60A021D10423A00EBCB9D /* ShadowView.swift in Sources */, 9779A1CC1D34225E0071EFAB /* SearchOperation.swift in Sources */, @@ -1597,7 +1595,6 @@ 97E891691CA976E90001CA32 /* FileManager.swift in Sources */, 97E60A061D10504000EBCB9D /* LibraryBackupTBVC.swift in Sources */, 9779A1CA1D34225E0071EFAB /* NetworkObserver.swift in Sources */, - 974570F41C2DABB500680E43 /* ZIMMultiReaderAPI.swift in Sources */, 971A105C1D022DAD007FC62C /* LibraryTabBarController.swift in Sources */, 971A10461D022CB2007FC62C /* SearchController.swift in Sources */, 9779987B1C1E1C9600B1DD5E /* Extensions.swift in Sources */, @@ -1622,7 +1619,6 @@ 975227A21D020C00001D1DDE /* stringTools.cpp in Sources */, 9711879A1CEB546C00B9909D /* CoreDataExtension.swift in Sources */, 970C61971D34243600087758 /* URLSessionDownloadTaskOperation.swift in Sources */, - 97A7140A1C274FCB00951244 /* DirectoryMonitor.swift in Sources */, 971A10341D022AEC007FC62C /* BookmarkTBVC.swift in Sources */, 975227A01D020C00001D1DDE /* resourceTools.cpp in Sources */, 971A10601D022DF2007FC62C /* LanguageTBVC.swift in Sources */, @@ -1630,9 +1626,11 @@ 971A106C1D022E50007FC62C /* Utilities.swift in Sources */, 971A10591D022DAD007FC62C /* LibraryLocalBookDetailTBVC.swift in Sources */, 971A10431D022C54007FC62C /* SettingTBVC.swift in Sources */, + 973DD4211D34434C009D45DB /* SearchResult.swift in Sources */, 9779A1C91D34225E0071EFAB /* GlobalOperationQueue.swift in Sources */, 970C61991D3429E400087758 /* ReachabilityCondition.swift in Sources */, 978C589C1C1CD86E0077AE47 /* Article.swift in Sources */, + 973DD41E1D34428F009D45DB /* ZimMultiReader.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1941,7 +1939,6 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Kiwix/libkiwix/static", "$(PROJECT_DIR)/Kiwix/libkiwix/iOS", ); PRODUCT_BUNDLE_IDENTIFIER = self.Kiwix; @@ -1976,7 +1973,6 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Kiwix/libkiwix/static", "$(PROJECT_DIR)/Kiwix/libkiwix/iOS", ); PRODUCT_BUNDLE_IDENTIFIER = self.Kiwix; diff --git a/Kiwix/Operations/SearchOperation.swift b/Kiwix/Operations/SearchOperation.swift index 185677e8d..15d2f2271 100644 --- a/Kiwix/Operations/SearchOperation.swift +++ b/Kiwix/Operations/SearchOperation.swift @@ -21,7 +21,7 @@ class SearchOperation: GroupOperation { self.results = results } - for (id, zimReader) in ZIMMultiReader.sharedInstance.readers { + for (id, zimReader) in ZimMultiReader.sharedInstance.readers { let managedObjectContext = UIApplication.appDelegate.managedObjectContext guard let book = Book.fetch(id, context: managedObjectContext) else {continue} guard book.includeInSearch else {continue} diff --git a/Kiwix/ZIMMultiReaderAPI.swift b/Kiwix/ZIMMultiReaderAPI.swift deleted file mode 100644 index 9a5fc9ab4..000000000 --- a/Kiwix/ZIMMultiReaderAPI.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// ZIMMultiReaderAPI.swift -// Kiwix -// -// Created by Chris on 12/25/15. -// Copyright © 2015 Chris. All rights reserved. -// - -extension ZIMMultiReader { - - // MARK: - Search - - func search(searchTerm: String, zimFileID: String) -> [(id: String, articleTitle: String)] { - var resultTuples = [(id: String, articleTitle: String)]() - let firstCharRange = searchTerm.startIndex...searchTerm.startIndex - let firstLetterCapitalisedSearchTerm = searchTerm.stringByReplacingCharactersInRange(firstCharRange, withString: searchTerm.substringWithRange(firstCharRange).capitalizedString) - let searchTermVariations = Set([searchTerm, searchTerm.uppercaseString, searchTerm.lowercaseString, searchTerm.capitalizedString, firstLetterCapitalisedSearchTerm]) - - let reader = readers[zimFileID] - var results = Set() - for searchTermVariation in searchTermVariations { - guard let result = reader?.searchSuggestionsSmart(searchTermVariation) as? [String] else {continue} - results.unionInPlace(result) - } - - for result in results { - resultTuples.append((id: zimFileID, articleTitle: result)) - } - - return resultTuples - } - - // MARK: - Loading System - - func data(id: String, contentURLString: String) -> [String: AnyObject]? { - guard let reader = readers[id] else {return nil} - return reader.dataWithContentURLString(contentURLString) as? [String: AnyObject] - } - - func pageURLString(articleTitle: String, bookid id: String) -> String? { - guard let reader = readers[id] else {return nil} - return reader.pageURLFromTitle(articleTitle) - } - - func mainPageURLString(bookid id: String) -> String? { - guard let reader = readers[id] else {return nil} - return reader.mainPageURL() - } - - func randomPageURLString() -> (id: String, contentURLString: String)? { - var randomPageURLs = [(String, String)]() - for (id, reader) in readers{ - randomPageURLs.append((id, reader.getRandomPageUrl())) - } - - guard randomPageURLs.count > 0 else {return nil} - let index = arc4random_uniform(UInt32(randomPageURLs.count)) - return randomPageURLs[Int(index)] - } -} \ No newline at end of file diff --git a/Kiwix/DirectoryMonitor.swift b/Kiwix/ZimMultiReader/DirectoryMonitor.swift similarity index 100% rename from Kiwix/DirectoryMonitor.swift rename to Kiwix/ZimMultiReader/DirectoryMonitor.swift diff --git a/Kiwix/ZimMultiReader/ExtensionAndTypealias.swift b/Kiwix/ZimMultiReader/ExtensionAndTypealias.swift new file mode 100644 index 000000000..8f7ff8075 --- /dev/null +++ b/Kiwix/ZimMultiReader/ExtensionAndTypealias.swift @@ -0,0 +1,32 @@ +// +// ExtensionAndTypealias.swift +// Kiwix +// +// Created by Chris Li on 7/11/16. +// Copyright © 2016 Chris. All rights reserved. +// + +import UIKit + +extension ZimReader { + var metaData: [String: AnyObject] { + var metadata = [String: AnyObject]() + + if let id = getID() {metadata["id"] = id} + if let title = getTitle() {metadata["title"] = title} + if let description = getDesc() {metadata["description"] = description} + if let creator = getCreator() {metadata["creator"] = creator} + if let publisher = getPublisher() {metadata["publisher"] = publisher} + if let favicon = getFavicon() {metadata["favicon"] = favicon} + if let date = getDate() {metadata["date"] = date} + if let articleCount = getArticleCount() {metadata["articleCount"] = articleCount} + if let mediaCount = getMediaCount() {metadata["mediaCount"] = mediaCount} + if let fileSize = getFileSize() {metadata["size"] = fileSize} + if let langCode = getLanguage() {metadata["language"] = langCode} + + return metadata + } +} + +typealias ZimID = String +typealias ArticlePath = String diff --git a/Kiwix/ZimMultiReader/SearchResult.swift b/Kiwix/ZimMultiReader/SearchResult.swift new file mode 100644 index 000000000..e9c9af852 --- /dev/null +++ b/Kiwix/ZimMultiReader/SearchResult.swift @@ -0,0 +1,62 @@ +// +// SearchResult.swift +// Kiwix +// +// Created by Chris Li on 7/11/16. +// Copyright © 2016 Chris. All rights reserved. +// + +import UIKit + +class SearchResult: CustomStringConvertible { + let title: String + let path: String + let bookID: ZimID + let snippet: String? + + let probability: Double? // range: 0.0 - 1.0 + let distance: Int // Levenshtein distance, non negative integer + private(set) lazy var score: Double = { + if let probability = self.probability { + return WeightFactor.calculate(probability) * Double(self.distance) + } else { + return Double(self.distance) + } + }() + + init?(rawResult: [String: AnyObject]) { + let title = (rawResult["title"] as? String) ?? "" + let path = (rawResult["path"] as? String) ?? "" + let bookID = (rawResult["bookID"] as? ZimID) ?? "" + let snippet = rawResult["snippet"] as? String + + let distance = (rawResult["distance"]as? NSNumber)?.integerValue ?? title.characters.count + let probability: Double? = { + if let probability = (rawResult["probability"] as? NSNumber)?.doubleValue { + return probability / 100.0 + } else { + return nil + } + }() + + self.title = title + self.path = path + self.bookID = bookID + self.snippet = snippet + self.probability = probability + self.distance = distance + + if title == "" || path == "" || bookID == "" {return nil} + } + + var description: String { + var parts = [bookID, title] + if let probability = probability {parts.append("\(probability)%")} + parts.append("dist: \(distance)") + return parts.joinWithSeparator(", ") + } + + var rankInfo: String { + return "(\(distance), \(probability ?? -1), \(String(format: "%.4f", score)))" + } +} diff --git a/Kiwix/ZimMultiReader.swift b/Kiwix/ZimMultiReader/ZimMultiReader.swift similarity index 63% rename from Kiwix/ZimMultiReader.swift rename to Kiwix/ZimMultiReader/ZimMultiReader.swift index de62e6980..6b6cfb739 100644 --- a/Kiwix/ZimMultiReader.swift +++ b/Kiwix/ZimMultiReader/ZimMultiReader.swift @@ -1,5 +1,5 @@ // -// ZIMMultiReader.swift +// ZimMultiReader.swift // Kiwix // // Created by Chris on 12/19/15. @@ -9,10 +9,10 @@ import CoreData import PSOperations -class ZIMMultiReader: NSObject, DirectoryMonitorDelegate { +class ZimMultiReader: NSObject, DirectoryMonitorDelegate { - static let sharedInstance = ZIMMultiReader() - private(set) var readers = [ZIMID: ZimReader]() { + static let sharedInstance = ZimMultiReader() + private(set) var readers = [ZimID: ZimReader]() { didSet { if readers.count == 1 { guard let id = readers.keys.first else {return} @@ -42,15 +42,15 @@ class ZIMMultiReader: NSObject, DirectoryMonitorDelegate { // MARK: - DirectoryMonitorDelegate func directoryMonitorDidObserveChange() { - rescan() + scan() } - // MARK: - Refresh + // MARK: - Scan - func rescan() { + func scan() { /* If list of idx folders changes, reinitialize all zim readers, - because currently ZIMMultiReader cannot find out which ZimReader's index folder is added or deleted + because currently ZimMultiReader cannot find out which ZimReader's index folder is added or deleted Note: when a idx folder is added, the content of that idx folder will not finish copying, which makes it meanless to detect idx folder addition. Because, with a incompletely copied idx folder, the xapian initializer is guranteed to fail. So here only check for idx folder deletion. @@ -147,83 +147,59 @@ class ZIMMultiReader: NSObject, DirectoryMonitorDelegate { } return folderURLs } -} - -protocol ZimMultiReaderDelegate: class { - func firstBookAdded(id: ZIMID) -} - -extension ZimReader { - var metaData: [String: AnyObject] { - var metadata = [String: AnyObject]() - - if let id = getID() {metadata["id"] = id} - if let title = getTitle() {metadata["title"] = title} - if let description = getDesc() {metadata["description"] = description} - if let creator = getCreator() {metadata["creator"] = creator} - if let publisher = getPublisher() {metadata["publisher"] = publisher} - if let favicon = getFavicon() {metadata["favicon"] = favicon} - if let date = getDate() {metadata["date"] = date} - if let articleCount = getArticleCount() {metadata["articleCount"] = articleCount} - if let mediaCount = getMediaCount() {metadata["mediaCount"] = mediaCount} - if let fileSize = getFileSize() {metadata["size"] = fileSize} - if let langCode = getLanguage() {metadata["language"] = langCode} - - return metadata - } -} - -typealias ZIMID = String - -class SearchResult: CustomStringConvertible { - let title: String - let path: String - let bookID: ZIMID - let snippet: String? - - let probability: Double? // range: 0.0 - 1.0 - let distance: Int // Levenshtein distance, non negative integer - private(set) lazy var score: Double = { - if let probability = self.probability { - return WeightFactor.calculate(probability) * Double(self.distance) - } else { - return Double(self.distance) - } - }() - init?(rawResult: [String: AnyObject]) { - let title = (rawResult["title"] as? String) ?? "" - let path = (rawResult["path"] as? String) ?? "" - let bookID = (rawResult["bookID"] as? ZIMID) ?? "" - let snippet = rawResult["snippet"] as? String + // MARK: - Search + + func search(searchTerm: String, zimFileID: String) -> [(id: String, articleTitle: String)] { + var resultTuples = [(id: String, articleTitle: String)]() + let firstCharRange = searchTerm.startIndex...searchTerm.startIndex + let firstLetterCapitalisedSearchTerm = searchTerm.stringByReplacingCharactersInRange(firstCharRange, withString: searchTerm.substringWithRange(firstCharRange).capitalizedString) + let searchTermVariations = Set([searchTerm, searchTerm.uppercaseString, searchTerm.lowercaseString, searchTerm.capitalizedString, firstLetterCapitalisedSearchTerm]) - let distance = (rawResult["distance"]as? NSNumber)?.integerValue ?? title.characters.count - let probability: Double? = { - if let probability = (rawResult["probability"] as? NSNumber)?.doubleValue { - return probability / 100.0 - } else { - return nil - } - }() + let reader = readers[zimFileID] + var results = Set() + for searchTermVariation in searchTermVariations { + guard let result = reader?.searchSuggestionsSmart(searchTermVariation) as? [String] else {continue} + results.unionInPlace(result) + } - self.title = title - self.path = path - self.bookID = bookID - self.snippet = snippet - self.probability = probability - self.distance = distance + for result in results { + resultTuples.append((id: zimFileID, articleTitle: result)) + } - if title == "" || path == "" || bookID == "" {return nil} + return resultTuples + } + + // MARK: - Loading System + + func data(id: String, contentURLString: String) -> [String: AnyObject]? { + guard let reader = readers[id] else {return nil} + return reader.dataWithContentURLString(contentURLString) as? [String: AnyObject] + } + + func pageURLString(articleTitle: String, bookid id: String) -> String? { + guard let reader = readers[id] else {return nil} + return reader.pageURLFromTitle(articleTitle) } - var description: String { - var parts = [bookID, title] - if let probability = probability {parts.append("\(probability)%")} - parts.append("dist: \(distance)") - return parts.joinWithSeparator(", ") + func mainPageURLString(bookid id: String) -> String? { + guard let reader = readers[id] else {return nil} + return reader.mainPageURL() } - var rankInfo: String { - return "(\(distance), \(probability ?? -1), \(String(format: "%.4f", score)))" + func randomPageURLString() -> (id: String, contentURLString: String)? { + var randomPageURLs = [(String, String)]() + for (id, reader) in readers{ + randomPageURLs.append((id, reader.getRandomPageUrl())) + } + + guard randomPageURLs.count > 0 else {return nil} + let index = arc4random_uniform(UInt32(randomPageURLs.count)) + return randomPageURLs[Int(index)] } } + +protocol ZimMultiReaderDelegate: class { + func firstBookAdded(id: ZimID) +} + From c1edab0f054716ab963a702c8f67a669a5dc3fe7 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Wed, 13 Jul 2016 09:59:21 -0400 Subject: [PATCH 08/54] Refactor and version bump --- Kiwix-iOS/Info.plist | 4 ++-- Kiwix-iOS/iOSExtensions.swift | 2 +- Kiwix.xcodeproj/project.pbxproj | 4 ++++ Kiwix/FileManager.swift | 8 +++++++- Kiwix/Operations/ScanLocalBookOperation.swift | 13 +++++++++++++ Kiwix/ZimMultiReader/SearchResult.swift | 2 +- Kiwix/ZimMultiReader/ZimMultiReader.swift | 6 +++--- 7 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 Kiwix/Operations/ScanLocalBookOperation.swift diff --git a/Kiwix-iOS/Info.plist b/Kiwix-iOS/Info.plist index a95053fad..add263c7a 100644 --- a/Kiwix-iOS/Info.plist +++ b/Kiwix-iOS/Info.plist @@ -32,11 +32,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.6 + 1.6.1 CFBundleSignature ???? CFBundleVersion - 1583 + 1 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/Kiwix-iOS/iOSExtensions.swift b/Kiwix-iOS/iOSExtensions.swift index ad200e576..34db4a4dc 100644 --- a/Kiwix-iOS/iOSExtensions.swift +++ b/Kiwix-iOS/iOSExtensions.swift @@ -42,7 +42,7 @@ enum BuildStatus { extension UIApplication { class var buildStatus: BuildStatus { get { - return .Release + return .Alpha } } } diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index e1246ea4f..cb900e519 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -117,6 +117,7 @@ 973DD41E1D34428F009D45DB /* ZimMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD41B1D34428F009D45DB /* ZimMultiReader.swift */; }; 973DD4211D34434C009D45DB /* SearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD4201D34434C009D45DB /* SearchResult.swift */; }; 973DD4231D3443A3009D45DB /* ExtensionAndTypealias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD4221D3443A3009D45DB /* ExtensionAndTypealias.swift */; }; + 973DD4251D344558009D45DB /* ScanLocalBookOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD4241D344558009D45DB /* ScanLocalBookOperation.swift */; }; 975227821D020560001D1DDE /* Indexer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227811D020560001D1DDE /* Indexer.storyboard */; }; 975227991D020C00001D1DDE /* indexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 975227921D020C00001D1DDE /* indexer.cpp */; settings = {COMPILER_FLAGS = "-w"; }; }; 9752279B1D020C00001D1DDE /* otherTools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 975227931D020C00001D1DDE /* otherTools.cpp */; }; @@ -364,6 +365,7 @@ 973DD41B1D34428F009D45DB /* ZimMultiReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ZimMultiReader.swift; path = ZimMultiReader/ZimMultiReader.swift; sourceTree = ""; }; 973DD4201D34434C009D45DB /* SearchResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SearchResult.swift; path = ZimMultiReader/SearchResult.swift; sourceTree = ""; }; 973DD4221D3443A3009D45DB /* ExtensionAndTypealias.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ExtensionAndTypealias.swift; path = ZimMultiReader/ExtensionAndTypealias.swift; sourceTree = ""; }; + 973DD4241D344558009D45DB /* ScanLocalBookOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ScanLocalBookOperation.swift; path = Operations/ScanLocalBookOperation.swift; sourceTree = ""; }; 97497B5A1D07487000ECD691 /* indexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = indexer.h; path = "Kiwix/libkiwix/C&C++/indexer.h"; sourceTree = ""; }; 97497B5B1D07487000ECD691 /* resourceTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = resourceTools.h; path = "Kiwix/libkiwix/C&C++/resourceTools.h"; sourceTree = ""; }; 97497B5C1D074FB800ECD691 /* htmlparse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = htmlparse.h; path = "Kiwix/libkiwix/C&C++/xapian/htmlparse.h"; sourceTree = ""; }; @@ -1053,6 +1055,7 @@ 973DD4031D343F09009D45DB /* Basic */, 9779A1C31D34225E0071EFAB /* GlobalOperationQueue.swift */, 9779A1C51D34225E0071EFAB /* RefreshLibraryOperation.swift */, + 973DD4241D344558009D45DB /* ScanLocalBookOperation.swift */, 9779A1C61D34225E0071EFAB /* SearchOperation.swift */, 970C61961D34243600087758 /* URLSessionDownloadTaskOperation.swift */, ); @@ -1605,6 +1608,7 @@ 971046321D19B96E002141C0 /* XapianSearcher.mm in Sources */, 973C8D5C1C25F945007272F9 /* Preference.swift in Sources */, 975B90FE1CEB909100D13906 /* iOSExtensions.swift in Sources */, + 973DD4251D344558009D45DB /* ScanLocalBookOperation.swift in Sources */, 971A10521D022D9D007FC62C /* AppDelegate.swift in Sources */, 977998781C1E0B7900B1DD5E /* Language+CoreDataProperties.swift in Sources */, 97D452BE1D1723FF0033666F /* CollectionViewCells.swift in Sources */, diff --git a/Kiwix/FileManager.swift b/Kiwix/FileManager.swift index a0ac52a73..0e9a77497 100644 --- a/Kiwix/FileManager.swift +++ b/Kiwix/FileManager.swift @@ -72,9 +72,15 @@ class FileManager { } } + // MARK: - Contents of Doc Folder + + class var zimFileURLsInDocDir: [NSURL] { + return [NSURL]() + } + // MARK: - Item Operations - class func fileExistAtURL(url: NSURL) -> Bool { + class func itemExistAtURL(url: NSURL) -> Bool { guard let path = url.path else {return false} return NSFileManager.defaultManager().fileExistsAtPath(path) } diff --git a/Kiwix/Operations/ScanLocalBookOperation.swift b/Kiwix/Operations/ScanLocalBookOperation.swift new file mode 100644 index 000000000..4bf4a524b --- /dev/null +++ b/Kiwix/Operations/ScanLocalBookOperation.swift @@ -0,0 +1,13 @@ +// +// ScanLocalBookOperation.swift +// Kiwix +// +// Created by Chris Li on 7/11/16. +// Copyright © 2016 Chris. All rights reserved. +// + +import UIKit + +class ScanLocalBookOperation: NSObject { + +} diff --git a/Kiwix/ZimMultiReader/SearchResult.swift b/Kiwix/ZimMultiReader/SearchResult.swift index e9c9af852..39ab4693d 100644 --- a/Kiwix/ZimMultiReader/SearchResult.swift +++ b/Kiwix/ZimMultiReader/SearchResult.swift @@ -10,7 +10,7 @@ import UIKit class SearchResult: CustomStringConvertible { let title: String - let path: String + let path: ArticlePath let bookID: ZimID let snippet: String? diff --git a/Kiwix/ZimMultiReader/ZimMultiReader.swift b/Kiwix/ZimMultiReader/ZimMultiReader.swift index 6b6cfb739..450aaa949 100644 --- a/Kiwix/ZimMultiReader/ZimMultiReader.swift +++ b/Kiwix/ZimMultiReader/ZimMultiReader.swift @@ -10,8 +10,10 @@ import CoreData import PSOperations class ZimMultiReader: NSObject, DirectoryMonitorDelegate { - static let sharedInstance = ZimMultiReader() + let searchQueue = OperationQueue() + weak var delegate: ZimMultiReaderDelegate? + private(set) var readers = [ZimID: ZimReader]() { didSet { if readers.count == 1 { @@ -20,8 +22,6 @@ class ZimMultiReader: NSObject, DirectoryMonitorDelegate { } } } - let searchQueue = OperationQueue() - weak var delegate: ZimMultiReaderDelegate? private let monitor = DirectoryMonitor(URL: FileManager.docDirURL) private var zimURLs = Set() From c50c7e8c6ec415fcd5d9975aafd50468d0c3e8a7 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Wed, 13 Jul 2016 15:44:07 -0400 Subject: [PATCH 09/54] ScanOperation Off load scan task to background thread, blazingly fast app launch speed --- Kiwix-iOS/AppDelegate.swift | 1 - Kiwix-iOS/Controller/MainVC.swift | 1 + Kiwix-iOS/Controller/MainVCDelegates.swift | 3 +- Kiwix-iOS/Model/Network.swift | 1 - Kiwix-iOS/Storyboard/Main.storyboard | 1 + .../{ShadowView.swift => ShadowViews.swift} | 0 Kiwix.xcodeproj/project.pbxproj | 8 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 26 +-- Kiwix/CoreData/Book.swift | 16 +- Kiwix/Operations/ScanLocalBookOperation.swift | 146 ++++++++++++++++- Kiwix/ZimMultiReader/ZimMultiReader.swift | 150 +++++------------- 11 files changed, 212 insertions(+), 141 deletions(-) rename Kiwix-iOS/View/{ShadowView.swift => ShadowViews.swift} (100%) diff --git a/Kiwix-iOS/AppDelegate.swift b/Kiwix-iOS/AppDelegate.swift index 480e43193..15c60c315 100644 --- a/Kiwix-iOS/AppDelegate.swift +++ b/Kiwix-iOS/AppDelegate.swift @@ -57,7 +57,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate { func applicationDidBecomeActive(application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(AppDelegate.recordActiveSession), userInfo: nil, repeats: false) - ZimMultiReader.sharedInstance.scan() } func applicationWillTerminate(application: UIApplication) { diff --git a/Kiwix-iOS/Controller/MainVC.swift b/Kiwix-iOS/Controller/MainVC.swift index a71c33e8d..a88227465 100644 --- a/Kiwix-iOS/Controller/MainVC.swift +++ b/Kiwix-iOS/Controller/MainVC.swift @@ -56,6 +56,7 @@ class MainVC: UIViewController { override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) + // showGetStarted() } diff --git a/Kiwix-iOS/Controller/MainVCDelegates.swift b/Kiwix-iOS/Controller/MainVCDelegates.swift index 7dc5a329c..c6afb542e 100644 --- a/Kiwix-iOS/Controller/MainVCDelegates.swift +++ b/Kiwix-iOS/Controller/MainVCDelegates.swift @@ -45,7 +45,8 @@ extension MainVC: LPTBarButtonItemDelegate, TableOfContentsDelegate, ZimMultiRea // MARK: - ZimMultiReaderDelegate - func firstBookAdded(id: ZimID) { + func firstBookAdded() { + guard let id = ZimMultiReader.sharedInstance.readers.keys.first else {return} loadMainPage(id) } diff --git a/Kiwix-iOS/Model/Network.swift b/Kiwix-iOS/Model/Network.swift index 1fc63b670..11e0e0897 100644 --- a/Kiwix-iOS/Model/Network.swift +++ b/Kiwix-iOS/Model/Network.swift @@ -163,7 +163,6 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU let bookDownloadTask = book.downloadTask else {return} context.performBlockAndWait { () -> Void in - book.isLocal = true self.context.deleteObject(bookDownloadTask) } diff --git a/Kiwix-iOS/Storyboard/Main.storyboard b/Kiwix-iOS/Storyboard/Main.storyboard index 14830c8d1..496f89c52 100644 --- a/Kiwix-iOS/Storyboard/Main.storyboard +++ b/Kiwix-iOS/Storyboard/Main.storyboard @@ -348,6 +348,7 @@