Protocol Extension で UICollectionView をちょっと便利に

UICollectionView を使う際、以下のようにセルを再利用していました

// CustomCell.swift

class CustomCell: UICollectionViewCell {
    static func nib() -> UINib {
        return UINib(nibName: String(describing: self), bundle: nil)
    }

    static func reuseIdentifier() -> String {
        return String(describing: self)
    }
}

// CustomViewController.swift
...

// セルを登録
collectionView.register(
    CustomCell.nib(), 
    forCellWithReuseIdentifier: CustomCell.reuseIdentifier()
)

...

// 登録したセルを取り出す
collectionView.dequeueReusableCell(
    withReuseIdentifier: CustomCell.reuseIdentifier(), 
    for: indexPath
)

この .nib .reuseIdentifier をカスタムセルごとに書くのが面倒になったので、今回は protocol extension を使って解消しようと思います

(UICollectionView については以下)
https://developer.apple.com/documentation/uikit/uicollectionview

Protocol Extension

Protocols — The Swift Programming Language (Swift 5.1)

protocol を拡張して実装し、適合した型に提供することができます

拡張してコードを共通化するなら通常の extension でも良いのですが、こちらはより柔軟で再利用性が高いです

先程の .nib .reuseIdentifier メソッドを protocol extension で実装します

// 1. protocol を定義
protocol ReusableProtocol {
    static func nib() -> UINib
    static func reuseIdentifier() -> String
}

// 2. protocol を実装
extension ReusableProtocol {
    static func nib() -> UINib {
        return UINib(nibName: String(describing: self), bundle: nil)
    }

    static func reuseIdentifier() -> String {
        return String(describing: self)
    }
}

// 3. UICollectionView に適合
extension UICollectionViewCell: ReusableProtocol {}

これで、 UICollectionViewCell のサブクラスで .nib .reuseIdentifier` メソッドが使えるようになりました

通常の extension と比較して

再利用性が高い

protocol なので、他のクラスに適合させるだけで再利用できます
UITableViewCell で再利用したい...場合でも、コードは以下の1行

extension UITableViewCell: ReusableProtocol {}

対象を限定できる

protocol を実装する際に where を使うことで、対象を絞ることができます
UICollectionViewCell でのみ使いたい際は以下のようにする

extension ReusableProtocol where Self: UICollectionViewCell {
    static func nib() -> UINib {
        return UINib(nibName: String(describing: self), bundle: nil)
    }

    static func reuseIdentifier() -> String {
        return String(describing: self)
    }
}

そのほか

デメリットとしては、そのまま objc から使えない点でしょうか...(objc 側で定義してあげれば使える?)

extension をうまく使えば UICollectionView の .register / .dequeue も簡略化できそうです
名前空間とやりすぎに注意しつつ使おうと思います