Combining Alamofire and RxSwift

Pham Hoan picture Pham Hoan · Dec 7, 2015 · Viewed 9.7k times · Source

I have this custom implementation of Alamofire:

protocol HTTPProtocol: class {
    typealias RequestType
    typealias RespondType
    func doRequest(requestData: RequestType) -> Self
    func completionHandler(block:(Result<RespondType, NSError>) -> Void) -> Self
}

//example of a request:
locationInfo
      //Make a request
    .doRequest(HTTPLocationInfo.RequestType(coordinate: $0))

      //Call back when request finished
    .completionHandler { result in
        switch result {
            case .Success(let info): self.locationInfoRequestSuccess(info)
            case .Failure(let error): self.locationInfoRequestFailed(error)
        }               
    }

I want to apply MVVM and RxSwift into my project. However, I can't find a proper way to do this.

What I want to achieve is a ViewModel and a ViewController that can do these things:

class ViewController {
    func googleMapDelegate(mapMoveToCoordinate: CLLocationCoordinate2D) {
        // Step 1: set new value on `viewModel.newCoordinate` and make a request
    }

    func handleViewModelCallBack(resultParam: ...*something*) {
        // Step 3: subscribeOn `viewModel.locationInfoResult` and do things.
    }
}

class ViewModel {
    //Result if a wrapper object of Alamofire.
    typealias LocationInfoResult = (Result<LocationInfo.Respond, NSError>) -> Void
    let newCoordinate = Variable<CLLocationCoordinate2D>(kInvalidCoordinate)
    let locationInfoResult: Observable<LocationInfoResult>

    init() {
        // Step 2: on newCoordinate change, from step 1, request Location Info
        // I could not find a solution at this step
        // how to make a `completionHandler` set its result on `locationInfoResult`
    }
}

Any help is deeply appreciated. Thank you.

Answer

Mostafa Abdellateef picture Mostafa Abdellateef · Jul 28, 2016

You can use RxAlamofire as @Gus said in the comment. But if you are using any library that doesn't support Rx extensions by default you may need to do the conversion by hand.

So for the above code snippet, you can create an observable from the callback handler you had implemented

    func getResultsObservable() -> Observable<Result> {
        return Observable.create{ (observer) -> Disposable in
            locationInfo
                //Make a request
                .doRequest( .... )

                //Call back when request finished
                .completionHandler { result in
                    switch result {
                    case .Success(let info): observer.on(Event.Next(info))
                    case .Failure(let error): observer.on(Event.Error(NetworkError()))
                    }
            }       
            return Disposables.create {
               // You can do some cleaning here      
            }
        }
    }

Callback handlers are implementation to observer pattern, so mapping it to a custom Observable is a straight forward operation.

A good practice is to cancel the network request in case of disposing, for example this is a complete disposable Post request:

return Observable<Result>.create { (observer) -> Disposable in
        let requestReference = Alamofire.request("request url",
            method: .post,
            parameters: ["par1" : val1, "par2" : val2])
            .validate()
            .responseJSON { (response) in
                switch response.result{
                case .success:
                     observer.onNext(response.map{...})
                     observer.onCompleted()
                case .failure:
                    observer.onError(NetworkError(message: response.error!.localizedDescription))
                }
        }
        return Disposables.create(with: {
            requestReference.cancel()
        })

Note: before swift 3 Disposables.create() is replaced with NopDisposable.instance