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. REBUILDING
    Ryan Nystrom @_ryannystrom

    View Slide

  2. TECH DEBT
    Image source: Reddit user u/Pracy_

    View Slide

  3. View Slide

  4. View Slide

  5. Cells
    Header
    0
    1
    2
    3
    4
    5
    6

    View Slide

  6. Feed Item

    View Slide

  7. View Slide


  8. +

    View Slide

  9. Feed Item Video User

    View Slide

  10. Feed Item
    Video User

    View Slide

  11. Feed Item

    View Slide

  12. Feed Item

    View Slide

  13. Feed Item

    View Slide

  14. Feed Item

    View Slide

  15. Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  16. Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  17. Feed
    Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  18. Feed
    Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  19. Collections Networking Main Feed
    Feed
    Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  20. M
    V
    C

    View Slide

  21. Massive
    View
    Controller

    View Slide

  22. What’s the worst that can happen?

    View Slide

  23. View Slide

  24. FEED 2.0
    Image source: Reddit user u/KetoEater

    View Slide

  25. 1 2 5
    3 4 6 7 8
    From

    View Slide

  26. 1 cat
    3 4 7 8
    9 10
    1 2 5
    3 4 6 7 8
    From
    To

    View Slide

  27. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. Diffing using Least Common Subsequence
    Finds all deletes, reloads, inserts, moves

    O(n) complexity
    Image source: The Internet

    View Slide

  32. Collections Networking Main Feed
    Feed
    Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  33. Feed
    Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  34. Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item
    “The World”

    View Slide

  35. Feed Item
    Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  36. Feed Item
    “Item Controller”

    View Slide

  37. Feed Item
    “Item Controller”
    • Number of items
    • Configure cells
    • Cell size
    • Interaction
    • Business logic

    View Slide

  38. Feed Item
    Feed Item
    Feed Item
    Feed Item

    View Slide

  39. Feed Item
    Feed Item
    Feed Item
    Feed Item
    Any Object

    View Slide

  40. +

    View Slide

  41. View Slide

  42. Image source: Reddit user u/orbojunglist
    OPEN SOURCE

    View Slide

  43. • 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

    View Slide

  44. • 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

    View Slide

  45. What can we give back?

    View Slide

  46. IGListKit

    View Slide

  47. IGItemController

    View Slide

  48. 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) {}
    }

    View Slide

  49. class LabelItemController: IGListItemController, IGListItemType {
    ...
    }

    View Slide

  50. func numberOfItems() -> UInt {
    return 1
    }

    View Slide

  51. func sizeForItemAtIndex(index: Int) -> CGSize {
    return CGSize(width: collectionContext!.containerSize.width,
    height: 55)
    }

    View Slide

  52. var item: String?
    func didUpdateToItem(item: AnyObject) {
    self.item = item as? String
    }

    View Slide

  53. func cellForItemAtIndex(index: Int) -> UICollectionViewCell {
    let cell = collectionContext!.dequeReusableCellOfClass(LabelCell.self,
    forItemController: self,
    atIndex: index) as! LabelCell
    cell.label.text = item
    return cell
    }

    View Slide

  54. 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) {}
    }

    View Slide

  55. IGListAdapter

    View Slide

  56. //MARK: IGListAdapterDataSource
    func itemsForListAdapter(listAdapter: IGListAdapter) -> [IGListDiffable] {
    return [
    "Foo",
    "Bar",
    "Baz"
    ]
    }
    func listAdapter(listAdapter: IGListAdapter, itemControllerForItem item: AnyObject) -> IGListItemController {
    return LabelItemController()
    }

    View Slide

  57. func itemsForListAdapter(listAdapter: IGListAdapter) -> [IGListDiffable] {
    return [
    "Foo",
    "Bar",
    "Baz"
    ]
    }

    View Slide

  58. func listAdapter(listAdapter: IGListAdapter,
    itemControllerForItem item: AnyObject) -> IGListItemController {
    return LabelItemController()
    }

    View Slide

  59. View Slide

  60. let spinToken = NSObject()
    func itemsForListAdapter(listAdapter: IGListAdapter) -> [IGListDiffable] {
    return [
    "Foo",
    “Bar",
    spinToken,
    “Baz”
    ]
    }

    View Slide

  61. func listAdapter(listAdapter: IGListAdapter,
    itemControllerForItem item: AnyObject) -> IGListItemController {
    if item === spinToken {
    return SpinnerItemController()
    } else {
    return LabelItemController()
    }
    }

    View Slide

  62. View Slide

  63. func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
    filterString = text
    adapter.performUpdatesAnimated(true, completion: nil)
    }

    View Slide

  64. let words = [ “Foo", “Bar", “Baz" ]
    func itemsForListAdapter(listAdapter: IGListAdapter) -> [IGListDiffable] {
    return words.filter { word in
    return word.containsString(filterString)
    }
    }

    View Slide

  65. View Slide

  66. Why should I use IGListKit?

    View Slide

  67. • Feed with multiple data types
    • Fast, crash-free, animated updates
    • Reusable components
    • Never performBatchUpdates or reloadData
    WHY SHOULD I USE IGLISTKIT?

    View Slide

  68. 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

    View Slide

  69. 3.9M diffs since this talk began

    View Slide

  70. View Slide

  71. View Slide

  72. COMING THIS MONT
    github.com/instagram/IGListKit

    View Slide

  73. View Slide