2
\$\begingroup\$

I've implemented abstract module for fields validation according to this MVP clean architecture repo.
In a nutshell I've created ValidationGateway protocol containing signatures for validate methods. Then created ValidationUseCase protocol and implementation to wrap called exact method on validationGateway like this:

typealias ValidationUseCaseCompletionHandler = (_ result: ValidationResult<Void>) -> Void

protocol ValidateFieldsUseCase {
    ...
    func validatePassword(password: String, completionHandler: @escaping ValidationUseCaseCompletionHandler)
    ...
}

class ValidateFieldsUseCaseImplementation: ValidateFieldsUseCase {
    let validationGateway: ValidationGateway

    init(validationGateway: ValidationGateway) {
        self.validationGateway = validationGateway
    }

...

func validatePassword(password: String, completionHandler: @escaping ValidationUseCaseCompletionHandler) {
        self.validationGateway.validatePassword(password: password) { (result) in
            completionHandler(result)
        }
    }

...

And of course created implementation for gateway, eg.

class LocalValidationGateway: ValidationGateway {
...
  func validatePassword(password: String, completionHandler: @escaping ValidationGatewayCompletionHandler) {
        if password.isEmpty {
            completionHandler(.failure(PasswordValidationError.passwordIsEmpty))

        } else if isLengthTooShort(password, min: FieldLengths.minPasswordLength) {
            completionHandler(.failure(PasswordValidationError.passwordTooShort))
...

In a result I've got an abstract module that can be injected in any presenter and used for field validation like this:

...
let fieldValidatorGateway = LocalValidationGateway()
let fieldValidationUseCase = ValidateFieldsUseCaseImplementation(validationGateway: fieldValidatorGateway)

let presenter = ChangePasswordPresenterImplementation(view: changePasswordViewController,
                                                           account: account,
                                                           changeCredentialsUseCase: changeCredentialsUseCase,
                                                           fieldValidatorUseCase: fieldValidationUseCase)
...

And used in presenter like this:

...
func updatePassword(currentPassword: String, newPassword: String, newPasswordRepeat: String) {
    fieldValidatorUseCase.validatePassword(password: currentPassword) { (result) in
        switch result {
        case .success():
            self.checkNewPassword(currentPassword, newPassword, newPasswordRepeat)
        case let .failure(error): break
        //                self.view.displayCurrentPasswordError(errorType: error)
        default: break
        }
    }
}

func checkNewPassword(_ currentPassword: String,_ newPassword: String,_ newPasswordRepeat: String) {
    fieldValidatorUseCase.validatePassword(password: newPassword) { (result) in
        switch result {
        case .success():
            self.checkPasswordRepeat(currentPassword, newPassword, newPasswordRepeat)
        case let .failure(error): break
        //                self.view.displayNewPasswordError(errorType: error)
        default: break
        }
    }
}
...

My problem is that according to that solution every field that needs to be checked has to be wrapped like this and have completion handler served/implemented. From one point of view this is very readable, it's easy to find what does happen on success and failure, what fields are checked, code is divided in nice blocks of code. But from another, when you check more than a 3-4 fields it really starts to look like boilerplate code.

I like the module part of this solution, and not going to resign from it anytime soon, but question is is there some better solution to handle these fileds? Some kind of generic collection of textfields? Or do/try/catch where everything could be passed into one "do" block and catched error passed directly to view for display?

Sorry for long post, I hope everything is clear.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.