2016-09-02 2 views
1

Я пытаюсь разработать систему разбиения на страницы в приложении iOS с помощью RxSwift. Вариант использования прост: пользователь может вводить текст в поле поиска, а приложение выполняет запрос, который разбит на страницы. Когда он изменяет значение, новый запрос выполняется на первой странице (это означает, что значение наблюдаемого должно быть сброшено до 1). Если пользователь очищает поле поиска (или вводит текст с менее чем двумя символами), список результатов очищается и текущая страница сбрасывается. Следующая страница выбирается, когда пользователь прокручивается в нижнюю часть списка.Как объединить два наблюдаемых в реактивных расширениях для того, чтобы отображать результаты?

Это не случайный случай или iOS, и я полагаю, он может быть написан идентичным образом с использованием RxKotlin или RxJs или любого другого реактивного расширения.

Моя текущая попытка - установить наблюдаемое для текста и наблюдаемое для текущей страницы и объединить их для выполнения запроса с обоими параметрами. Мне уже удалось выполнить именно то, что я ищу, но используя глобальные атрибуты для хранения текущего запроса и текущей страницы. Я хотел бы найти способ использовать только значения, излучаемые наблюдаемыми, без необходимости их поддерживать (я думаю, что код будет более кратким и легким для чтения и понимания).

Вот мой текущий код:

// self.nextPage is a Variable<Int> 
    let moreObs: Observable<Int> = self.nextPage.asObservable() 
     .distinctUntilChanged() // Emit only if page has changed. 

    // self.searchTextObservable is a PublishedSubject<String> that receives the values from the textfield 
    let searchObs: Observable<String> = self.searchTextObservable 
     .throttle(0.4, scheduler: MainScheduler.instance) // Wait 400ms when the user stops writing. 
     .distinctUntilChanged() // Emit only if query has changed. 

    self.resultsObservable = Observable 
     .combineLatest(searchObs, moreObs) { query, page in 
      return ["q": query, "p": "\(page)"] 
     } 
     .subscribeOn(MainScheduler.instance) // Emit on main thread. 
     .observeOn(ConcurrentDispatchQueueScheduler(globalConcurrentQueueQOS: .Background)) // Perform on background thread. 
     .map { params in 
      if params["q"]!.characters.count > 2 { 
       return params 
      } 
      return [:] 
     } 
     .flatMap { params in 
      return params.isEmpty ? 
      Observable.of([]) : 
      self.search(params) 
     } 
     .map { results in 
      if results.count > 0 { 
       self.results.appendContentsOf(results) 
      } else { 
       self.results = [] 
      } 
      return self.results 
    } 

До сих пор единственная функция, которая не работает является операция сброса на стоимости СледующаяСтраница в. Если бы я заставить его 1 когда searchObs излучает:

let searchObs: Observable<String> = self.searchTextObservable 
     .throttle(0.4, scheduler: MainScheduler.instance) // Wait 400ms when the user stops writing. 
     .distinctUntilChanged() // Emit only if query has changed. 
     .map {query in 
      self.nextPage.value = 1 
      return query 
     } 

Тогда у меня есть 2 запросы, которые выполняются.

Я злоупотребляю Rx?

ответ

2

Я бы не использовал combineLatest. Ваш номер страницы зависит от вашего текущего текста поиска, поэтому вы должны связать его с flatMapLatest. Таким образом, вы не отвечаете за то, чтобы поддерживать свое состояние самостоятельно, вместо того, чтобы позволить цепочке операторов сбросить это для вас.

let disposeBag = DisposeBag() 

let searchText = PublishSubject<String>() // search field text 
let newPageNeeded = PublishSubject<Void>() // fires when a new page is needed 

struct RequestPage { 
    let query: String 
    let page: Int 
} 

let requestNeeded = searchText.asObservable() 
    .flatMapLatest { text in 
     newPageNeeded.asObservable() 
      .startWith(()) 
      .scan(RequestPage(query: text, page: 0)) { request, _ in 
       return RequestPage(query: text, page: request.page + 1) 
      } 
    } 

requestNeeded 
    .subscribeNext { print($0) } 
    .addDisposableTo(disposeBag) 

searchText.onNext("A") 

searchText.onNext("B") 
newPageNeeded.onNext(()) 

searchText.onNext("C") 
newPageNeeded.onNext(()) 
newPageNeeded.onNext(()) 

Это выведет:

(RequestPage # 1) (запрос: "А", страница: 1)
(RequestPage # 1) (запрос: "В", страница: 1)
(RequestPage # 1) (запрос: "В", страница: 2)
(RequestPage # 1) (запрос: "С", стр: 1)
(RequestPage # 1) (запрос: "С" , страница: 2)
(RequestPage № 1) (запрос: «C», страница: 3)

+0

Это прекрасно! – obo

Смежные вопросы