Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining Alamofire and RxSwift

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.

like image 834
Pham Hoan Avatar asked Dec 07 '15 13:12

Pham Hoan


1 Answers

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

like image 198
Mostafa Abdellateef Avatar answered Nov 14 '22 18:11

Mostafa Abdellateef