8

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)
}

...
solidcell
  • 7,639
  • 4
  • 40
  • 59
meow
  • 27,476
  • 33
  • 116
  • 177

1 Answers1

15

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.
solidcell
  • 7,639
  • 4
  • 40
  • 59
  • this is awesome - exactly the kind of very practical, simple snippets that is needed! i have a few questions regarding your last example: i set up answers as a variable, so i can access its value easily when i need it. in your case, answers is simply an observable which observes the initial (never changes), and the refresh, which is whenever the value changes. wouldn't you need to put a filter {$0 == false} so you only refresh when the refreshing value is false? secondly, i see what you did there - the answers will ONLY be set when the loadAnswer completes. – meow Jul 29 '16 at 21:04
  • No need for a `filter` since `rc.rx_controlEvent(.ValueChanged)` will only emit an event when refreshing begins. – solidcell Jul 29 '16 at 21:08
  • would you be able to use variable in lieu of obseverable for answers in your example? – meow Jul 29 '16 at 21:10
  • Yea it would simply be doing what you were doing before: using a `bindTo`. You also wouldn't need the `shareReplayLatestWhileConnected` anymore. I don't see the need though. – solidcell Jul 29 '16 at 21:14