Search code examples
iosrx-swiftaws-amplifyswift5

AWS Amplify Swift API login iOS Mobile App


I am following the docs for implementing the login by API,

class AWSUserPool {

    var userAuthenticationError: Error?

    AWSMobileClient.default().signIn(username: <param username>, password: <param password>) { (signInResult, error) in
        if let error = error  {
            print("\(error.localizedDescription)")
            self.userAuthenticationError = error
        } else if let signInResult = signInResult {
            switch (signInResult.signInState) {
            case .signedIn:
                print("User is signed in.")
            case .smsMFA:
                print("SMS message sent to \(signInResult.codeDetails!.destination!)")
            default:
                print("Sign In needs info which is not yet supported.")
            }
        }
    }
}

I have instantiated the class and the login works when the correct credentials are supplied, I want to show the error message to an alert but I can print the error which is an optional (user not found....) but I am unable to assign it to a class variable.

import RxSwift

        let observable: Observable<Error?> = Observable<Error?>.just(self.awsUserPool?.userAuthenticationError)
        let disposeBag = DisposeBag()


        observable.subscribe { event in
           print(event)
        }
        .disposed(by: disposeBag)

Solution

  • I would first write a reactive wrapper around AWSMobileClient like so:

    extension Reactive where Base: AWSMobileClient {
        func signIn(username: String, password: String) -> Observable<SignInResult> {
            return Observable.create { observer in
                self.base.signIn(username: username, password: password) { (signinResult, error) in
                    if let signinResult = signinResult {
                        observer.onNext(signinResult)
                        observer.onCompleted()
                    }
                    else {
                        observer.onError(error ?? RxError.unknown)
                    }
                }
                return Disposables.create()
            }
        }
    }
    

    Then to put the error in an Observable do something like this:

    class AWSUserPool {
    
        let userAuthenticationError: Observable<Error>
        private let disposeBag = DisposeBag()
    
        init(username: String, password: String) {
            let result = AWSMobileClient.default().rx.signIn(username: username, password: password)
                .materialize()
    
            result
                .compactMap { $0.element }
                .subscribe(onNext: { signinResult in
                    switch signinResult.signInState {
                    case .signedIn:
                        print("User is signed in.")
                    case .smsMFA:
                        print("SMS message sent to \(signinResult.codeDetails!.destination!)")
                    default:
                        print("Sign In needs info which is not yet supported.")
                    }
                })
                .disposed(by: disposeBag)
            userAuthenticationError = result.compactMap { $0.error }
        }
    }
    

    I probably wouldn't bother with the AWSUserPool though... That's up to you.