user15102768
user15102768

Reputation:

UICollectionViewDiffableDataSource custom subclass initialization

So I am making a custom subclass of UICollectionViewDiffableDataSource.

My code looks like this :

class MyDiffableDataSource: UICollectionViewDiffableDataSource<String, String> {
    
    convenience required init(collectionView: UICollectionView) {
        self.init(collectionView: collectionView) { (collection, index, item) in
            if index.item % 2 == 0 {
                return self.firstCell(collection, item, index)
            } else {
                return self.secondCell(collection, item, index)
            }
        }
        self.supplementaryViewProvider = { (_, _, index) in
            return self.header(collectionView, index)
        }
    }
    
    func firstCell(_ collection: UICollectionView, _ item: String, _ index: IndexPath) -> UICollectionViewListCell {
        let cellReg = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, _, item) in
            var content = cell.defaultContentConfiguration()
            content.text = item
            cell.contentConfiguration = content
            cell.accessories = [.disclosureIndicator()]
        }
        return collection.dequeueConfiguredReusableCell(using: cellReg, for: index, item: item)
    }
    
    func secondCell(_ collection: UICollectionView, _ item: String, _ index: IndexPath) -> UICollectionViewListCell {
        let cellReg = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, _, item) in
            var content = cell.defaultContentConfiguration()
            content.text = item
            cell.contentConfiguration = content
            cell.accessories = [.checkmark()]
        }
        return collection.dequeueConfiguredReusableCell(using: cellReg, for: index, item: item)
    }
    
    func header(_ collection: UICollectionView, _ index: IndexPath) -> UICollectionReusableView {
        let headerReg = UICollectionView.SupplementaryRegistration
        <UICollectionReusableView>(elementKind: UICollectionView.elementKindSectionHeader) { (_, _, _) in }
        return collection.dequeueConfiguredReusableSupplementary(using: headerReg, for: index)
    }
}

But of course I get the 'self' used before 'self.init' call or assignment to 'self' error from Xcode.

How can I initialize MyDiffableDataSource only with a UICollectionView parameter without getting this error?

I know i can make the functions firstCell() and secondCell() static, or that i can put all the content of the two functions directly in the init. But that would not be great if i add more later and static would prevent me from accessing other non static properties.

Is there any other solutions that I can use?

Thanks!

Upvotes: 2

Views: 475

Answers (1)

Vlad Tretiak
Vlad Tretiak

Reputation: 1

Maybe it will be useful to someone. I developed a protocol-based solution that does not require static methods for this problem.

protocol CellProviderRegistry {
    associatedtype Cell: UICollectionViewCell
    associatedtype Item

    // Optional: Add the following line if you need dependency
    // associatedtype Dependency
    
    typealias CellRegistration = UICollectionView.CellRegistration<Cell, Item>
    
    func callAsFunction(with dependency: Dependency) -> CellRegistration
}

// Simple example
struct WidgetCellProvider: CellProviderRegistry {
    func callAsFunction(with dependency: DashboardViewModel) -> CellRegistration {
        CellRegistration { cell, indexPath, item in
           // Configure your cell
           cell.backgroundColor = UIColor.clear
        }
    }
}

final class DashboardDiffableDataSource: UICollectionViewDiffableDataSource<Section, Item> {
    private let cellRegistration = WidgetCellProvider()
    
    init(collectionView: UICollectionView, dependency: DashboardViewModel) {
        // Inside this block we call instance method `callAsFunction` while avoiding the static version
        let provider = cellRegistration(with: dependency)
        
        super.init(collectionView: collectionView) { collectionView, indexPath, item in
            collectionView.dequeueConfiguredReusableCell(
                using: provider,
                for: indexPath,
                item: item
            )
        }
    }
}

Upvotes: 0

Related Questions