4

I think it's better if I explain what I'm trying to achieve because I think the error is on my misunderstanding on how Observables work.

I have a UIViewController that contains a UITableView I'm also using RxSwift and RxDataSources, so I'm binding my tableView items like this:

vm.model
.debug()
.drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)

Where vm is a viewModel which contains:

self.model = self.network.provider.getMarkets()
                .map { (markets: [Market]) -> [Row] in
                    var rows = [Row]()
                    for market in markets {                        
                        rows.append(.market(market: market))
                    }
                    return rows
                }
                .map { (rows: [Row]) -> [Model] in
                    return [Model(section: .market, items: rows)]
                }
                .shareReplay(1)
                .asDriver(onErrorJustReturn: [])

Where model is:

var model: Driver<[Model]>

This all works great the first time, the tableview displays the items, but the print from the debug():

2017-04-28 20:07:21.382: MarketAndLanguageSelectionViewController.swift:36 (viewDidLoad()) -> subscribed
2017-04-28 20:07:22.287: MarketAndLanguageSelectionViewController.swift:36 (viewDidLoad()) -> Event next(*Multiple items*)
2017-04-28 20:07:22.289: MarketAndLanguageSelectionViewController.swift:36 (viewDidLoad()) -> Event completed
2017-04-28 20:07:22.289: MarketAndLanguageSelectionViewController.swift:36 (viewDidLoad()) -> isDisposed

The problem is I didn't want the datasource to dispose because I wan't to update it based on the user action. If the user clicks a tableViewCell I want to update the model. Any ideas on how can I achieve this?

Sorry for such a big question.

Diogo Antunes
  • 2,241
  • 1
  • 22
  • 37

1 Answers1

2

I'm guessing that network.provider.getMarkets() makes a network call which returns a single result and completes.

Now, getMarkets() is the source, and tableView.rx.items is the sink. Once the source completes, the chain is broken.

It sounds like what you want to do is create a new getMarkets Observable every time the user taps something, as well as calling getMarkets once for free. I would expect something like:

let markets = trigger.flatMap { 
        self.network.provider.getMarkets()
    }.map { (markets: [Market]) -> [Row] in
        var rows = [Row]()
        for market in markets {
            rows.append(.market(market: market))
        }
        return rows
    }.map { (rows: [Row]) -> [Model] in
        return [Model(section: .market, items: rows)]
    }.startWith(self.network.provider.getMarkets())
    .shareReplay(1)
    .asDriver(onErrorJustReturn: [])

Note that the only real difference is the beginning trigger.flatMap {. Your source will then be the button or whatever the user taps on to cause the network update which won't complete until it's deleted.

(The above is untested code, but it should give you an idea of the shape you want.)

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • Thank you Daniel, but this would make a network call every time the user taps a cell, correct? What I wanna do is just make the network call once and then every time the user taps a cell change the model generated by that network call which in turns updates the tableview – Diogo Antunes Apr 29 '17 at 12:07
  • You did say, "If the user clicks a tableViewCell I want to update the model." – Daniel T. Apr 29 '17 at 12:10
  • Yes, perhaps I explained myself wrong :) I make one initial call, from that call I map the data to a specific model which is then feed to the the tableView. When the user taps a cell I wan't to update that same model but without making a network call, makes sense? Sorry for the confusion, I appreciate the help – Diogo Antunes Apr 29 '17 at 12:12
  • Ah... You need to define all the ways the model can change and make an Observable for each one, then merge the Observables together. That is your `model`, not just the network call. – Daniel T. Apr 29 '17 at 13:05