Lightweight library for grouping input fields.
Each field has a set of rules to be validated. On form validation all fields gets validated one-by-one. Failed rules for each field passed to field itself, to show error, for example.
- Input field fully customizable.
- Shift focus to next field on return.
- Shif focus to first failed field of form validation.
- Validate form with custom behaviour.
- Display/Hide error for every input field with custom behaviour.
- A banch of predefined text validation rules for most cases.
- Custom validation rules.
- Create input field and display error for failed validation rules, if needed.
struct MyField: View {
let title: String
let text: Binding<String>
// Failed rules come from FormField during whole form validation.
let failedRules: [ValidationRule]
var body: some View {
VStack(alignment: .leading) {
TextField(title, text: text)
.background(Color.white)
// Display error.
if let errorMessage = failedRules.first?.message, errorMessage.isEmpty == false {
Text(errorMessage)
.foregroundColor(.red)
}
}
}
}
- Group input fields into form.
struct ContentView: View {
@State var name: String = ""
@State private var isAllFieldValid = false
var body: some View {
FormView( First failed field
validate: [.manual], // Form will be validated on user action.
hideError: .onValueChanged, // Error for field wil be hidden on field value change.
isAllFieldValid: $isAllFieldValid // Property indicating the result of validation of all fields without focus
) { proxy in
FormField(
value: $name,
rules: [TextValidationRule.notEmpty(message: "Name field should no be empty")],
isRequired: true, // field parameter, necessary for correct determination of validity of all fields
) { failedRules in
MyField(title: "Name", text: $name, failedRules: failedRules)
}
// Other input fields...
Button("Validate") {
// Validate form on user action.
print("Form is valid: \(proxy.validate())")
}
.disabled(isAllFieldValid == false) // Use isAllFieldValid to automatically disable the action button
}
}
}
The form can be validated under one or multiple conditions simultaneously, such as:
onFieldValueChanged
- each field validated on it's value changed.onFieldFocus
- each field validated on focus gain.onFieldFocusLost
- each field validated on focus lost.manual
- on callproxy.validate()
. Default behaviour. First failed field is focused automatically.
Error for each field gets hidden at one of three specific times:
onValueChanged
- value of field with error has changed. Defaule behaviour.onFocus
- field with error is focused..onFucusLost
- field with error lost focus.
Property indicating the result of validation of all fields without focus. Using this property you can additionally build ui update logic, for example block the next button.
Extend ValidationRule
:
extension ValidationRule {
static var myRule: Self {
Self.custom(conditions: [.manual, .onFieldValueChanged, .onFieldFocus]) {
return ($0.isEmpty == false, "Text should not be empty")
}
}
}
A banch of predefind rules for text validation is available via ValidationRule
:
- notEmpty - value not empty.
- digitsOnly - value contains only digits.
- lettersOnly - value contains only letters.
- email - is valid email.
- minLenght/maxLenth - value length greate/less.
- regex - evaluate regular expresstion.
- etc...
If you need to display validation errors from external services (e.g., a backend) use ValidationRule.external
:
ValidationRule.external { [weak self] in
guard let self else {
return (true, "")
}
return await self.availabilityCheckAsync($0)
}
private func availabilityCheckAsync(_ value: String) async -> (Bool, String) {
let isAvailable = try await ...
return (isAvailable, "Not available")
}
FormView doesn't use any external dependencies.
- Swift 5.0
- iOS 15.0
dependencies: [
.package(
url: "https://github.com/MobileUpLLC/FormView",
.upToNextMajor(from: "1.3.0")
)
]
FormView is destributed under the MIT license.