Search code examples
iosswiftrequestrx-swiftmoya

How do I combine observe two result rx sequences and subscribe with one closure arguments?


My sample is create call rest api two observable of result from load rx.request api from moya

func dataOneObservable() -> Observable<ObJectOneClass> {
        
        return myprovider.rx.request(API.loadDataDetail())
            .asObservable()
            .retry()
            .observeOn(MainScheduler.instance)
            .filterSuccessfulStatusAndRedirectCodes()
            .catchObjectError()
            .mapObject(ObJectOneClassResult.self)
            .map({ (response) -> ObJectOneClass in
                if ObJectOneClass.data != nil {
                    if let item = response.data {
                        return item
                    }
                    
                    return ObJectOneClass()
                }
                
                return ObJectOneClass()
            })
    } 

func dataTwoObservable() -> Observable<ObJectTwoClass> {
            
            return myprovider.rx.request(API.loadDataProfile())
                .asObservable()
                .retry()
                .observeOn(MainScheduler.instance)
                .filterSuccessfulStatusAndRedirectCodes()
                .catchObjectError()
                .mapObject(ObJectTwoClassResult.self)
                .map({ (response) -> ObJectTwoClass in
                    if ObJectTwoClass.data != nil {
                        if let item = response.data {
                            return item
                        }
                        
                        return ObJectTwoClass()
                    }
                    
                    return ObJectTwoClass()
                })
        } 

Then I want to combine result by use combineLastest of RxSwift but when I use .subscribe my response event can't passing result

my function call is same this:

    func testCombine(completion:@escaping(_ result:Result<(ObJectOneClass,ObJectTwoClass),Error>) -> ()){
        
    

        _ = Observable.combineLatest(dataOneObservable(), dataTwoObservable())
                .asObservable()
                .subscribe({ event in
    //Event<(ObJectOneClass,ObJectTwoClass)>
//case .next((a, b)):
                    switch event{
                        
                    case .next(response):
                        completion(.success(response))
                    case let .error(error):
                        completion(.failure(error as NSError))
                    default:
                        break
                    }
                })
        }

Then enter image description here

please help me guide to syntax completion this.


Solution

  • Here's an interesting solution:

    func testCombine(completion: @escaping(_ result: Result<(ObJectOneClass, ObJectTwoClass), Error>) -> ()) {
        _ = Observable.combineLatest(dataOneObservable(), dataTwoObservable())
            .map(Result.success)
            .catch(combine(Result.failure, Observable.just))
            .subscribe(onNext: completion)
    }
    
    func combine<A, B, C>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C {
        { g(f($0)) }
    }
    

    That map to Result.success, catch just Result.failure is really common when you are dealing with Result types. It's so common that you might want to make a single operator to capture the notion.

    extension Observable {
        func toResult() -> Infallible<Result<Element, Error>> {
            map(Result.success)
                .asInfallible(onErrorRecover: combine(Result.failure, Infallible.just))
        }
    }
    

    In response to your comment, here's a somewhat less advanced version:

    extension Observable {
        func toResult() -> Infallible<Result<Element, Error>> {
            map(Result.success)
                .asInfallible(onErrorRecover: { Infallible.just(Result.failure($0)) })
        }
    }