intuit / CardParts

A reactive, card-based UI framework built on UIKit for iOS developers.
Other
2.52k stars 224 forks source link

CardPartCollectionView binding datasource throws error 'Generic parameter 'Self' could not be inferred' #65

Closed briannadoubt closed 6 years ago

briannadoubt commented 6 years ago

I am running into an issue binding my CardPartCollectionView datasource.

First I set up my model

struct CollectionViewModel {
    var title: String?
    var image: Storefront.Image?
    var description: String?
    var handle: String?
    var products = Variable([Storefront.Product]())
}

struct SectionOfCollection {
    var header: String
    var items: [Item]
}

extension SectionOfCollection: SectionModelType {
    init(original: SectionOfCollection, items: [Item]) {
        self = original
        self.items = items
    }

    typealias Item = Storefront.Product
}

Then I set up my CardPartsFullScreenViewController with an instance of the model, the collectionView CardPart, the layout for the collectionView, then I setup the collectionView with all these components.

class CollectionCardController: CardPartsFullScreenViewController {

    let model = CollectionViewModel()
    lazy var collectionViewCardPart = CardPartCollectionView(collectionViewLayout: layout)

    lazy var layout: UICollectionViewFlowLayout = {
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 10
        layout.minimumLineSpacing = 10
        layout.scrollDirection = .vertical
        layout.itemSize = CGSize(width: width, height: height)
        return layout
    }()
    lazy var width: CGFloat = {
        return self.view.frame.width / 2 - 20 - 20
    }()
    lazy var height = self.width + 88

    override func viewDidLoad() {
        super.viewDidLoad()

        setupCollectionView()
        collectionViewCardPart.collectionView.register(ProductCollectionViewCell.self, forCellWithReuseIdentifier: "ProductCell")
    }

    func setupCollectionView() {

        let dataSource = RxCollectionViewSectionedReloadDataSource<SectionOfCollection>(configureCell: {[weak self] (_, collectionView, indexPath, product) -> UICollectionViewCell in

            print(self?.model.title as Any)

            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductCell", for: indexPath) as? ProductCollectionViewCell else {
                return UICollectionViewCell()
            }

            cell.set(product)

            return cell
        })

        let section = [ SectionOfCollection(header: "Products", items: model.products.value) ]

        // This is where the error appears: "Generic parameter 'Self' could not be inferred"
        model.products.asObservable().bind(to: collectionViewCardPart.collectionView.rx.items(dataSource: dataSource)).disposed(by: bag)
    }
}

I have been able to get my data from the server and map all the ui components to the cards that I'm using as cells, but the datasource hasn't been set correctly due to this error and nothing ever appears!

I did find something on this error based in RXDataSources and I was able to get the error to go away and compile with this solution but it did not bind the datasource so data still does not show.

This one also has some logical explanations but also does not fix the issue either.

Any help is appreciated!

lwdupont commented 6 years ago

Well @croossin is a far better RxSwift person than I am, but guessing I wonder if you need to add self to collectionViewCardPart? Or maybe RxSwift doesn't like the data source being a local variable?

bharathmurs commented 6 years ago

Hi @bornbrie, Firstly thanks for trying CardParts 👍 If i understand the issue correctly, the closure for the datasource is not being called correct? My first hunch with Rx experience is that since dataSource is a local variable to setupCollectionView it might be getting de-allocated and hence wont be getting called back. try defining dataSource as a property under CollectionCardController.

briannadoubt commented 6 years ago

Thank you sincerely for your responses!

It does make sense that the dataSource would get deallocated inside a method like that. I did as suggested and refactored the dataSource to a local variable, but still no dice.

Also, adding self. to collectionViewCardPart.collectionView.rx.items(dataSource: dataSource) didn't make a difference either. Mind you this is a swift compiler error and prevents the project from building. Cleaning and re-building/dumping Derived Data didn't change anything either, for what it's worth.

screen shot 2018-07-04 at 10 08 35 am
bharathmurs commented 6 years ago

@bornbrie Ah, a compiler error.! I see the problem in your CollectionViewModel model definition while defining products. You have var products = Variable([Storefront.Product]()) and it has to be var products = Variable([SectionOfCollection]())

and while creating section object

let section = [ SectionOfCollection(header: "Products", items: model.products.value[section].items) ]

Hope this helps.

bharathmurs commented 6 years ago

The error says Self could not be inferred. Because your dataSource was defined as RxCollectionViewSectionedReloadDataSource<SectionOfCollection> but observing to products which was defined as Storefront.Product, hence the mismatch and the compiler was not able to infer the type passed.

croossin commented 6 years ago

@bornbrie Did @bharathmurs answer help solve your issue?

croossin commented 6 years ago

@bornbrie I will be closing this but feel free to open it back up if this didn't solve your issue!