@attheodo

What?

  • Instagram's iOS app backbone
    • Very actively maintained
  • Data-driven framework on top of UICollectionView
    • But still UICollectionView at its core
  • Allows to easily create collections based on different data types.
    • Mimicking UITableViews is equally easy
  • Decoupled diffing algorithm

Diffing Algorithm

  • Given two "files", find all the differences
  • Algorithm is based on Paul Heckel's paper:
    • https://dl.acm.org/citation.cfm?id=359467
  • Finds inserts, updates, deletes in O(n)
  • Needs to be able to (uniquely) identify and compare
    • ​ListDiffable

Wait, but why?

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (7), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).' 

 

Wait, but why?

  • In relatively simple collections, tracking and managing inserts, updates and deletions is easy.
    • insertItemsAtIndexPaths:
    • updateItemsAtIndexPaths:
    • deleteItemsAtIndexPaths:
  • But... after a while and a bunch of crashes:
    • collectionView.reloadData()😅

ListDiffable

  • It's a protocol
  • Allows an object to be identified and compared (for equality)
    • func diffIdentifier() -> NSObjectProtocol
    • func isEqual(toDiffableObject object: ListDiffable?) -> Bool
  • IGListKit collection views are built based on an array of ListDiffable objects

ListDiffable

class Message {
    var messageId: String
    var content: String
    var senderName: String
    
    init(messageId: String, content: String, senderName: String) {
        self.messageId = messageId
        self.content = content
        self.senderName = senderName
    }
}

extension Message: ListDiffable {
    func diffIdentifier() -> NSObjectProtocol {
        return messageId as NSObjectProtocol
    }
    
    func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
        guard let object = object as? Message else { return false }
        return content == object.content && senderName == object.senderName
    }
}

IGListKit Architecture

[ListDiffable]
IGListAdapter
IGListSectionController
UICollectionViewCell

IGListAdapter

  • Well... it's an adapter
    • ​...Over UICollectionView/delegate/dataSource
    • ... by taking your objects..
    • ... and creating section controllers...
    • ... that serve as the delegate and datasource for these objects
  • Also carries the "context" of the UICollectionView
  • APIs for reloading stuff, getting objects and controllers for index paths, visible cells etc.

IGListAdapter

  • Well... it's an adapter
    • ​...Over UICollectionView/delegate/dataSource
    • ... by taking your objects..
    • ... and creating section controllers...
    • ... that serve as the delegate and datasource for these objects
  • Also carries the "context" of the UICollectionView
  • APIs for reloading stuff, getting objects and controllers for index paths, visible cells etc.

IGListAdapterDelegate

  • objectsForListAdapter:
  • listAdapter:sectionControllerForObject:
  • emptyViewForListAdapter:

IGListSectionController

  • numberOfItems
  • minimumLineSpacing
  • minimumInterimSpacing
  • sizeForItemAt:
  • cellForItemAt:
  • didUpdateToObject:
  • didSelectItemAt:
  • didDeselectItemAt:

Examples

Made with Slides.com