Kashif Jilani
Kashif Jilani

Reputation: 1257

iOS: How to delete cell in tableview using RxSwift

I have just started working with RxSwift and facing some challenges. I have created tableview with multiple section and able to tap and get details. However if I try to delete any particular cell, its not working. I am not sure what I am doing wrong in RxSwift. Below is my code.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, User>>(
      configureCell: { (_, tv, indexPath, element) in
        let cell = tv.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = string
        cell.textLabel?.numberOfLines = 0
        return cell
      },
      titleForHeaderInSection: { dataSource, sectionIndex in
        return dataSource[sectionIndex].model
      }
    )
    dataSource.canEditRowAtIndexPath = { dataSource, indexPath  in
      return true
    }

    viewModel.getUsers()
      .bind(to: tableView.rx.items(dataSource: dataSource))
      .disposed(by: disposeBag)

    tableView.rx
      .itemSelected
      .map { indexPath in
        return (indexPath, dataSource[indexPath])
      }
      .subscribe(onNext: { pair in
        print("Tapped \(pair.1) @ \(pair.0)")
      })
      .disposed(by: disposeBag)


    tableView.rx.itemDeleted
      .subscribe{
        print($0)
      }
      .disposed(by: disposeBag)

    tableView.rx
      .setDelegate(self)
      .disposed(by: disposeBag)
  }

Upvotes: 3

Views: 6816

Answers (1)

E-Riddie
E-Riddie

Reputation: 14780

Issue

tableView.rx.itemDeleted triggers an event containing the indexPath, where the delete action has happened. The change on your data should be handled by you. You are not getting any update, because you are not changing anything, you are just printing the indexPath out.

Solution

Since you are using a viewModel.getUsers() which returns you an Observable<[SectionModel<String, User>]> judging from your code. You also should introduce a method on the viewModel which will be used to remove an item at a specific indexPath.

In order to achieve that, you need to have a storage of your elements in a BehaviorSubject. This will hold the current value of the data, and when updated it will emit the new data to the ones which subscribed to it.

let sectionListSubject = BehaviorSubject(value: [SectionModel<String, User>]())

When you initialize your viewModel, you need to populate this subject with data doing so:

sectionListSubject.onNext([
            SectionModel(model: "First section", items: [
                User(),
                User(),
                User()
                ]),
            SectionModel(model: "Second section", items: [
                User(),
                User(),
                User()
                ])
            ])

Then your getUsers() method should look like:

func getUsers() -> Observable<[SectionModel<String, User>]> {
    return sectionListSubject.asObservable()
}

Last step on your viewModel, would be to implement the removeItem(at:)

func removeItem(at indexPath: IndexPath) {
    guard var sections = try? sectionListSubject.value() else { return }

    // Get the current section from the indexPath
    var currentSection = sections[indexPath.section]

    // Remove the item from the section at the specified indexPath
    currentSection.items.remove(at: indexPath.row)

    // Update the section on section list
    sections[indexPath.section] = currentSection

    // Inform your subject with the new changes
    sectionListSubject.onNext(sections)
}

Now on the codebase you have you just need to change:

tableView.rx.itemDeleted
    .subscribe(onNext: { self.viewModel.removeItem(at: $0) })
    .disposed(by: disposeBag)

Deleting should now work.

Upvotes: 7

Related Questions