Ivan.Ye
Ivan.Ye

Reputation: 21

I want to check text validate when tap button

I want to check text validate when tap button. If validate failure, it will show error text on UILabel and do not send request. Else if validate success, it will send request.

I see many demo about login, but they control button enabled to avoid validate data when tap button. I am puzzled with it.

I have write some code.

class LoginViewModel: BaseViewModel, ViewModelType {
    struct Input {
        let loginTaps: Driver<Void>
    }
    struct Output {
        let validatedUsername: Driver<Bool>
        let validatedPassword: Driver<Bool>
    }

    let username = BehaviorRelay(value: "")
    let password = BehaviorRelay(value: "")
    let loginTapped = PublishSubject<Void>()

    func transform(input: Input) -> Output {

        let validatedUsername = username.asDriver(onErrorJustReturn: "").map { username in
            return username.isPhoneNumber
        }

        let validatedPassword = password.asDriver(onErrorJustReturn: "").map { password in
            return password.count > 7
        }

        input.loginTaps.map { () -> Void in
            <#code#>
            // I want do check and then do network request
        }

        input.loginTaps.drive(onNext: { [weak self] () in
                self?.loginTapped.onNext(())
            }).disposed(by: rx.disposeBag)

        loginTapped.flatMapLatest { _  -> Observable<RxSwift.Event<Token>> in

           // and if I want to return Bool not Token, how should I do????????????

            return self.provider.login(username: self.username.value, password: self.password.value)
                .asObservable()
                .materialize()
            }.subscribe(onNext: { (event) in
                switch event {
                case .next(let token):
                    AuthManager.setToken(token: token)
                case .error(let error):
                    log.error(error.localizedDescription)
                default: break
                }
            }).disposed(by: rx.disposeBag)

        return Output(validatedUsername: validatedUsername,
                      validatedPassword: validatedPassword)
    }
}

Upvotes: 2

Views: 818

Answers (1)

Daniel T.
Daniel T.

Reputation: 33979

I would expect to see something like this:

class LoginViewModel {
    struct Input {
        let loginTap: Signal<Void>
        let username: Driver<String>
        let password: Driver<String>
    }

    struct Output {
        let errorText: Driver<String>
        let loginSuccess: Signal<Void>
    }

    var networkRequest: (URLRequest) -> Observable<Data> = { _ in fatalError("need to replace this with an implementation.") }

    func transform(input: Input) -> Output {

        func isValidCredentials(username: String, password: String) -> Bool {
            return username.isPhoneNumber && password.count > 7
        }

        let credentials = Driver.combineLatest(input.username, input.password)

        // this chain emits when the login button is tapped and the credentials are invalid
        let invalidInputError = input.loginTap
            .withLatestFrom(credentials)
            .filter { !isValidCredentials(username: $0, password: $1) }
            .map { _ in "Credentials are invalid" }
            .asDriver(onErrorRecover: { _ in fatalError("can't get here") })

        let networkRequest = self.networkRequest // to avoid dealing with `self` in the flatMap below

        // this chain makes a request if the login button is tapped while the credentials are valid
        let loginResult = input.loginTap
            .withLatestFrom(credentials)
            .filter { isValidCredentials(username: $0, password: $1) }
            .map { URLRequest.login(username: $0, password: $1) }
            .asObservable()
            .flatMapLatest { networkRequest($0).materialize() }
            .share(replay: 1)

        // this chain emits when the login result produces an error
        let loginError = loginResult
            .map { $0.error }
            .filter { $0 != nil }
            .map { $0!.localizedDescription }
            .asDriver(onErrorRecover: { _ in fatalError("can't get here") })

        // this chain emits when the login result succeeds
        let loginSuccess = loginResult
            .filter { $0.element != nil }
            .map { _ in }
            .asSignal(onErrorRecover: { _ in fatalError("can't get here") })

        let errorText = Driver.merge(invalidInputError, loginError)

        return Output(errorText: errorText, loginSuccess: loginSuccess)
    }
}

Upvotes: 1

Related Questions