Operations

This commit is contained in:
Chris Li 2016-09-12 17:03:03 -04:00
parent ec151e49f9
commit bf3adbe58a
12 changed files with 218 additions and 236 deletions

View File

@ -27,7 +27,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSURLProtocol.registerClass(KiwixURLProtocol)
// Network.shared.restoreProgresses()
//Network.shared.restoreProgresses()
// Register notification
let settings = UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: nil) // Here are the notification permission the app wants
@ -64,7 +64,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} else {
return false
}
}
// MARK: - Active

View File

@ -7,6 +7,7 @@
//
import UIKit
import Operations
class SearchResultTBVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
@ -120,15 +121,19 @@ class SearchResultTBVC: UIViewController, UITableViewDataSource, UITableViewDele
tableView.reloadData()
return
}
let operation = SearchOperation(searchTerm: searchText) { (results) in
guard let results = results else {return}
self.searchResults = results
self.tableView.reloadData()
if results.count > 0 {
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: true)
}
}
ZimMultiReader.sharedInstance.startSearch(operation)
// let operation = SearchOperation(searchTerm: searchText) { [unowned self] (results) in
// guard let results = results else {return}
// self.searchResults = results
// self.tableView.reloadData()
// if results.count > 0 {
// self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: true)
// }
// }
let operation = SearchOperation(searchTerm: searchText)
operation.addObserver(DidFinishObserver {(operation, errors) in
print("search op did finish, result injection")
})
GlobalQueue.shared.add(search: operation)
}
}

View File

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

View File

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

View File

@ -93,7 +93,7 @@
97A127CB1D777CF100FB204D /* SearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A127C71D777CF100FB204D /* SearchController.swift */; };
97A127CC1D777CF100FB204D /* SearchResultTBVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A127C81D777CF100FB204D /* SearchResultTBVC.swift */; };
97A1FD161D6F71CE00A80EE2 /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD121D6F71CE00A80EE2 /* DirectoryMonitor.swift */; };
97A1FD171D6F71CE00A80EE2 /* ExtensionAndTypealias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD131D6F71CE00A80EE2 /* ExtensionAndTypealias.swift */; };
97A1FD171D6F71CE00A80EE2 /* Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD131D6F71CE00A80EE2 /* Extension.swift */; };
97A1FD181D6F71CE00A80EE2 /* SearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD141D6F71CE00A80EE2 /* SearchResult.swift */; };
97A1FD191D6F71CE00A80EE2 /* ZimMultiReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD151D6F71CE00A80EE2 /* ZimMultiReader.swift */; };
97A1FD1C1D6F71D800A80EE2 /* KiwixURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97A1FD1A1D6F71D800A80EE2 /* KiwixURLProtocol.swift */; };
@ -118,12 +118,12 @@
97C601DC1D7F15C400362D4F /* Bookmark.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C601DB1D7F15C400362D4F /* Bookmark.storyboard */; };
97C601DE1D7F342100362D4F /* HTMLHeading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97C601DD1D7F342100362D4F /* HTMLHeading.swift */; };
97D452BE1D1723FF0033666F /* CollectionViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D452BD1D1723FF0033666F /* CollectionViewCells.swift */; };
97D4D64F1D874E6E00C1B065 /* SearchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D4D64E1D874E6E00C1B065 /* SearchOperation.swift */; };
97D55EF61D2075180081B523 /* TableOfContentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D55EF51D2075180081B523 /* TableOfContentsController.swift */; };
97D6811B1D6E2A7100E5FA99 /* DownloadTasksController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D6811A1D6E2A7100E5FA99 /* DownloadTasksController.swift */; };
97D681231D6F70AC00E5FA99 /* GlobalQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */; };
97D681241D6F70AC00E5FA99 /* RefreshLibraryOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */; };
97D681251D6F70AC00E5FA99 /* ScanLocalBookOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D6811E1D6F70AC00E5FA99 /* ScanLocalBookOperation.swift */; };
97D681261D6F70AC00E5FA99 /* SearchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D6811F1D6F70AC00E5FA99 /* SearchOperation.swift */; };
97D681271D6F70AC00E5FA99 /* UIOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D681201D6F70AC00E5FA99 /* UIOperations.swift */; };
97D681281D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */; };
97D6812E1D6F70DE00E5FA99 /* Kiwix.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 97D6812A1D6F70DE00E5FA99 /* Kiwix.xcdatamodeld */; };
@ -315,7 +315,7 @@
97A127C71D777CF100FB204D /* SearchController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchController.swift; sourceTree = "<group>"; };
97A127C81D777CF100FB204D /* SearchResultTBVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchResultTBVC.swift; sourceTree = "<group>"; };
97A1FD121D6F71CE00A80EE2 /* DirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = "<group>"; };
97A1FD131D6F71CE00A80EE2 /* ExtensionAndTypealias.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionAndTypealias.swift; sourceTree = "<group>"; };
97A1FD131D6F71CE00A80EE2 /* Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extension.swift; sourceTree = "<group>"; };
97A1FD141D6F71CE00A80EE2 /* SearchResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchResult.swift; sourceTree = "<group>"; };
97A1FD151D6F71CE00A80EE2 /* ZimMultiReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZimMultiReader.swift; sourceTree = "<group>"; };
97A1FD1A1D6F71D800A80EE2 /* KiwixURLProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KiwixURLProtocol.swift; sourceTree = "<group>"; };
@ -349,12 +349,13 @@
97C601DB1D7F15C400362D4F /* Bookmark.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Bookmark.storyboard; sourceTree = "<group>"; };
97C601DD1D7F342100362D4F /* HTMLHeading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLHeading.swift; sourceTree = "<group>"; };
97D452BD1D1723FF0033666F /* CollectionViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCells.swift; sourceTree = "<group>"; };
97D4D64E1D874E6E00C1B065 /* SearchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchOperation.swift; sourceTree = "<group>"; };
97D55EF51D2075180081B523 /* TableOfContentsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsController.swift; path = "Kiwix-iOS/Controller/TableOfContentsController.swift"; sourceTree = SOURCE_ROOT; };
97D6811A1D6E2A7100E5FA99 /* DownloadTasksController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadTasksController.swift; sourceTree = "<group>"; };
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalQueue.swift; sourceTree = "<group>"; };
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshLibraryOperation.swift; sourceTree = "<group>"; };
97D6811E1D6F70AC00E5FA99 /* ScanLocalBookOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanLocalBookOperation.swift; sourceTree = "<group>"; };
97D6811F1D6F70AC00E5FA99 /* SearchOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchOperation.swift; sourceTree = "<group>"; };
97D6811F1D6F70AC00E5FA99 /* SearchOperation-old.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SearchOperation-old.swift"; sourceTree = "<group>"; };
97D681201D6F70AC00E5FA99 /* UIOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIOperations.swift; sourceTree = "<group>"; };
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateWidgetDataSourceOperation.swift; sourceTree = "<group>"; };
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionDownloadTaskOperation.swift; sourceTree = "<group>"; };
@ -626,10 +627,10 @@
97254FDD1C26442F0056950B /* ZimMultiReader */ = {
isa = PBXGroup;
children = (
97A1FD121D6F71CE00A80EE2 /* DirectoryMonitor.swift */,
97A1FD131D6F71CE00A80EE2 /* ExtensionAndTypealias.swift */,
97A1FD141D6F71CE00A80EE2 /* SearchResult.swift */,
97A1FD151D6F71CE00A80EE2 /* ZimMultiReader.swift */,
97A1FD121D6F71CE00A80EE2 /* DirectoryMonitor.swift */,
97A1FD131D6F71CE00A80EE2 /* Extension.swift */,
97A1FD141D6F71CE00A80EE2 /* SearchResult.swift */,
);
path = ZimMultiReader;
sourceTree = "<group>";
@ -988,7 +989,8 @@
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */,
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */,
97D6811E1D6F70AC00E5FA99 /* ScanLocalBookOperation.swift */,
97D6811F1D6F70AC00E5FA99 /* SearchOperation.swift */,
97D4D64E1D874E6E00C1B065 /* SearchOperation.swift */,
97D6811F1D6F70AC00E5FA99 /* SearchOperation-old.swift */,
97D681201D6F70AC00E5FA99 /* UIOperations.swift */,
97D681211D6F70AC00E5FA99 /* UpdateWidgetDataSourceOperation.swift */,
97D681221D6F70AC00E5FA99 /* URLSessionDownloadTaskOperation.swift */,
@ -1545,7 +1547,6 @@
97A127C41D774C9900FB204D /* ControllerRetainer.swift in Sources */,
97D681321D6F70EC00E5FA99 /* MigrationPolicy.swift in Sources */,
97D6811B1D6E2A7100E5FA99 /* DownloadTasksController.swift in Sources */,
97D681261D6F70AC00E5FA99 /* SearchOperation.swift in Sources */,
9764CBD11D806AD800072D6A /* RefreshLibControl.swift in Sources */,
971A10381D022C15007FC62C /* WebViewController.swift in Sources */,
97C601DE1D7F342100362D4F /* HTMLHeading.swift in Sources */,
@ -1555,6 +1556,7 @@
9764F5991D833F2B00E0B1C4 /* KiwixURL.swift in Sources */,
97A127CC1D777CF100FB204D /* SearchResultTBVC.swift in Sources */,
97A8AD841D6C951A00584ED1 /* LocalBooksController.swift in Sources */,
97D4D64F1D874E6E00C1B065 /* SearchOperation.swift in Sources */,
97D452BE1D1723FF0033666F /* CollectionViewCells.swift in Sources */,
971A102F1D022AD5007FC62C /* Logo.swift in Sources */,
97D55EF61D2075180081B523 /* TableOfContentsController.swift in Sources */,
@ -1565,7 +1567,7 @@
97A1FD261D6F71E200A80EE2 /* ZimReader.mm in Sources */,
97A1FD1C1D6F71D800A80EE2 /* KiwixURLProtocol.swift in Sources */,
97D6813F1D6F712800E5FA99 /* Article+CoreDataProperties.swift in Sources */,
97A1FD171D6F71CE00A80EE2 /* ExtensionAndTypealias.swift in Sources */,
97A1FD171D6F71CE00A80EE2 /* Extension.swift in Sources */,
971A103C1D022C2C007FC62C /* FontSizeTBVC.swift in Sources */,
971A103B1D022C2C007FC62C /* AdjustLayoutTBVC.swift in Sources */,
97219DBD1D383A00009FDFF1 /* BookmarkController.swift in Sources */,

View File

@ -10,6 +10,31 @@ import Operations
class GlobalQueue: OperationQueue {
static let shared = GlobalQueue()
private weak var scanOperation: ScanLocalBookOperation?
private weak var searchOperation: SearchOperation?
func add(scan operation: ScanLocalBookOperation) {
addOperation(operation)
scanOperation = operation
}
func add(search operation: SearchOperation) {
if let _ = searchOperation {
print("search is not released")
}
if let scanOperation = scanOperation {
operation.addDependency(scanOperation)
print("scan not finished")
}
if let searchOperation = self.searchOperation {
searchOperation.cancel()
}
addOperation(operation)
searchOperation = operation
}
}
public enum OperationErrorCode: Int {

View File

@ -11,17 +11,14 @@ import Operations
class ScanLocalBookOperation: Operation {
private let context: NSManagedObjectContext
private var firstBookAdded = false
private(set) var firstBookAdded = false
private var lastZimFileURLSnapshot: Set<NSURL>
private var currentZimFileURLSnapshot = Set<NSURL>()
private(set) var currentZimFileURLSnapshot = Set<NSURL>()
private let lastIndexFolderURLSnapshot: Set<NSURL>
private var currentIndexFolderURLSnapshot = Set<NSURL>()
private(set) var currentIndexFolderURLSnapshot = Set<NSURL>()
private var completionHandler: ((currentZimFileURLSnapshot: Set<NSURL>, currentIndexFolderURLSnapshot: Set<NSURL>, firstBookAdded: Bool) -> Void)
init(lastZimFileURLSnapshot: Set<NSURL>, lastIndexFolderURLSnapshot: Set<NSURL>,
completionHandler: ((currentZimFileURLSnapshot: Set<NSURL>, currentIndexFolderURLSnapshot: Set<NSURL>, firstBookAdded: Bool) -> Void)) {
init(lastZimFileURLSnapshot: Set<NSURL>, lastIndexFolderURLSnapshot: Set<NSURL>) {
self.context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
context.parentContext = NSManagedObjectContext.mainQueueContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
@ -29,7 +26,6 @@ class ScanLocalBookOperation: Operation {
self.lastZimFileURLSnapshot = lastZimFileURLSnapshot
self.lastIndexFolderURLSnapshot = lastIndexFolderURLSnapshot
self.completionHandler = completionHandler
super.init()
addCondition(MutuallyExclusive<ZimMultiReader>())
name = String(self)
@ -54,10 +50,6 @@ class ScanLocalBookOperation: Operation {
override func operationDidFinish(errors: [ErrorType]) {
context.performBlockAndWait {self.context.saveIfNeeded()}
NSManagedObjectContext.mainQueueContext.performBlockAndWait {NSManagedObjectContext.mainQueueContext.saveIfNeeded()}
NSOperationQueue.mainQueue().addOperationWithBlock {
self.completionHandler(currentZimFileURLSnapshot: self.currentZimFileURLSnapshot,
currentIndexFolderURLSnapshot: self.currentIndexFolderURLSnapshot, firstBookAdded: self.firstBookAdded)
}
}
private func updateReaders() {

View File

@ -0,0 +1,110 @@
//
// SearchOperation.swift
// Kiwix
//
// Created by Chris Li on 4/9/16.
// Copyright © 2016 Chris. All rights reserved.
//
import UIKit
import Operations
class SearchOperation: GroupOperation {
let completionHandler: ([SearchResult]?) -> Void
private(set) var results = [SearchResult]()
//private let startTime = NSDate()
init(searchTerm: String, completionHandler: ([SearchResult]?) -> Void) {
self.completionHandler = completionHandler
super.init(operations: [NSOperation]())
let sortOperation = SortSearchResultsOperation { (results) in
self.results = results
}
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}
let operation = SingleBookSearchOperation(zimReader: zimReader,
lowerCaseSearchTerm: searchTerm.lowercaseString,
completionHandler: { [unowned sortOperation] (results) in
sortOperation.results += results
})
addOperation(operation)
sortOperation.addDependency(operation)
}
addOperation(sortOperation)
addCondition(MutuallyExclusive<ZimMultiReader>())
}
override func operationDidFinish(errors: [ErrorType]) {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.completionHandler(self.cancelled ? nil : self.results)
}
}
}
private class SingleBookSearchOperation: Operation {
let zimReader: ZimReader
let lowerCaseSearchTerm: String
let completionHandler: ([SearchResult]) -> Void
init(zimReader: ZimReader, lowerCaseSearchTerm: String, completionHandler: ([SearchResult]) -> Void) {
self.zimReader = zimReader
self.lowerCaseSearchTerm = lowerCaseSearchTerm
self.completionHandler = completionHandler
super.init()
}
override private func execute() {
var results = [String: SearchResult]()
let indexedDics = zimReader.searchUsingIndex(lowerCaseSearchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
let titleDics = zimReader.searchSuggestionsSmart(lowerCaseSearchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
let mixedDics = titleDics + indexedDics // It is important we process the title search result first, so that we always keep the indexed search result
for dic in mixedDics {
guard let result = SearchResult (rawResult: dic, lowerCaseSearchTerm: lowerCaseSearchTerm) else {continue}
results[result.path] = result
}
completionHandler(Array(results.values))
finish()
}
}
private class SortSearchResultsOperation: Operation {
let completionHandler: ([SearchResult]) -> Void
var results = [SearchResult]()
init(completionHandler: ([SearchResult]) -> Void) {
self.completionHandler = completionHandler
super.init()
}
override private func execute() {
sort()
completionHandler(results)
finish()
}
private func sort() {
results.sortInPlace { (result0, result1) -> Bool in
if result0.score != result1.score {
return result0.score < result1.score
} else {
if result0.snippet != nil {return true}
if result1.snippet != nil {return false}
return titleCaseInsensitiveCompare(result0, result1: result1)
}
}
}
// MARK: - Utilities
private func titleCaseInsensitiveCompare(result0: SearchResult, result1: SearchResult) -> Bool {
return result0.title.caseInsensitiveCompare(result1.title) == NSComparisonResult.OrderedAscending
}
}

View File

@ -2,109 +2,14 @@
// SearchOperation.swift
// Kiwix
//
// Created by Chris Li on 4/9/16.
// Created by Chris Li on 9/12/16.
// Copyright © 2016 Chris. All rights reserved.
//
import UIKit
import Operations
class SearchOperation: GroupOperation {
let completionHandler: ([SearchResult]?) -> Void
private(set) var results = [SearchResult]()
//private let startTime = NSDate()
init(searchTerm: String, completionHandler: ([SearchResult]?) -> Void) {
self.completionHandler = completionHandler
super.init(operations: [NSOperation]())
let sortOperation = SortSearchResultsOperation { (results) in
self.results = results
}
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}
let operation = SingleBookSearchOperation(zimReader: zimReader,
lowerCaseSearchTerm: searchTerm.lowercaseString,
completionHandler: { [unowned sortOperation] (results) in
sortOperation.results += results
})
addOperation(operation)
sortOperation.addDependency(operation)
}
addOperation(sortOperation)
addCondition(MutuallyExclusive<ZimMultiReader>())
}
override func operationDidFinish(errors: [ErrorType]) {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.completionHandler(self.cancelled ? nil : self.results)
}
init(searchTerm: String) {
super.init(operations: [])
}
}
private class SingleBookSearchOperation: Operation {
let zimReader: ZimReader
let lowerCaseSearchTerm: String
let completionHandler: ([SearchResult]) -> Void
init(zimReader: ZimReader, lowerCaseSearchTerm: String, completionHandler: ([SearchResult]) -> Void) {
self.zimReader = zimReader
self.lowerCaseSearchTerm = lowerCaseSearchTerm
self.completionHandler = completionHandler
super.init()
}
override private func execute() {
var results = [String: SearchResult]()
let indexedDics = zimReader.searchUsingIndex(lowerCaseSearchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
let titleDics = zimReader.searchSuggestionsSmart(lowerCaseSearchTerm) as? [[String: AnyObject]] ?? [[String: AnyObject]]()
let mixedDics = titleDics + indexedDics // It is important we process the title search result first, so that we always keep the indexed search result
for dic in mixedDics {
guard let result = SearchResult (rawResult: dic, lowerCaseSearchTerm: lowerCaseSearchTerm) else {continue}
results[result.path] = result
}
completionHandler(Array(results.values))
finish()
}
}
private class SortSearchResultsOperation: Operation {
let completionHandler: ([SearchResult]) -> Void
var results = [SearchResult]()
init(completionHandler: ([SearchResult]) -> Void) {
self.completionHandler = completionHandler
super.init()
}
override private func execute() {
sort()
completionHandler(results)
finish()
}
private func sort() {
results.sortInPlace { (result0, result1) -> Bool in
if result0.score != result1.score {
return result0.score < result1.score
} else {
if result0.snippet != nil {return true}
if result1.snippet != nil {return false}
return titleCaseInsensitiveCompare(result0, result1: result1)
}
}
}
// MARK: - Utilities
private func titleCaseInsensitiveCompare(result0: SearchResult, result1: SearchResult) -> Bool {
return result0.title.caseInsensitiveCompare(result1.title) == NSComparisonResult.OrderedAscending
}
}

View File

@ -0,0 +1,32 @@
//
// ExtensionAndTypealias.swift
// Kiwix
//
// Created by Chris Li on 7/11/16.
// Copyright © 2016 Chris. All rights reserved.
//
import UIKit
typealias ZimID = String
typealias ArticlePath = String
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
}
}

View File

@ -1,76 +0,0 @@
//
// ExtensionAndTypealias.swift
// Kiwix
//
// Created by Chris Li on 7/11/16.
// Copyright © 2016 Chris. All rights reserved.
//
import UIKit
typealias ZimID = String
typealias ArticlePath = String
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
}
}
// https://gist.github.com/adamyanalunas/69f6601fad6040686d300a1cdc20f500
private extension String {
subscript(index: Int) -> Character {
return self[startIndex.advancedBy(index)]
}
subscript(range: Range<Int>) -> String {
let start = startIndex.advancedBy(range.startIndex)
let end = startIndex.advancedBy(range.endIndex)
return self[start..<end]
}
}
extension String {
func levenshtein(string cmpString: String) -> Int {
let (length, cmpLength) = (characters.count, cmpString.characters.count)
var matrix = Array(
count: cmpLength + 1,
repeatedValue: Array(
count: length + 1,
repeatedValue: 0
)
)
for m in 1..<cmpLength {
matrix[m][0] = matrix[m - 1][0] + 1
}
for n in 1..<length {
matrix[0][n] = matrix[0][n - 1] + 1
}
for m in 1..<(cmpLength + 1) {
for n in 1..<(length + 1) {
let penalty = self[n - 1] == cmpString[m - 1] ? 0 : 1
let (horizontal, vertical, diagonal) = (matrix[m - 1][n] + 1, matrix[m][n - 1] + 1, matrix[m - 1][n - 1])
matrix[m][n] = min(horizontal, vertical, diagonal + penalty)
}
}
return matrix[cmpLength][length]
}
}

View File

@ -13,12 +13,9 @@ class ZimMultiReader: NSObject, DirectoryMonitorDelegate {
static let sharedInstance = ZimMultiReader()
weak var delegate: ZimMultiReaderDelegate?
private weak var scanOperation: ScanLocalBookOperation?
private weak var searchOperation: SearchOperation?
private let searchQueue = OperationQueue()
private(set) var readers = [ZimID: ZimReader]()
private let monitor = DirectoryMonitor(URL: NSFileManager.docDirURL)
private(set) var readers = [ZimID: ZimReader]()
private var lastZimFileURLSnapshot = Set<NSURL>()
private var lastIndexFolderURLSnapshot = Set<NSURL>()
@ -35,15 +32,20 @@ class ZimMultiReader: NSObject, DirectoryMonitorDelegate {
}
func startScan() {
let scanOperation = ScanLocalBookOperation(lastZimFileURLSnapshot: lastZimFileURLSnapshot, lastIndexFolderURLSnapshot: lastIndexFolderURLSnapshot) { (currentZimFileURLSnapshot, currentIndexFolderURLSnapshot, firstBookAdded) in
self.lastZimFileURLSnapshot = currentZimFileURLSnapshot
self.lastIndexFolderURLSnapshot = currentIndexFolderURLSnapshot
if firstBookAdded {
let operation = ScanLocalBookOperation(lastZimFileURLSnapshot: lastZimFileURLSnapshot, lastIndexFolderURLSnapshot: lastIndexFolderURLSnapshot)
operation.addObserver(DidFinishObserver { (operation, errors) in
guard let operation = operation as? ScanLocalBookOperation else {return}
NSOperationQueue.mainQueue().addOperationWithBlock({
self.lastZimFileURLSnapshot = operation.currentZimFileURLSnapshot
self.lastIndexFolderURLSnapshot = operation.currentIndexFolderURLSnapshot
guard operation.firstBookAdded else {return}
self.delegate?.firstBookAdded()
}
}
GlobalQueue.shared.addOperation(scanOperation)
self.scanOperation = scanOperation
})
})
operation.queuePriority = .VeryHigh
if readers.count == 0 { operation.qualityOfService = .UserInitiated }
GlobalQueue.shared.add(scan: operation)
}
// MARK: - Reader Addition / Deletion
@ -69,20 +71,6 @@ class ZimMultiReader: NSObject, DirectoryMonitorDelegate {
startScan()
}
// MARK: - Search
func startSearch(searchOperation: SearchOperation) {
if let scanOperation = scanOperation {
searchOperation.addDependency(scanOperation)
}
if let searchOperation = self.searchOperation {
searchOperation.cancel()
}
searchQueue.addOperation(searchOperation)
self.searchOperation = searchOperation
}
// MARK: - Loading System
func data(id: String, contentURLString: String) -> [String: AnyObject]? {