IGListKit を使ってみた!

IOS

こんにちわ、モバイルソリューション部の馬場です。


今回は Instagram がオープンソースで公開している IGListKit を使ってみました。


IGListKit は UICollectionView のライブラリです。


Instagram app のフィードをリファクタリングした際に得た知見をオープンソースで公開したもののようです。


大きな TableView や CollectionView の更新や削除でパフォーマンスの問題となる reloadData の問題などが改善されており、シンプルで見通しの良いコードが書けるようになるそうです。


IGListKit を実装する上で必要になるクラスは次の3つが必要です。



  • Model

  • Section Controller

  • View Controller


では、順を追って説明していきます。


Model の作成


まず、Collection View を表示させるデータとしてモデルクラスを作る必要があります。こんな感じです。


import UIKit
import IGListKit

final class User: NSObject {

let id: Int
let name: String

init(id: Int, name: String) {
self.id = id
self.name = name
}
}

// MARK: - IGListDiffable
extension NSObject: IGListDiffable {

public func diffIdentifier() -> NSObjectProtocol {
return self
}

public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
return isEqual(object)
}

}

IGListDiffable に準拠した専用のモデルクラスを作成する必要があります。


2つのオブジェクト比較するためのコードを追加します。


Section Controller


次に、データソースを提供する部分の Section Controller を作成します。


こんな感じです。


import IGListKit

class SectionController: IGListSectionController {
var user: User?
}

最初に、IGListSectionController を継承したクラスを作ります。


次に、Section Controller に IGListSectionType を準拠させます。


こんな感じです。


// MARK: - IGListSectionType
extension SectionController: IGListSectionType {
func numberOfItems() -> Int {
return 1
}

func sizeForItem(at index: Int) -> CGSize {
guard let context = collectionContext else { return .zero }
return CGSize(width: context.containerSize.width, height: 44)
}

func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext!.dequeueReusableCellFromStoryboard(withIdentifier: "cell", for: self, at: index) as! CollectionViewCell
cell.textLabel.text = user?.name
return cell
}

func didUpdate(to item: Any) {
self.user = item as? User
}

func didSelectItem(at index: Int) {}
}

IGListSectionType を準拠する際に、必要メソッドを実装します。


numberOfItemsやsizeForItem、cellForItemといったデータソースを組み立てる上で必要なメソッドが定義されています。


上の例では、Storyboard 上に作成した Cell を読み込みUserデータを設定しています。


View Controller


最後に View Controller の実装です。


import IGListKit
class ViewController: UIViewController {

// MARK: - IBOutlets
@IBOutlet weak var collectionView: IGListCollectionView!

// MARK: - Properties
lazy var adapter: IGListAdapter = {
return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 0)
}()

lazy var users = [
User(id: 1, name: "Littlefinger"),
User(id: 2, name: "Tommen Baratheon"),
User(id: 3, name: "Roose Bolton")
]

// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
adapter.collectionView = collectionView
adapter.dataSource = self
}
}

まず、IGListAdapter 定義して IGListCollectionView と View Controller を関連付けさせます。


次に、IGListAdapterDataSource を準拠させて、Section Controller と View Controller を関連付けさせます。


こんな感じです。


// MARK: - IGListAdapterDataSource
extension ViewController: IGListAdapterDataSource {

func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
return users
}

func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
let sectionController = SectionController()
return sectionController
}

func emptyView(for listAdapter: IGListAdapter) -> UIView? { return nil }

}

以上で IGListKit を実行させることができます。


所感としては、reloadData 問題を解決できる点と複雑なセクションEnumを使った Cell の管理ができるようになる利点が素晴らしいので、今後積極的に使っていきたいと思います。

この記事へのコメント