Validation
Client-side validation in your mobile app helps catch errors early on, right on the user’s device. This gives them immediate feedback and lets them fix mistakes without waiting for the app to communicate with a server. This improves the user experience by making it faster and smoother, and it also helps your app run more efficiently by reducing unnecessary communication with the server. However, don’t forget to also implement server-side validation for extra security!
Usage
Dependencies
Within your app
module’s build.gradle.kts
file, incorporate the following dependencies:
implementation(project(":core:validation"))
Example
Define your validation rules
Create a class that extends TextValidationRule
and implements its methods to define your validation rules:
class MyCustomRule(
override val errorMessage: (context: Context) -> String = { context ->
// Default error message
context.getString(R.string.error_message)
}
): TextValidationRule {
override val validationRule: (String) -> Boolean = {
// Your validation logic
// e.g.: input.length > 5
// Return true if the input is valid
}
}
There are also some predefined rules you can use:
- RequiredRule: Checks if the input is not null or empty
- EmailRule: Checks if the input is a valid email address
- PasswordRule: Checks if the input is a valid password (at least 6 characters, 1 uppercase, 1 lowercase, 1 digit)
Create a validator
Create a TextValidator
in your UIState
class to validate your input:
data class MyUIState(
// The input you want to validate (value is updated by UI events)
val requiredInput: String? = null,
// Option 1: Store validation Result as variable
var requiredInputValidationResult: ValidationResult: ValidationResult? = null
// Option 2: Store validation Result as State
var requiredInputValidationResultAsState: MutableState<ValidationResult?> = mutableStateOf(null)
) {
// This is a validator with the rules on which you want to validate
val validator = TextValidator.withRules(MyCustomRule()) // Takes a vararg of rules so you can add as many as you want
// This method will only work if you use the ValidationResult as State
// For the other option you have to call the validate method on the validator and manually update the state
fun validateRequiredInput(context: Context) {
if(requiredInput == null) return // If the input is null, we don't want to validate it
// Perform the validation and update the result
// .value is used to update the MutableState
requiredInputValidationResult.value = validator.validate(context, requiredInput)
}
}
Validating the input
You can now call the validateRequiredInput
method in your composable to validate the input. This will update requiredInputValidationResultAsState
with the validation result:
@Composable
fun MyComposable(uiState: MyUIState) {
val context = LocalContext.current
LaunchedEffect(uiState.requiredInput) {
uiState.validateRequiredInput(context)
}
}
Alternatively you can also validate the input in your ViewModel
:
class MyViewModel {
val _uiState = MutableStateFlow(MyUIState())
val uiState = _uiState.asStateFlow()
fun onAction(action: MyAction) {
when(action) {
is MyAction.ValidateRequiredInput -> {
_uiState.update {
it.copy(
requiredInputValidationResult = it.validator.validate(context, it.requiredInput)
)
}
}
}
}
}
Display error messages
You can now display error messages based on the validation result:
MyTextField(
error = uiState.requiredInputValidationResult.errorMessage() // Will be your error message if the validation fails
)
Checking if inputs are valid
You can check if all inputs are valid by calling the isValid
method on the TextValidator
:
val areInputsValid by remember {
derivedStateOf {
uiState.requiredInputValidationResult.isValid() == true
// More validation checks can be added
// e.g.: && uiState.otherInputValidationResult?.isValid() == true && ...
}
}
Button(
enabled = areInputsValid
)