5

guys Im totally new in Rxswift, is there is a way to do this scenario in RxSwift ?

What I got is this.. but problem is i dont have indexPath

datasource.sectionModels
        .asObservable()
        .bindTo(tableView.rx.items) { tableView, row, element in
            guard let sectionType = SectionType(rawValue: indexPath.section) else { return 0 }

            let indexPath = IndexPath(row: row, section: 0)

            var itemForIndexPath: SectionViewModel {
                return self.datasource.sectionModels.value[indexPath.section]
            }

            switch sectionType {
            case .nickTitle, .nickIfno:
                let infoCell = tableView.dequeueReusableCell(
                    withIdentifier: InfoTableViewCell.name,
                    for: indexPath
                    ) as! InfoTableViewCell

                var datasource: InfoCellDatasourceProtocol = InfoCellNormalState(text: itemForIndexPath.text)
                if itemForIndexPath.errorStyle {
                    datasource = InfoCellErrorState(text: itemForIndexPath.text)
                }

                infoCell.configureCell(datasource: datasource)
            }

This is what I need in RxSwift

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let sectionType = SectionType(rawValue: indexPath.section) else { return UITableViewCell() }

    var itemForIndexPath: SectionViewModel {
        return self.datasource.sectionModels.value[indexPath.section]
    }

    switch sectionType {
    case .nickTitle, .nickInfo:
        let infoCell = tableView.dequeueReusableCell(
            withIdentifier: InfoTableViewCell.name,
            for: indexPath
            ) as! InfoTableViewCell

        var datasource: InfoCellDatasourceProtocol = InfoCellNormalState(text: itemForIndexPath.text)
        if itemForIndexPath.errorStyle {
            datasource = InfoCellErrorState(text: itemForIndexPath.text)
        }

        infoCell.configureCell(datasource: datasource)

        return infoCell

datasource snippet:

open class RegistrationNickDataSource: NickDatasourceProtocol {
    public var error: Variable<ErrorType>?
    public var success: Variable<Bool> = Variable(false)
    fileprivate let request = ValidateNameRequest()


    public var nickHints: Variable<[String]>?
    public var sectionModels: Variable<[SectionViewModel]> =  Variable([
    SectionViewModel(
        text: "your_nick_hint".localized,
        type: .info,
        errorStyle: false
    ),
    SectionViewModel(
        text: "your_nick_placeholder".localized,
        type: .input,
        errorStyle: false
    ),
    SectionViewModel(
        text: "your_nick_info".localized,
        type: .info,
        errorStyle: false
    )]
)

Thanks for every help

Denis Kakačka
  • 697
  • 1
  • 8
  • 21

2 Answers2

5

Here is an example from the repo to make a sectioned table view:

https://github.com/ReactiveX/RxSwift/blob/master/RxExample/RxExample/Examples/SimpleTableViewExampleSectioned/SimpleTableViewExampleSectionedViewController.swift

The upshot of it is that you have to instantiate a RxTableViewSectionedReloadDataSource.

However, I don't see in your code where you actually have sections. Sections in a UITableView imply a 2 dimensional array and you only have a 1 dimensional array...

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
4

I suggest using RxDataSources. It will give you access to a cell's index path when you call configureCell.

Your data source and cell will be configured using something like:

func setupDataSource() 
{
    let dataSource = RxTableViewSectionedReloadDataSource<MySection>()

    dataSource.configureCell = { (theDataSource: TableViewSectionedDataSource<MySection>,                     
                                  theTableView,         
                                  theIndexPath,                                  
                                  item: MyItem) in
        let cell = theTableView.dequeueReusableCell(withIdentifier: InfoTableViewCell.name,
                                                    for: theIndexPath) as! InfoTableViewCell

        /* Do any setup of the cell here. */

        return cell;
    }       

    dataSource.titleForHeaderInSection = { theDataSource, index in
        return theDataSource.sectionModels[index].header;
    }
}

You can leave out the types in the closure parameters if you have problems getting the types to be accepted. Swift can infer them for you.


Setting up structs for your custom data source including your section models would look something like the following:

/// Holds data to display.
struct MyItem
{
    var text: String
    var type: MyCustomEnum
    var errorStyle: Bool
}

/// Defines a section type for use with sections for the table.
struct MySection
{
    var header: String
    var items: [Item]
}

/// Tie your custom data model to SectionModelType.
extension MySection: SectionModelType
{
    typealias Item = MyItem

    init(original: MySection,
         items: [Item])
    {
        self = original
        self.items = items
    }
}

The penultimate step is to bind the data source by placing your sections into an Observable. The following code is an example where the function sections() returns an Observable of type Observable<[MySection]>:

sections()
    .bind(to: tableView.rx.items(dataSource: dataSource))
    .disposed(by: bag)

Finally, getting the data to appear can be done using:

 override func viewDidLoad()
 {
     super.viewDidLoad()
     setupDataSource()
 }
Daniel Zhang
  • 5,778
  • 2
  • 23
  • 28