Bhavik Modi
Bhavik Modi

Reputation: 1565

RxSwift: How to populate the data in collection view cell inside the table view using ViewModel?

I do have following structure:

- TableView
-- Custom Table View Cell
--- CollectionView
---- Custom CollectionView Cell

I want to understand that how can I pass the data from / using view model in this structure with RxSwift - MVVM Structure.

Whenever I do get response from API it should update the data in table view rows and associated collection view cell respectively.

Upvotes: 4

Views: 2956

Answers (2)

Daniel T.
Daniel T.

Reputation: 33967

The simplest solution is to use an array of arrays.

For example. Let's assume your API returns:

struct Group: Decodable {
    let items: [String]
}

Then your view model would be as simple as this:

func tableViewItems(source: Observable<[Group]>) -> Observable<[[String]]> {
    return source
        .map { $0.map { $0.items } }
}

When creating your cell, you can wrap the inner array into an observable with Observable.just() like this:

// in your view controller's viewDidLoad for example.
        tableViewItems(source: apiResponse)
            .bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: CollectionTableViewCell.self)) { _, element, cell in
                Observable.just(element)
                    .bind(to: cell.collectionView.rx.items(cellIdentifier: "Cell", cellType: UICollectionViewCell.self)) { _, element, cell in
                        let label = (cell.viewWithTag(6969) as? UILabel) ?? UILabel()
                        label.tag = 6969
                        label.text = element
                        label.sizeToFit()
                        cell.addSubview(label)
                }
                .disposed(by: cell.disposeBag)
        }
        .disposed(by: dispsoeBag)

Upvotes: 3

Nick
Nick

Reputation: 252

Here is an example I wrote just now to demonstrate how you can use RxSwift to do what you want. Important Note: This is a rough example, not optimally written and not tested! I just wrote it using a text editor hope it helps you out, if not I will try to polish it when I have some more time.

class MyViewModel {
    // Lets say TableData is your model for the tableView data and CollectionData for your collectionView
    public let tableData : PublishSubject<[TableData]> = PublishSubject()
    public let collectionData : PublishSubject<[CollectionData]> = PublishSubject()

    private let disposeBag = DisposeBag()

    func fetchData() {
        // Whenever you get an update from your API or whatever source you call .onNext
        // Lets assume you received an update and stored them on a variable called newShopsUpdate 
        self.tableData.onNext(newTableDataUpdate)
        self.collectionData.onNext(newCollectionDataDataUpdate)
    }
}

class MyViewController: UIViewController {
    var tableData: BehaviorRelay<[TableData]> = BehaviorRelay(value: [])
    var collectionData:  BehaviorRelay<[CollectionData]> = BehaviorRelay(value: [])
    let viewModel = MyViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Setup Rx Bindings
        viewModel
            .tableData
            .observeOn(MainScheduler.instance)
            .bind(to: self.tableData)
            .disposed(by: DisposeBag())
        viewModel
            .collectionData
            .observeOn(MainScheduler.instance)
            .bind(to: self.collectionData)
            .disposed(by: DisposeBag())

        // Register yours Cells first as usual
        // ...
        // Setting the datasource using RxSwift
        tableData.bind(to: tableView.rx.items(cellIdentifier: "yourCellIdentifier", cellType: costumeTableViewCell.self)) { row, tableData, cell in 
            // Set all the cell properties here
            // Lets also assume you have you collectionView inside one of the cells
            cell.tableData = tableData

            collectionData.bind(to: cell.collectionView.rx.items(cellIdentifier: "yourCellIdentifier", cellType: costumeCollectionViewCell.self)) { row, collectionData, cell in 
                // Set all the cell properties here
                cell.collectionData = collectionData
            }.disposeBag(by: DisposeBag())
        }.disposed(by: DisposeBag())
    }
}

Upvotes: 2

Related Questions