API funtion using RxSwift

Viewed 134

I want to change the API request code written using the closure to RxSwift.
For example, I would like to make rxGetList() function using getList() function.

// This function cannot be modified.
func getList(success: @escaping ([String]) -> Void,
             failure: @escaping (Error) -> Void) {
    // Request to Server...
}

func rxGetList() -> Observable<String> {
    // Using getList() function
    // TODO
}

What code should I write in TODO section? Please give me some advice.

2 Answers

The easiest way to meet your expectations is to use something like this:

func rxGetList() -> Observable<String> {
        return Observable.create { observer in
            
            getList(success: { result in
                for everyString in result {
                    observer.onNext(everyString)
                }
                observer.onCompleted()
            }, failure: { error in
                observer.onError(error)
            })
            
            return Disposables.create() {
                // specify any action to be performed on observable dispose (like cancel URL task)
            }
        }
    }

Note that you have [String] specified as an input type of your success closure. If it's not a typo then above code fits. If you want one String instead, it's as simple as this:

func rxGetList() -> Observable<String> {
        return Observable.create { observer in
            
            getList(success: { result in
                observer.onNext(result)
                observer.onCompleted()
            }, failure: { error in
                observer.onError(error)
            })
            
            return Disposables.create() {
                // specify any action to be performed on observable dispose (like cancel URL task)
            }
        }
    }

Petr Grigorev's answer is the correct one, but if you want to have fun with some extreme function composition, here's a more advanced way to handle it:

let rxGetList = Observable.create(rx_(getList(success:failure:)))
    .flatMap { Observable.from($0) }

func rx_<A>(_ fn: @escaping (@escaping (A) -> Void, @escaping (Error) -> Void) -> Void) -> (AnyObserver<A>) -> Disposable {
    {
        fn(singleObserve($0), $0.onError)
        return Disposables.create()
    }
}

func singleObserve<A>(_ observer: AnyObserver<A>) -> (A) -> Void {
    {
        observer.onNext($0)
        observer.onCompleted()
    }
}

I'm not sure about actually using the above, but if you have a lot of functions that you want to wrap, it may help reduce the amount of code you have to write.

Related