Объедините 2 объекта PublishSubject и отправьте с Observable.combineLatest - PullRequest
0 голосов
/ 21 октября 2019

У меня есть 2 отдельных набора данных в моем сервисе.

Featured и Standard content.

У меня есть 2 вызова API, которые я делаю, чтобы вернуть эти элементы. Они могут использоваться отдельно, однако у меня также есть случай использования, когда я хотел бы взять оба набора данных, обеспечить некоторое обогащение на основе условия и затем вернуть их потребителю.

Я надеялся, что смогу сделатьчто-то вроде этого:


class ContentService: ContentServiceType {

    let featured = PublishSubject<[Content]>()
    let standard = PublishSubject<[Content]>()

    let content: Observable<(featured: [Content], standard: [Content])>

    private let client: Client<ContentAPI>
    private let disposeBag = DisposeBag()

    init(client: Client<ContentAPI>) {
        self.client = client

        content = Observable
            .combineLatest(featured, standard)
            .map { (featured, standard) -> (featured: [Content], standard: [Content]) in

        /*
            Do some enrichment and create then return new, updated versions
        */

                return (featured: updatedFeatured, standard: updatedStandard)
        }.share()
    }

    func fetchStandardContent(page: Int = 0, size: Int = 100) -> Single<Void> {

        let params = ["page": page, "size": size]
        let request: Single<Content> = client.request(.getStandardContent(params))

        return request.map { [unowned self] launchers in
            self.standard.onNext(content.props)
            return ()
        }
    }

    func fetchFeaturedContent(page: Int = 0, size: Int = 100) -> Single<Void> {

        let params = ["page": page, "size": size]
        let request: Single<Content> = client.request(.getFeaturedContent(params))

        return request.map { [unowned self] content in
            self.featured.onNext(content.props)
            return ()
        }
    }
 }   

В других местах моих приложений я тогда надеялся, что смогу сделать что-то вроде

        contentSvc.content
            .observeOn(MainScheduler.instance)
            .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
            .subscribe(onNext: { content in
               /* do something w/ content */
            }).disposed(by: disposeBag)

И затем всякий раз, когда вызывается contentSvc.fetchFeaturedContent или contentSvc.fetchStandardContent, contentSvc.content подписчик выше получает новые данные.

Вместо этого content, похоже, не излучает никаких значений.

Ответы [ 2 ]

2 голосов
/ 21 октября 2019

combineLatest требует, чтобы оба источника испускали прежде, чем он испустит себя, я полагаю.

Возможно, я бы посмотрел на использование BehaviorSubject или BehaviorRelay вместо PublishSubject.

0 голосов
/ 24 октября 2019

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

let featured = BehaviorRelay(value: [Content]())
let standard = BehaviorRelay(value: [Content]())

func getContent() -> Observable<(featured: [Content], standard: [Content])> {
     return Observable
       .combineLatest(
         featured.asObservable(),
         standard.asObservable(),
         resultSelector: { (featured, standard) -> (featured: [Content], standard: [Content]) in
           return (featured: featured, standard: standard)
       }
     )
  }

func addElemToFeatured() {
    featured.accept([Content(name: "abc")])
  }

  func addElemToStandard() {
    standard.accept([Content(name: "xyz")])
  }

Вызовите метод getContent () из разных классов в методе инициализатора. Также вызовите addElemToFeatured, addElemToStandard из разных мест, например, действие кнопки.

listener!.getContent()
      .subscribe(onNext: { (featured, standard) in
        print(featured)
        print(standard)
      }).disposed(by: disposeBag)
...