Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ryan Nystrom: Refactoring at scale – Lessons learned rewriting Instagram’s feed

Realm
September 02, 2016

Ryan Nystrom: Refactoring at scale – Lessons learned rewriting Instagram’s feed

Ryan is a lead iOS engineer at Instagram working on app infrastructure in New York City. He is an avid open source advocate and contributor at Facebook on projects like AsyncDisplayKit. Ryan is also an author and presenter with RayWenderlich.com publishing work on the Apple Watch, 3D Touch, and Reactive Cocoa.

Abstract:
When apps grow, there eventually comes a time where you hear the dreaded word “refactor”. For established apps, refactors present interesting problems from the amount of code and teams involved. Not to mention performance and testing implications on a large application.

But no matter the size, there are common problems that any app runs into when taking on a big refactor. Things like massive view controllers, dependencies, and managing goals and priorities.

Come learn about how and why the Instagram team took on rewriting their iOS feed from the bottom up, and see what it takes to ship a successful refactor.

Twitter: https://twitter.com/_ryannystrom

Realm

September 02, 2016
Tweet

More Decks by Realm

Other Decks in Technology

Transcript

  1. 1 cat 3 4 7 8 9 10 1 2

    5 3 4 6 7 8 From To
  2. 2 6 2 6 1 5 3 4 7 8

    1 5 3 4 7 8 1 cat 3 4 7 8 9 10 Delete 2, 6
  3. cat 5 1 2 3 4 6 7 8 1

    3 4 7 8 1 cat 3 4 7 8 9 10 Reload 5
  4. 1 2 5 3 4 6 7 8 1 cat

    3 4 7 8 1 cat 3 4 7 8 9 10 Move
  5. 1 2 5 3 4 6 7 8 Insert 9,

    10 1 cat 3 4 7 8 9 10 9 10
  6. Diffing using Least Common Subsequence Finds all deletes, reloads, inserts,

    moves O(n) complexity Image source: The Internet
  7. Feed Item “Item Controller” • Number of items • Configure

    cells • Cell size • Interaction • Business logic
  8. +

  9. • Subclass UIViewController • Conform to UICollectionViewDataSource • Register cells

    • Return number of sections • Return number of items • Return cells • Return cell size • Insert select items • Delete others • Try to move… CREATING A UICollectionView FEED
  10. • Subclass UIViewController • Conform to UICollectionViewDataSource • Register cells

    • Return number of sections • Return number of items • Return cells • Return cell size • Insert select items • Delete others • Try to move… • Just reloadData CREATING A UICollectionView FEED
  11. class LabelItemController: IGListItemController, IGListItemType { var item: String? func numberOfItems()

    -> UInt { return 1 } func sizeForItemAtIndex(index: Int) -> CGSize { return CGSize(width: collectionContext!.containerSize.width, height: 55) } func cellForItemAtIndex(index: Int) -> UICollectionViewCell { let cell = collectionContext!.dequeReusableCellOfClass(LabelCell.self, forItemController: self, atIndex: index) as! LabelCell cell.label.text = item return cell } func didUpdateToItem(item: AnyObject) { self.item = item as? String } func didSelectItemAtIndex(index: Int) {} func didDeselectItemAtIndex(index: Int) {} }
  12. func cellForItemAtIndex(index: Int) -> UICollectionViewCell { let cell = collectionContext!.dequeReusableCellOfClass(LabelCell.self,

    forItemController: self, atIndex: index) as! LabelCell cell.label.text = item return cell }
  13. class LabelItemController: IGListItemController, IGListItemType { var item: String? func numberOfItems()

    -> UInt { return 1 } func sizeForItemAtIndex(index: Int) -> CGSize { return CGSize(width: collectionContext!.containerSize.width, height: 55) } func cellForItemAtIndex(index: Int) -> UICollectionViewCell { let cell = collectionContext!.dequeReusableCellOfClass(LabelCell.self, forItemController: self, atIndex: index) as! LabelCell cell.label.text = item return cell } func didUpdateToItem(item: AnyObject) { self.item = item as? String } func didSelectItemAtIndex(index: Int) {} func didDeselectItemAtIndex(index: Int) {} }
  14. //MARK: IGListAdapterDataSource func itemsForListAdapter(listAdapter: IGListAdapter) -> [IGListDiffable] { return [

    "Foo", "Bar", "Baz" ] } func listAdapter(listAdapter: IGListAdapter, itemControllerForItem item: AnyObject) -> IGListItemController { return LabelItemController() }
  15. func listAdapter(listAdapter: IGListAdapter, itemControllerForItem item: AnyObject) -> IGListItemController { if

    item === spinToken { return SpinnerItemController() } else { return LabelItemController() } }
  16. let words = [ “Foo", “Bar", “Baz" ] func itemsForListAdapter(listAdapter:

    IGListAdapter) -> [IGListDiffable] { return words.filter { word in return word.containsString(filterString) } }
  17. • Feed with multiple data types • Fast, crash-free, animated

    updates • Reusable components • Never performBatchUpdates or reloadData WHY SHOULD I USE IGLISTKIT?
  18. IGListKit Supplementary views Display events Working ranges Unit tested Navigation

    support Visible item controllers Stacked item controller Single-cell item controller Empty background view iOS 8+ Custom layouts Experiments Extendable Examples Tutorials