Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxSwift: using rx_refreshing for uirefreshcontrol

I am using the UIRefreshControl + Variable binding to reload data.

It is working, however, the following feels wrong to me:

1) I know there is a rx_refreshing variable in the RXCocoa extension, but I am unable to get it to work in this context.

2) I am binding answers (which is a Variable of array) twice. Once when I load the view controller and again when the UIRefreshControl is refreshing.

3) The parts where I check for whether the UIRefreshControl is refreshing or not looks really awkward. It feels like it defeats the purpose of using reactive?

...

let answers: Variable<[Answer]> = Variable([])

override func viewDidLoad() {       
  loadAnswers()
     .shareReplay(1)
     .bindTo(answers)
     .addDisposableTo(self.disposeBag)
  setupRx()
}

func loadAnswers() -> Observable<[Answer]> {
  return Network.rxArrayRequest(Spark.Answers)
}  

func setupRx() {
  rc.rx_controlEvent(.ValueChanged)
    .map { _ in !self.rc.refreshing }
    .filter { $0 == false }
    .flatMapLatest { [unowned self] _ in
      return self.loadAnswers()
    }
    .bindTo(answers)
    .addDisposableTo(self.disposeBag)

  rc.rx_controlEvent(.ValueChanged)
    .map { _ in self.rc.refreshing }
    .filter { $0 == true }
    .subscribeNext { [unowned self] _ in
      self.rc.endRefreshing()
    }
    .addDisposableTo(self.disposeBag)
}

...
like image 463
meow Avatar asked Jul 25 '16 23:07

meow


1 Answers

So first of all, It's not actually working. It just seems to be working. In your code, you're actually not waiting for the network request to finish before you call rc.endRefreshing(). Instead, you're just making the network call and then immediately calling endRefreshing().

// `rc.rx_controlEvent(.ValueChanged)` only gets called once,
// when the user pulls down.

rc.rx_controlEvent(.ValueChanged)       // user pulled down to refresh
  .map { _ in !self.rc.refreshing }     // !true -> false 
  .filter { $0 == false }               // false == false
  .flatMapLatest { [unowned self] _ in
    return self.loadAnswers()           // request answers
  }
  .bindTo(answers)
  .addDisposableTo(self.disposeBag)

rc.rx_controlEvent(.ValueChanged)       // user pulled down to refresh
  .map { _ in self.rc.refreshing }      // true -> true
  .filter { $0 == true }                // true == true
  .subscribeNext { [unowned self] _ in
    self.rc.endRefreshing()             // end refreshing
  }
  .addDisposableTo(self.disposeBag)

To address concern 1, you're right, you can use rx_refreshing to turn off refreshing instead of endRefreshing().

To address concern 2, I don't think the Variable is necessary or useful, at least in this example. You could still use it though. Also, it's not necessary to loadAnswers() in two places.

To address concern 3, yea, you could be simplifying this a lot and using Rx a bit more.

Here's code that would actually work, use rx_refreshing, and simplify things a lot:

let initial = Observable<Void>.just(())
let refresh = rc.rx_controlEvent(.ValueChanged).map { _ in () }
let answers = Observable.of(initial, refresh)
    .merge()
    .flatMapLatest{ _ in self.loadAnswers() }
    .shareReplayLatestWhileConnected()

answers
    .map { _ in false }
    .bindTo(rc.rx_refreshing)
    .addDisposableTo(disposeBag)

// also use `answers` to bind to your data source, etc.
like image 55
solidcell Avatar answered Oct 20 '22 14:10

solidcell