Skip to content

Instantly share code, notes, and snippets.

@smyrgl
Created September 27, 2015 06:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smyrgl/e4922230d25d90ef8a98 to your computer and use it in GitHub Desktop.
Save smyrgl/e4922230d25d90ef8a98 to your computer and use it in GitHub Desktop.
Update ASTableView/CollectionView using TLIndexPathTools
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