1
votes

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()
        }
    }
1
Try cleaning and rebuilding. What you have in your edit should work... BTW, using .empty() instead of .of() would be more idiomatic. - Daniel T.
@DanielT. Cleaning and rebuilding didn't work :/ It crashes in the run method of the FlatMapLatest class with the same error. - Bas
There must be something going on in your get... function then or somewhere else in the code. I copy-pasted the code into a sandbox project and it worked perfectly. - Daniel T.
@DanielT. Hmmmm, okay, thanks! I'll look into that, I just imagined that it wouldn't be those functions, because as stand-alone functions they work like they are supposed to. - Bas
@DanielT. After a lot of testing, I couldn't find the issue. I added the functions, Do you see anything out of the ordinary ? - Bas

1 Answers

2
votes

There is operator flatMapLatest which does exactly the same as switchMap in RxJS.

You can find usage example here