I'm a very beginner with RxSwift and I'm trying to begin with a simple login screen. So I have 2 text fields and a login button, which is bind to a PublishSubject
so every time I tap the button, I'll send a network request to perform authentication.
Since authentication may fails, I went with a Driver
so I could replay my request each time I click the button.
I have 2 version of what I think is the same code but one works and one doesn't. I'm trying to understand what happens behind the scene.
Here the first version which works (request every time I touch the button) :
let credentials = Driver.combineLatest(email.asDriver(), password.asDriver()) { ($0, $1) }
self.signIn = signInTaps
.asDriver(onErrorJustReturn: ())
.withLatestFrom(credentials)
.flatMapLatest { email, password in // returns Driver<Result<AuthenticateResponse, APIError>>
return provider.request(.Authenticate(email: email, password: password))
.filterSuccessfulStatusCodes()
.mapObject(AuthenticateResponse)
.map { element -> Result<AuthenticateResponse, APIError> in
return .Success(element)
}
.asDriver { error in
let e = APIError.fromError(error)
return Driver<Result<AuthenticateResponse, APIError>>.just(.Failure(e))
}
.debug()
}
And here's the one which doesn't work (request fires only on first click) :
let credentials = Observable.combineLatest(email.asObservable(), password.asObservable()) { ($0, $1) }
self.signIn = signInTaps.asObservable()
.withLatestFrom(c)
.flatMapLatest { email, password in // returns Observable<AuthenticateResponse>
return provider.request(.Authenticate(email: email, password: password))
.filterSuccessfulStatusCodes()
.mapObject(AuthenticateResponse)
}
.map { element -> Result<AuthenticateResponse, APIError> in // returns Observable<Result<AuthenticateResponse, APIError>>
return .Success(element)
}
.asDriver { error in // returns Driver<Result<AuthenticateResponse, APIError>>
let e = APIError.fromError(error)
return Driver<Result<AuthenticateResponse, APIError>>.just(.Failure(e))
}
.debug()
For information, here's my properties declaration :
let email = Variable("")
let password = Variable("")
let signInTaps = PublishSubject<Void>()
let signIn: Driver<Result<AuthenticateResponse, APIError>>
RxSwift is a library for composing asynchronous and event-based code by using observable sequences and functional style operators, allowing for parameterized execution via schedulers.
Traits are observables with a narrow set of behaviors. Traits are simply a wrapper struct with a single read-only Observable sequence property.
There are four subject types in RxSwift: PublishSubject : Starts empty and only emits new elements to subscribers. BehaviorSubject : Starts with an initial value and replays it or the latest element to new subscribers.
RxSwift and Combine are reactive programming solutions whose purpose is to handle asynchronous events. RxSwift is the most popular framework whereas Combine is Apple's recently introduced built-in solution.
Let's break down what's happening in the first one (since they're mostly the same):
// 1.
let credentials = Driver.combineLatest(email.asDriver(), password.asDriver()) { ($0, $1) }
// 2.
self.signIn = signInTaps
.asDriver(onErrorJustReturn: ())
// 3.
.withLatestFrom(credentials)
// 4.
.flatMapLatest { email, password in // returns Driver<Result<AuthenticateResponse, APIError>>
return provider.request(.Authenticate(email: email, password: password))
.filterSuccessfulStatusCodes()
.mapObject(AuthenticateResponse)
.map { element -> Result<AuthenticateResponse, APIError> in
return .Success(element)
}
.asDriver { error in
let e = APIError.fromError(error)
return Driver<Result<AuthenticateResponse, APIError>>.just(.Failure(e))
}
.debug()
}
email
and password and combining them into a tuple of String
s.credentials
with a tap of a button.The second example is mostly the same except you're using observables instead of drivers. Check on signInTaps
and see if you're receiving events every time you tap the button. It could be that somewhere down the line the signal is deallocating and really the only difference between the two versions is the use of drivers and observables.
Also keep in mind that using a driver is just syntactic sugar over an observable.
let intDriver = sequenceOf(1, 2, 3, 4, 5, 6)
.asDriver(onErrorJustReturn: 1)
.map { $0 + 1 }
.filter { $0 < 5 }
is the same as
let intObservable = sequenceOf(1, 2, 3, 4, 5, 6)
.observeOn(MainScheduler.sharedInstance)
.catchErrorJustReturn(1)
.map { $0 + 1 }
.filter { $0 < 5 }
.shareReplay(1)
So when you use observables over drivers, you're losing .observeOn
and shareReplay
. It could be that with drivers you're just seeing the replay and cached values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With