Cleaner logic in LibraryOnlineTBVC & LibRefreshOp

This commit is contained in:
automactic 2016-05-05 14:23:37 -04:00
parent a6e03b6fec
commit 4fec367d75
2 changed files with 58 additions and 79 deletions

View File

@ -9,11 +9,11 @@
import UIKit import UIKit
import CoreData import CoreData
class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelegate, BookTableCellDelegate, LTBarButtonItemDelegate, RefreshLibraryOperationDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate { class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelegate, BookTableCellDelegate, LTBarButtonItemDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate {
var booksShowingDetail = Set<Book>() var booksShowingDetail = Set<Book>()
weak var refreshOperation: RefreshLibraryOperation?
var messsageLabelConfigTimer: NSTimer? var messsageLabelConfigTimer: NSTimer?
var refreshing = false
// MARK: - Override // MARK: - Override
@ -26,18 +26,20 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
tableView.emptyDataSetSource = self tableView.emptyDataSetSource = self
tableView.emptyDataSetDelegate = self tableView.emptyDataSetDelegate = self
reconnectToExistingRefreshOperation()
refreshLibraryForTheFirstTime()
configureToolBar() configureToolBar()
} }
override func viewWillAppear(animated: Bool) { override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
segmentedControl.selectedSegmentIndex = 0 segmentedControl.selectedSegmentIndex = 0
configureRefreshStatus()
messsageLabelConfigTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(configureMessage), userInfo: nil, repeats: true) 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) { override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
messsageLabelConfigTimer?.invalidate() messsageLabelConfigTimer?.invalidate()
@ -68,38 +70,40 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
startRefresh(invokedAutomatically: false) startRefresh(invokedAutomatically: false)
} }
// MARK: - RefreshLibraryOperationDelegate
func refreshDidStart() {
configureRefreshStatus()
configureToolBarVisibility(animated: true)
}
func refreshDidFinish() {
configureRefreshStatus()
configureToolBarVisibility(animated: true)
}
// MARK: - Others // MARK: - Others
func reconnectToExistingRefreshOperation() {
guard let operation = refreshOperation ??
UIApplication.globalOperationQueue.operation(String(RefreshLibraryOperation)) as? RefreshLibraryOperation
else {return}
refreshOperation = operation
operation.delegate = self
}
func refreshLibraryForTheFirstTime() { func refreshLibraryForTheFirstTime() {
guard Preference.libraryLastRefreshTime == nil else {return} guard Preference.libraryLastRefreshTime == nil else {return}
startRefresh(invokedAutomatically: true) startRefresh(invokedAutomatically: true)
} }
func startRefresh(invokedAutomatically invokedAutomatically: Bool) { func startRefresh(invokedAutomatically invokedAutomatically: Bool) {
let refreshOperation = RefreshLibraryOperation(invokedAutomatically: invokedAutomatically) let refreshOperation = RefreshLibraryOperation(invokedAutomatically: invokedAutomatically) { (errorCode) in
refreshOperation.delegate = self 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) UIApplication.globalOperationQueue.addOperation(refreshOperation)
self.refreshOperation = refreshOperation
} }
// MARK: - ToolBar Button // MARK: - ToolBar Button
@ -126,15 +130,13 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
setToolbarItems(toolBarItems, animated: false) setToolbarItems(toolBarItems, animated: false)
configureToolBarVisibility(animated: false) configureToolBarVisibility(animated: false)
configureMessage(isRefreshing: false) configureMessage()
} }
func configureToolBarVisibility(animated animated: Bool) { func configureMessage() {
navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated) if refreshing {
} messageButton.text = LocalizedStrings.refreshing
} else {
func configureMessage(isRefreshing isRefreshing: Bool = false) {
if !isRefreshing {
guard let sectionInfos = fetchedResultController.sections else {messageButton.text = nil; return} guard let sectionInfos = fetchedResultController.sections else {messageButton.text = nil; return}
let count = sectionInfos.reduce(0) {$0 + $1.numberOfObjects} 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) 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 return string
}() }()
messageButton.text = localizedBookCountString + "\n" + localizedRefreshTimeString messageButton.text = localizedBookCountString + "\n" + localizedRefreshTimeString
} else {
messageButton.text = LocalizedStrings.refreshing
} }
} }
func configureRefreshStatus() { func configureToolBarVisibility(animated animated: Bool) {
let executing = refreshOperation?.executing ?? false navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated)
executing ? refreshLibButton.startRotating() : refreshLibButton.stopRotating() }
configureMessage(isRefreshing: executing)
func configureRotatingStatus() {
refreshing ? refreshLibButton.startRotating() : refreshLibButton.stopRotating()
}
func configureEmptyTableBackground() {
tableView.reloadEmptyDataSet() tableView.reloadEmptyDataSet()
} }
@ -187,7 +192,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
} }
func buttonTitleForEmptyDataSet(scrollView: UIScrollView!, forState state: UIControlState) -> NSAttributedString! { 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 text = NSLocalizedString("Refreshing...", comment: "Book Library, book downloader, refreshing button text")
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(17.0), NSForegroundColorAttributeName: UIColor.darkGrayColor()] let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(17.0), NSForegroundColorAttributeName: UIColor.darkGrayColor()]
return NSAttributedString(string: text, attributes: attributes) return NSAttributedString(string: text, attributes: attributes)
@ -207,7 +212,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
} }
func emptyDataSetDidTapButton(scrollView: UIScrollView!) { func emptyDataSetDidTapButton(scrollView: UIScrollView!) {
guard self.refreshOperation == nil else {return} guard !refreshing else {return}
startRefresh(invokedAutomatically: false) startRefresh(invokedAutomatically: false)
} }
@ -310,7 +315,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
fetchedResultController.fetchRequest.predicate = onlineCompoundPredicate fetchedResultController.fetchRequest.predicate = onlineCompoundPredicate
fetchedResultController.performFetch(deleteCache: true) fetchedResultController.performFetch(deleteCache: true)
tableView.reloadData() tableView.reloadData()
configureMessage(isRefreshing: false) configureMessage()
} }
private var langPredicate: NSPredicate { private var langPredicate: NSPredicate {
@ -360,5 +365,7 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
func controllerDidChangeContent(controller: NSFetchedResultsController) { func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates() tableView.endUpdates()
configureToolBarVisibility(animated: true)
configureMessage()
} }
} }

View File

@ -11,21 +11,20 @@ import CoreData
class RefreshLibraryOperation: GroupOperation { class RefreshLibraryOperation: GroupOperation {
weak var delegate: RefreshLibraryOperationDelegate? var completionHandler: ((errorCode: Int?) -> Void)?
weak var presentationContext: LibraryOnlineTBVC?
var completionHandler: (() -> Void)?
init(invokedAutomatically: Bool, presentationContext: LibraryOnlineTBVC? = nil, completionHandler: (() -> Void)? = nil) { init(invokedAutomatically: Bool, completionHandler: ((errorCode: Int?) -> Void)?) {
super.init(operations: []) super.init(operations: [])
name = String(RefreshLibraryOperation) name = String(RefreshLibraryOperation)
self.completionHandler = completionHandler
// 1.Parse // 1.Parse
let parseOperation = ParseLibraryOperation() let parseOperation = ParseLibraryOperation()
// 0.Download library // 0.Download library
let url = NSURL(string: "http://www.kiwix.org/library.xml")! 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)} if let error = error {self.aggregateError(error)}
parseOperation.xmlData = data parseOperation.xmlData = data
} }
@ -33,24 +32,9 @@ class RefreshLibraryOperation: GroupOperation {
fetchOperation.addObserver(NetworkObserver()) fetchOperation.addObserver(NetworkObserver())
fetchOperation.addCondition(ReachabilityCondition(host: url, allowCellular: Preference.libraryRefreshAllowCellularData)) 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<RefreshLibraryOperation>())
if invokedAutomatically { if invokedAutomatically {
addCondition(AllowAutoRefreshCondition()) addCondition(AllowAutoRefreshCondition())
addCondition(LibraryIsOldCondition()) addCondition(LibraryIsOldCondition())
addCondition(ReachabilityCondition(host: url, allowCellular: Preference.libraryRefreshAllowCellularData))
} }
addOperation(fetchOperation) addOperation(fetchOperation)
@ -59,22 +43,10 @@ class RefreshLibraryOperation: GroupOperation {
} }
override func finished(errors: [NSError]) { override func finished(errors: [NSError]) {
if let firstError = errors.first { completionHandler?(errorCode: errors.first?.code)
if firstError.code == .NetworkError {
produceOperation(RefreshLibraryInternetRequiredAlert(presentationContext: presentationContext))
}
} else {
guard !Preference.libraryHasShownPreferredLanguagePrompt else {return}
produceOperation(RefreshLibraryLanguageFilterAlert(libraryOnlineTBVC: presentationContext))
}
} }
} }
protocol RefreshLibraryOperationDelegate: class {
func refreshDidStart()
func refreshDidFinish()
}
class ParseLibraryOperation: Operation, NSXMLParserDelegate { class ParseLibraryOperation: Operation, NSXMLParserDelegate {
var xmlData: NSData? var xmlData: NSData?
let context: NSManagedObjectContext let context: NSManagedObjectContext
@ -91,7 +63,7 @@ class ParseLibraryOperation: Operation, NSXMLParserDelegate {
} }
override func execute() { override func execute() {
guard let data = xmlData else {return} guard let data = xmlData else {finish(); return}
let xmlParser = NSXMLParser(data: data) let xmlParser = NSXMLParser(data: data)
xmlParser.delegate = self xmlParser.delegate = self
xmlParser.parse() 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 elementName == "book" else {return}
guard let id = attributeDict["id"] else {return} guard let id = attributeDict["id"] else {return}