Created
September 27, 2015 06:27
-
-
Save smyrgl/e4922230d25d90ef8a98 to your computer and use it in GitHub Desktop.
Update ASTableView/CollectionView using TLIndexPathTools
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Operations | |
typealias Node = ASCellNode | |
typealias AttemptUpdateNodeBlock = (Node, AnyObject) -> Bool | |
final class UpdateAsyncTableCollectionViewOperation: GroupOperation { | |
var animated = true | |
weak var tableView: ASTableView? | |
weak var collectionView: ASCollectionView? | |
let attemptUpdateNode: AttemptUpdateNodeBlock? | |
let update: TLIndexPathUpdates | |
let managedObjectContext: NSManagedObjectContext | |
init(update: TLIndexPathUpdates, context: NSManagedObjectContext, attemptUpdateNode: AttemptUpdateNodeBlock? = nil) { | |
self.update = update | |
self.managedObjectContext = context | |
self.attemptUpdateNode = attemptUpdateNode | |
super.init(operations: []) | |
} | |
override func execute() { | |
var suboperations = [Operation]() | |
logInfo("Update table operation starting") | |
let deletesAndMoveDeletes = update.deletedItems + update.movedItems | |
logInfo("Total deletes and move deletes: \(deletesAndMoveDeletes.count)") | |
let reportedDeletes = deletesAndMoveDeletes | |
.map { return update.oldDataModel!.indexPathForItem($0)! } | |
.filter { !update.deletedSectionNames.containsObject(update.oldDataModel!.sectionNameForSection($0.section)) } | |
.sort { $0.compare($1) == .OrderedDescending } | |
logInfo("Filtered deletes and move deletes: \(reportedDeletes.count)") | |
let insertsAndMoveInserts = update.insertedItems + update.movedItems | |
logInfo("Total inserted and move inserts: \(insertsAndMoveInserts.count)") | |
let reportedInserts = insertsAndMoveInserts | |
.map { return update.updatedDataModel!.indexPathForItem($0)! } | |
.filter { !update.insertedSectionNames.containsObject(update.updatedDataModel!.sectionNameForSection($0.section)) } | |
.sort { $0.compare($1) == .OrderedAscending } | |
logInfo("Filtered inserts and move inserts: \(reportedInserts.count)") | |
var unhandledUpdatedItems = Set<NSIndexPath>(update.modifiedItems.map { return update.oldDataModel!.indexPathForItem($0)! }) | |
logInfo("Total item updates: \(unhandledUpdatedItems.count)") | |
let updateVisibleNodesOp: Operation | |
if let attemptUpdateNode = attemptUpdateNode where !unhandledUpdatedItems.isEmpty { | |
updateVisibleNodesOp = BlockOperation(mainQueueBlock: { [weak self] in | |
guard let strongSelf = self else { return } | |
logInfo("Starting attempt updates") | |
let tableView = strongSelf.tableView | |
let collectionView = strongSelf.collectionView | |
guard strongSelf.tableView != nil || strongSelf.collectionView != nil else { return } | |
let visibleIndexPaths = tableView?.indexPathsForVisibleRows ?? collectionView?.indexPathsForVisibleItems() ?? [] | |
let visibleUpdated = unhandledUpdatedItems.intersect(visibleIndexPaths) | |
if visibleUpdated.isEmpty { return } | |
strongSelf.managedObjectContext.performBlockAndWait({ () -> Void in | |
assert(NSThread.isMainThread()) | |
for indexPath in visibleUpdated { | |
let node = (tableView?.nodeForRowAtIndexPath(indexPath) ?? collectionView!.nodeForItemAtIndexPath(indexPath))! | |
let item = strongSelf.update.updatedDataModel!.itemAtIndexPath(indexPath)! | |
if attemptUpdateNode(node, item) { | |
unhandledUpdatedItems.remove(indexPath) | |
} | |
} | |
}) | |
}) | |
} else { | |
updateVisibleNodesOp = BlockOperation(block: nil) | |
} | |
suboperations.append(updateVisibleNodesOp) | |
updateVisibleNodesOp.name = "Update Visible Nodes" | |
let submitUpdateOp = BlockOperation( block: { [weak self] continuation in | |
guard let strongSelf = self else { return } | |
dispatch_async(dispatch_get_main_queue(), { () -> Void in | |
logInfo("Starting submit updates") | |
if let tableView = strongSelf.tableView { | |
tableView.beginUpdates() | |
let update = strongSelf.update | |
logInfo("Unhandled update items count: \(unhandledUpdatedItems.count)") | |
logInfo("Filtered delete items: \(reportedDeletes.count)") | |
logInfo("Section deletes: \(update.deletedSectionNames.count)") | |
logInfo("Section moves: \(update.movedSectionNames.count)") | |
logInfo("Section inserts: \(update.insertedSectionNames.count)") | |
logInfo("Filtered inserts: \(reportedInserts.count)") | |
if !unhandledUpdatedItems.isEmpty { | |
tableView.reloadRowsAtIndexPaths(Array(unhandledUpdatedItems), withRowAnimation: .None) | |
} | |
if !reportedDeletes.isEmpty { | |
tableView.deleteRowsAtIndexPaths(reportedDeletes, withRowAnimation: .Automatic) | |
} | |
let sectionNamesToDelete = update.deletedSectionNames + update.movedSectionNames | |
if sectionNamesToDelete.count > 0 { | |
let sectionsToDelete = NSMutableIndexSet() | |
for sectionName in sectionNamesToDelete { | |
let sectionName = sectionName as! String | |
sectionsToDelete.addIndex(update.oldDataModel!.sectionForSectionName(sectionName)) | |
} | |
tableView.deleteSections(sectionsToDelete, withRowAnimation: .Automatic) | |
} | |
let sectionNamesToInsert = update.insertedSectionNames + update.movedSectionNames | |
if sectionNamesToInsert.count > 0 { | |
let sectionsToInsert = NSMutableIndexSet() | |
for sectionName in sectionNamesToInsert { | |
let sectionName = sectionName as! String | |
sectionsToInsert.addIndex(update.updatedDataModel!.sectionForSectionName(sectionName)) | |
} | |
tableView.insertSections(sectionsToInsert, withRowAnimation: .Automatic) | |
} | |
if reportedInserts.count > 0 { | |
tableView.insertRowsAtIndexPaths(reportedInserts, withRowAnimation: .Automatic) | |
} | |
tableView.endUpdatesAnimated(strongSelf.animated, completion: { finished in | |
continuation() | |
}) | |
} else { | |
continuation() | |
} | |
}) | |
}) | |
suboperations.append(submitUpdateOp) | |
submitUpdateOp.name = "Submit update" | |
for (operation, nextOperation) in zip(suboperations, suboperations[1..<suboperations.count]) { | |
nextOperation.addDependency(operation) | |
} | |
for operation in suboperations { | |
addOperation(operation) | |
} | |
super.execute() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment