Search code examples
rx-swift

RXSwift why loading variable return wrong value?


Here is my view model:

    let loadMoreTrigger = PublishSubject<Void>()
    let refreshTrigger = PublishSubject<Void>()
    let loading = BehaviorRelay<Bool>(value: false)
    let stories = BehaviorRelay<[Story]>(value: [])
    var offset = 0
    let error = PublishSubject<String>()
    let selectedFeedType: BehaviorRelay<FeedType> = BehaviorRelay(value: .best)

   override init() {
    super.init()
    let refreshRequest = loading.asObservable().sample(refreshTrigger).flatMap { loading -> Observable<[Story]> in
        if loading {
            return Observable.empty()
        } else {
            self.offset = 0
            return self.fetchStories(type: self.selectedFeedType.value, offset: self.offset)
        }
    }
    let loadMoreRequest = loading.asObservable().sample(loadMoreTrigger).flatMap { loading -> Observable<[Story]> in
        if loading {
            return Observable.empty()
        } else {
            self.offset += 10
            return self.fetchStories(type: self.selectedFeedType.value, offset: self.offset)
        }
    }
    let request = Observable.merge(refreshRequest, loadMoreRequest).share(replay: 1)
    
    let response = request.flatMap { (stories) -> Observable<[Story]> in
        request.do(onError: { error in
            self.error.onNext(error.localizedDescription)
        }).catchError { (error) -> Observable<[Story]> in
            Observable.empty()
        }
    }.share(replay: 1)
    
    Observable.combineLatest(request, response, stories.asObservable()) { request, response, stories in
        return self.offset == 0 ? response : stories + response
    }.sample(response).bind(to: stories).disposed(by: disposeBag)
    
    Observable.merge(request.map{_ in true}, response.map{_ in false}, error.map{_ in false}).bind(to: loading).disposed(by: disposeBag)
}

Then when i checking loading observer i have false -> true, instead of true -> false. I just don't understand why it happening.

    loading.subscribe {
        print($0)
    }.disposed(by: disposeBag)

In my viewController i call refreshTrigger on viewWillAppear using rx.sentMessage

Here is getFeed function:

func getFeed(type: FeedType, offset: Int) -> Observable<[Story]> {
    return provider.rx.request(.getFeed(type: type, offset: offset)).asObservable().flatMap { (response) -> Observable<[Story]> in
        do {
            let feedResponse = try self.jsonDecoder.decode(BaseAPIResponse<[Story]>.self, from: response.data)
            guard let stories = feedResponse.data else { return .error(APIError.requestFailed)}
            return .just(stories)
        } catch {
            return .error(error)
        }
    }.catchError { (error) -> Observable<[Story]> in
        return .error(error)
    }
}

Solution

  • Your request and response observables are emitting values at the exact same time. Which one shows up in your subscribe first is undefined.

    Specifically, request doesn't emit a value until after the fetch request completes. Try this instead:

    Observable.merge(
        loadMoreTrigger.map { true },
        refreshTrigger.map { true },
        response.map { _ in false },
        error.map { _ in false }
    )
    .bind(to: loading)
    .disposed(by: disposeBag)
    

    There are lots of other problems in your code but the above answers this specific question.