IGListKit 的演化:一起來看 Instagram 如何逐步解決 App 問題!


IGListKit + MVVM 是 Instagram 對於 iOS UICollectionView UI 與數據解耦的解決方案,IGListKit 的設計理念是以數據驅動,來解決不同 Team 之間的需求,包含不同的數據與不一樣的 Layout。

有關於 Code 說明與範例,我建議你搭配原始碼參考閱讀 IGListKitArchetype

原始 UICollectionView 再用戶日益增長的 Instagram 有甚麼問題

Instagram 是一款照片與影片的社交平台,目前月活躍使用者已超過 10 億。隨著 Instagram 的成長,因為越來越多的業務性質,Instagram 也需要更多更複雜的 Cell Layout。

好的架構與解決方案通常都不是一開始就做好的,而是隨著產品的成長與用戶的大規模提升,架構逐漸演進而成的。

原生 UICollectionView 的理念

  1. 可高度客製化
  2. 商業邏輯與 UI Code 解耦合
  3. Cell Reuse 資源可重複利用(我們知道創建 View 的開銷非常大)

下列是模仿 ig 首頁貼文形式的 Layout 畫面:

instagram-layout

我們可以看到畫面中紅框代表 Cell 元件,目前為止我們有兩種 Cell ,上面負責顯示使用者訊息,下面負責顯示貼文。

讓我們來撰寫程式碼吧!首先,要決定有多少個 Section

一個 Section 先設定回應一個 item

回應對應的 UICollectionViewCell

  • UserInfoViewCell:負責顯示用戶大頭貼、名稱、與更多(對應 View Model – PostViewModel)
  • UserImageViewCell:負責顯示用戶貼文圖片(對應 View Model – PostImageViewModel)
備註:cell.updateWith 是自定義的一個協定,用來更新與綁定資料。

然後,我們要依照不同的 Cell 設定不同的高度:

當資料是用戶資訊時,我們設定高度為 50;而當資料是貼文照片時,我們設定高度為 400。

當然,完整原始碼位於最上發方說明的位置可以參考 CollectionNormalController 這是一般 CollectionView 的實現過程。

接下來讓我們情境模擬一下,因業務需求我們需要增加下列推薦關注的元件

layout-2

那我們需要增加哪些 Code 呢?

  • UserFocusViewCell:負責顯示推薦關注(對應 View Model – FocusViewModel)

讓我們增加判斷回應的 Cell:

並修改應返回的高度:

看起來修改的幅度並不多,但是這裡出了幾個問題!

layout-3
  1. 多人合作時,大家需要在同樣的 Function 反覆添加與修改邏輯
  2. Controller 越來越臃腫
  3. 因業務需求新增 UI 需要修改多個團隊的代碼,職責分權不明確
  4. 可讀性及可維護性下降
  5. 業務邏輯與設計需求耦合
  6. 難以增加 A/B Test 代碼

因這些業務與成長需求,ig 不得不另外尋找解決方案,而 IGListKit 就是透過這些需求演化而成的。

IGListKit 的理念

  1. 增加一層 SectionController,拆分商業邏輯與 UI
  2. 提升 Code 的可重用度
  3. 高性能更新畫面機制 (O(n))

IGListKit 主要提供了這些功能:

  1. 不需要一次次調用 performBatchUpdates(_:, completion:)reloadData()
  2. 具有可重複使用的 CellComponents
  3. 創建具有多種數據類型的集合
  4. 自定義模型的差異行為
  5. 只依賴 UICollectionView
  6. 可擴充的 API
  7. 使用 Objective-C 編寫,並完整支援 Swift

IGListKit 簡單範例使用

為了表示 IGListKit 擴充性與可重用性,我們沿用上面製作好的 UIViewModel

  • CollectionIGListKitController 為 IGListKit 使用方式
  • UserInfoViewCell:負責顯示用戶大頭貼、名稱、與更多(對應 View Model – PostViewModel)
  • UserImageViewCell:負責顯示用戶貼文圖片(對應 View Model – PostImageViewModel)
  • UserFocusViewCell:負責顯示推薦關注(對應 View Model – FocusViewModel)
  • PostData:模擬資料來源

ViewModel 的實作

要使用 IGListKit,我們的 ViewModel 必須遵守 ListDiffable 協定。我們先了解一下這個協定吧!ListDiffable 必須實現兩個 function:

  1. func diffIdentifier() -> NSObjectProtocol:用於定義辨識項目
  2. func isEqual(toDiffableObject object: ListDiffable?) -> Bool:用於辨識兩者是否為同一個 Model

現在,我們可以開始實作了!首先,建立一個專屬的辨識協定 PostPageProtocol

這個協定很簡單,遵守 ListDiffable,並且規定必須實作 identifier 用於資源比較。然後,在我們的 PostViewModel 中加入 headerImageheaderTitle、和 headerRightButtonTitle,用於顯示用戶資訊。

再製作兩個 ViewModel PostImageViewModelPostImageViewModel,用於貼文照片與推薦關注。其中 PostImageViewModel 只加入屬性 mainImage,用於顯示照片;而 FocusViewModel 就加入屬性 headerImageheaderTitle、和 headerRightButtonTitle,用於顯示推薦關注的資訊。

Controller 製作

接下來,我們來製作 Controller 吧!我們先以下列程式碼建立一個 UIViewController注意不是 UICollectionViewController

我們首先生成一個 UICollectionView,再生成 ListAdapterUpdaterListAdapterListAdapterUpdater 負責 rowsection 的更新,而 ListAdapter 負責控制 CollectionView。

ListAdapterDataSource 的實作

因為我們的 adapter.dataSource 是指定 CollectionIGListKitController,所以必須實作 ListAdapterDataSource

返回合適的 ListSectionController,就如同一開始我們返回適合的 Cell 一樣。

所以,我們必須實作這三個 SectionController。這個步驟並不複雜,首先先定義了他們本身需要的 ModelsizeForItem 定義了這個 Cell 需要的大小,而 cellForItem 則定義了要返回哪一個 UICollectionViewCell

備註:由於 UICollectionViewCell 是重用的,這邊不再贅述,請參考原始碼即可。

此時,運行後應該會有一樣的畫面:

instagram-final

我們使用 IGListKit 後解決了甚麼問題

iglistkit

其實就是一開始 ig 團隊遇到的問題:

  1. 不同業務團隊只需負責自己的業務邏輯;
  2. 商業邏輯分離至 ListSectionController,解決越來越臃腫的 Controller,細分成許多 Child Controller。

總結

隨著業務與用戶規模的成長,App 一定會遇到許多複雜問題,而我們一定要透過架構的演進來解決新的問題。能跟著產品成長的團隊,才是好團隊!今天的範例說明了 IG 團隊在業務增長上所誕生的 IGListKit

IGListKit 使很多團隊可以專注在自己業務邏輯面的開發,並且讓職責更加明確,大大提高可擴展性、易讀性、與易維護性,同時也為 A/B Test 做好準備。

好的架構並不是一蹴而就,更多的是演化打磨與取捨。架構如此,人的成長也如此。感謝你的閱讀,亦強烈建議你搭配範例閱讀 IGListKitArchetype

yasuoyuhao,自認為終身學習者,對多領域都有濃厚興趣,喜歡探討各種事物。目前專職軟體開發,系統架構設計,企業解決方案。最喜歡 iOS with Swift。

此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This