dengST30
dengST30

Reputation: 4037

RxSwift: tap a button to do a request with Alamofire, how to make it work?

I have some UI rx button tap, I want to connect it to the network rx part.

Here is Rx btn tap part:

btn.rx.tap.take(1).flatMap {  [unowned self]  () -> Observable<Bool>  in
            // ...
            return NetManager.standard.request()
        }.debug().do(onNext: { _ in
                print("The data flows here")
            }).flatMapLatest { (echo: Bool) -> Observable<Bool> in
            if echo{
                if let app = UIApplication.shared.delegate as? AppDelegate{
                    app.goHome()
                }
                return Observable.of(true)
            }
            else{
                return Observable.of(false)
            }
        }.subscribe(onNext: { (result) in
            // ... , do sth with result
        }).disposed(by: rx.disposeBag)

Here is net request part with Alamofire:

class NetManager: NSObject {
    
    static let standard = NetManager()
    var dataRequest: DataRequest?
    
    func request() -> Observable<Bool> {
           return Observable<Bool>.create{ (observer) -> Disposable in
                    let data = // ...
                    // ...
                    self.dataRequest = AF.request(aURL, method: HTTPMethod.post, parameters: data, encoder: JSONParameterEncoder.prettyPrinted, headers: aHead).response { (response: DataResponse<Data?>) in
                            self.hud?.hide()
                           if let data = response.data{
                               observer.onNext(true)
                                // ....
                           }
                           else{
                               observer.onNext(false)
                           }
                       }
                    observer.onCompleted()
                    return Disposables.create{}
           }
       }
    
}

Here is the debug info:

Controller.swift:141 (viewDidLoad()) -> subscribed

Controller.swift:141 (viewDidLoad()) -> Event completed

Controller.swift:141 (viewDidLoad()) -> isDisposed

The result is , the do( next ) part seems like that it has not been called. Because no print in console.


The question is, how to make the rx logic work?


Here is what I tried:

turn btn.rx.tap.flatMap to btn.rx.tap.take(1).flatMap

because from this issue

if a flatMap completion would terminate the outer stream, it would mean that no more button taps would be registered in your stream, which don't really make sense.


Seen from this question

  btn.rx.tap.map { () -> Observable<Bool> in

    return NetManager.standard.request()
  }.flatMapLatest { (observer: Observable<Bool>) -> Observable<Bool> in
  
    // of cource, I can subscribe to observer
    /*
     observer.subscribe(onNext: { (echo) in
         
     })
     */
    
    
    // how to get the event element elegantly.
    
    return Observable.of(false)
}.subscribe(onNext: { (_) in
    
}).disposed(by: rx.disposeBag)

how to do this more elegantly.

Upvotes: 3

Views: 931

Answers (1)

boa_in_samoa
boa_in_samoa

Reputation: 607

The main issue here is that when performing the network request, you are not waiting for the network call to respond. So observer.onComplete() is triggered before observer.onNext()

class NetManager: NSObject {

static let standard = NetManager()
var dataRequest: DataRequest?

func request() -> Observable<Bool> {
       return Observable<Bool>.create { (observer) -> Disposable in
                let data = // ...
                // ...
                self.dataRequest = AF.request(aURL, method: HTTPMethod.post, parameters: data, encoder: JSONParameterEncoder.prettyPrinted, headers: aHead).response { (response: DataResponse<Data?>) in
                        self.hud?.hide()
                       if let data = response.data {
                           observer.onNext(true)
                           // ... 
                       }
                       else {
                           observer.onNext(false)
                       }
                       observer.onComplete() // Moving it here instead will wait for the response
                }
                return Disposables.create { }
       }
   }
}

At this point, your code should work as it is, but you're still overcomplicating a very simple thing.

Here is what to do:

btn.rx.tap
    .flatMap { _ in
        return NetManager.standard.request()
    }
    .subscribe(onNext: { (success) in
        if success {
            if let app = UIApplication.shared.delegate as? AppDelegate {
                app.goHome()
            }
        } 
    })
    .disposed(by: rx.disposeBag)

Upvotes: 2

Related Questions