In RxJS you can use the value from a observable for a new observable. For example:
this.authService.login(username, password).pipe(
switchMap((success: boolean) => {
if(success) {
return this.contactService.getLoggedInContact()
} else {
return of(null)
}
})
).subscribe(contact => {
this.contact = contact
})
But now I have to do a project in Swift and I want to achieve the same thing. I can get the two methods working, but using the result of the first observable for the second observable is something i can't get working. The switchMap pipe is something that does not exist in RxSwift and I cannot find the equivalent.
I've tried mapping the result of the login function to the observable and then flatmapping it, but unfortunately that didn't work.
What is the best way to do this in Swift without using a subscribe in a subscribe?
EDIT I've tried flat map:
APIService.login(email: "username", password: "password")
.flatMapLatest { result -> Observable<Contact> in
if result {
return APIService.getLoggedInContact()
} else {
return .of()
}
}.subscribe(onNext: {result in
print("Logged in contact: \(result)")
}, onError: {Error in
print(Error)
}).disposed(by: disposeBag)
But unfortunately that didn't work, I get an error Thread 1: EXC_BAD_ACCESS (code=1, address=0x13eff328c)
EDIT2:
This is the login function
static func login(email: String, password: String) -> Observable<Bool> {
return Observable<String>.create { (observer) -> Disposable in
Alamofire.request(self.APIBASEURL + "/contact/login", method: .post, parameters: [
"email": email,
"password": password
], encoding: JSONEncoding.default).validate().responseJSON(completionHandler: {response in
if (response.result.isSuccess) {
guard let jsonData = response.data else {
return observer.onError(CustomError.api)
}
let decoder = JSONDecoder()
let apiResult = try? decoder.decode(ApiLogin.self, from: jsonData)
return observer.onNext(apiResult!.jwt)
} else {
return self.returnError(response: response, observer: observer)
}
})
return Disposables.create()
}.map{token in
return KeychainWrapper.standard.set(token, forKey: "authToken")
}
}
This is the getLoggedInContact function
static func getLoggedInContact() -> Observable<Contact> {
return Observable.create { observer -> Disposable in
Alamofire.request(self.APIBASEURL + "/contact/me", method: .get, headers: self.getAuthHeader())
.validate().responseJSON(completionHandler: {response in
if (response.result.isSuccess) {
guard let jsonData = response.data else {
return observer.onError(CustomError.api)
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(.apiNewsDateResult)
let apiResult = try? decoder.decode(Contact.self, from: jsonData)
return observer.onNext(apiResult!)
} else {
return self.returnError(response: response, observer: observer)
}
})
return Disposables.create()
}
}
.empty()instead of.of()would be more idiomatic. - Daniel T.get...function then or somewhere else in the code. I copy-pasted the code into a sandbox project and it worked perfectly. - Daniel T.