diff --git a/Kiwix/LibraryOnlineTBVC.swift b/Kiwix/LibraryOnlineTBVC.swift index f128c14b..68b80fda 100644 --- a/Kiwix/LibraryOnlineTBVC.swift +++ b/Kiwix/LibraryOnlineTBVC.swift @@ -9,11 +9,11 @@ import UIKit import CoreData -class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelegate, BookTableCellDelegate, LTBarButtonItemDelegate, RefreshLibraryOperationDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate { +class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelegate, BookTableCellDelegate, LTBarButtonItemDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate { var booksShowingDetail = Set() - weak var refreshOperation: RefreshLibraryOperation? var messsageLabelConfigTimer: NSTimer? + var refreshing = false // MARK: - Override @@ -26,18 +26,20 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega tableView.emptyDataSetSource = self tableView.emptyDataSetDelegate = self - reconnectToExistingRefreshOperation() - refreshLibraryForTheFirstTime() configureToolBar() } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) segmentedControl.selectedSegmentIndex = 0 - configureRefreshStatus() messsageLabelConfigTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(configureMessage), userInfo: nil, repeats: true) } + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + refreshLibraryForTheFirstTime() + } + override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) messsageLabelConfigTimer?.invalidate() @@ -68,38 +70,40 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega startRefresh(invokedAutomatically: false) } - // MARK: - RefreshLibraryOperationDelegate - - func refreshDidStart() { - configureRefreshStatus() - configureToolBarVisibility(animated: true) - } - - func refreshDidFinish() { - configureRefreshStatus() - configureToolBarVisibility(animated: true) - } - // MARK: - Others - func reconnectToExistingRefreshOperation() { - guard let operation = refreshOperation ?? - UIApplication.globalOperationQueue.operation(String(RefreshLibraryOperation)) as? RefreshLibraryOperation - else {return} - refreshOperation = operation - operation.delegate = self - } - func refreshLibraryForTheFirstTime() { guard Preference.libraryLastRefreshTime == nil else {return} startRefresh(invokedAutomatically: true) } func startRefresh(invokedAutomatically invokedAutomatically: Bool) { - let refreshOperation = RefreshLibraryOperation(invokedAutomatically: invokedAutomatically) - refreshOperation.delegate = self + let refreshOperation = RefreshLibraryOperation(invokedAutomatically: invokedAutomatically) { (errorCode) in + defer { + NSOperationQueue.mainQueue().addOperationWithBlock({ + self.refreshing = false + self.configureMessage() + self.configureRotatingStatus() + self.configureEmptyTableBackground() + }) + } + if let errorCode = errorCode { + if errorCode == .NetworkError { + let alertOperation = RefreshLibraryInternetRequiredAlert(presentationContext: self) + UIApplication.appDelegate.globalOperationQueue.addOperation(alertOperation) + } + } else { + guard !Preference.libraryHasShownPreferredLanguagePrompt else {return} + let operation = RefreshLibraryLanguageFilterAlert(libraryOnlineTBVC: self) + UIApplication.appDelegate.globalOperationQueue.addOperation(operation) + } + } + + refreshing = true + configureMessage() + configureRotatingStatus() + configureEmptyTableBackground() UIApplication.globalOperationQueue.addOperation(refreshOperation) - self.refreshOperation = refreshOperation } // MARK: - ToolBar Button @@ -126,15 +130,13 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega setToolbarItems(toolBarItems, animated: false) configureToolBarVisibility(animated: false) - configureMessage(isRefreshing: false) + configureMessage() } - func configureToolBarVisibility(animated animated: Bool) { - navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated) - } - - func configureMessage(isRefreshing isRefreshing: Bool = false) { - if !isRefreshing { + func configureMessage() { + if refreshing { + messageButton.text = LocalizedStrings.refreshing + } else { guard let sectionInfos = fetchedResultController.sections else {messageButton.text = nil; return} let count = sectionInfos.reduce(0) {$0 + $1.numberOfObjects} let localizedBookCountString = String.localizedStringWithFormat(NSLocalizedString("%d book(s) available for download", comment: "Book Library, online book catalogue message"), count) @@ -150,15 +152,18 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega return string }() messageButton.text = localizedBookCountString + "\n" + localizedRefreshTimeString - } else { - messageButton.text = LocalizedStrings.refreshing } } - func configureRefreshStatus() { - let executing = refreshOperation?.executing ?? false - executing ? refreshLibButton.startRotating() : refreshLibButton.stopRotating() - configureMessage(isRefreshing: executing) + func configureToolBarVisibility(animated animated: Bool) { + navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated) + } + + func configureRotatingStatus() { + refreshing ? refreshLibButton.startRotating() : refreshLibButton.stopRotating() + } + + func configureEmptyTableBackground() { tableView.reloadEmptyDataSet() } @@ -187,7 +192,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega } func buttonTitleForEmptyDataSet(scrollView: UIScrollView!, forState state: UIControlState) -> NSAttributedString! { - if let _ = refreshOperation { + if refreshing == true { let text = NSLocalizedString("Refreshing...", comment: "Book Library, book downloader, refreshing button text") let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(17.0), NSForegroundColorAttributeName: UIColor.darkGrayColor()] return NSAttributedString(string: text, attributes: attributes) @@ -207,7 +212,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega } func emptyDataSetDidTapButton(scrollView: UIScrollView!) { - guard self.refreshOperation == nil else {return} + guard !refreshing else {return} startRefresh(invokedAutomatically: false) } @@ -310,7 +315,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega fetchedResultController.fetchRequest.predicate = onlineCompoundPredicate fetchedResultController.performFetch(deleteCache: true) tableView.reloadData() - configureMessage(isRefreshing: false) + configureMessage() } private var langPredicate: NSPredicate { @@ -360,5 +365,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() + configureToolBarVisibility(animated: true) + configureMessage() } } diff --git a/Kiwix/RefreshLibraryOperation.swift b/Kiwix/RefreshLibraryOperation.swift index 9edb684d..5e4d8bd7 100644 --- a/Kiwix/RefreshLibraryOperation.swift +++ b/Kiwix/RefreshLibraryOperation.swift @@ -11,21 +11,20 @@ import CoreData class RefreshLibraryOperation: GroupOperation { - weak var delegate: RefreshLibraryOperationDelegate? - weak var presentationContext: LibraryOnlineTBVC? - var completionHandler: (() -> Void)? + var completionHandler: ((errorCode: Int?) -> Void)? - init(invokedAutomatically: Bool, presentationContext: LibraryOnlineTBVC? = nil, completionHandler: (() -> Void)? = nil) { + init(invokedAutomatically: Bool, completionHandler: ((errorCode: Int?) -> Void)?) { super.init(operations: []) name = String(RefreshLibraryOperation) + self.completionHandler = completionHandler // 1.Parse let parseOperation = ParseLibraryOperation() // 0.Download library let url = NSURL(string: "http://www.kiwix.org/library.xml")! - let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in + let task = NSURLSession.sharedSession().dataTaskWithURL(url) { [unowned parseOperation] (data, response, error) -> Void in if let error = error {self.aggregateError(error)} parseOperation.xmlData = data } @@ -33,24 +32,9 @@ class RefreshLibraryOperation: GroupOperation { fetchOperation.addObserver(NetworkObserver()) fetchOperation.addCondition(ReachabilityCondition(host: url, allowCellular: Preference.libraryRefreshAllowCellularData)) - let stateObserver = BlockObserver( - startHandler: { (operation) -> Void in - NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in - self.delegate?.refreshDidStart() - }) - }, - produceHandler: nil) { (operation, errors) -> Void in - NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in - self.delegate?.refreshDidFinish() - }) - } - addObserver(stateObserver) - - addCondition(MutuallyExclusive()) if invokedAutomatically { addCondition(AllowAutoRefreshCondition()) addCondition(LibraryIsOldCondition()) - addCondition(ReachabilityCondition(host: url, allowCellular: Preference.libraryRefreshAllowCellularData)) } addOperation(fetchOperation) @@ -59,22 +43,10 @@ class RefreshLibraryOperation: GroupOperation { } override func finished(errors: [NSError]) { - if let firstError = errors.first { - if firstError.code == .NetworkError { - produceOperation(RefreshLibraryInternetRequiredAlert(presentationContext: presentationContext)) - } - } else { - guard !Preference.libraryHasShownPreferredLanguagePrompt else {return} - produceOperation(RefreshLibraryLanguageFilterAlert(libraryOnlineTBVC: presentationContext)) - } + completionHandler?(errorCode: errors.first?.code) } } -protocol RefreshLibraryOperationDelegate: class { - func refreshDidStart() - func refreshDidFinish() -} - class ParseLibraryOperation: Operation, NSXMLParserDelegate { var xmlData: NSData? let context: NSManagedObjectContext @@ -91,7 +63,7 @@ class ParseLibraryOperation: Operation, NSXMLParserDelegate { } override func execute() { - guard let data = xmlData else {return} + guard let data = xmlData else {finish(); return} let xmlParser = NSXMLParser(data: data) xmlParser.delegate = self xmlParser.parse() @@ -106,7 +78,7 @@ class ParseLibraryOperation: Operation, NSXMLParserDelegate { } } - @objc internal func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, var attributes attributeDict: [String : String]) { + @objc internal func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { guard elementName == "book" else {return} guard let id = attributeDict["id"] else {return}