Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions ExampleApp/ExampleApp/UI/ContentScreen/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import FormView

struct ContentView: View {
@ObservedObject var viewModel: ContentViewModel
@State var isAllFieldValid = false
Comment thread
reykich marked this conversation as resolved.
Outdated

var body: some View {
FormView(
validate: [.manual, .onFieldValueChanged, .onFieldFocus],
hideError: .onValueChanged
hideError: .onValueChanged,
isAllFieldValid: $isAllFieldValid
) { proxy in
FormField(
value: $viewModel.name,
Expand Down Expand Up @@ -52,7 +54,7 @@ struct ContentView: View {
print("Form is valid: \(await proxy.validate())")
}
}
.disabled(viewModel.isLoading)
.disabled(isAllFieldValid == false || viewModel.isLoading)
}
.padding(.horizontal, 16)
.padding(.top, 40)
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ struct MyField: View {
```swift
struct ContentView: View {
@State var name: String = ""
@State var isAllFieldValid = false
Comment thread
reykich marked this conversation as resolved.
Outdated

var body: some View {
FormView( First failed field
validate: .never, // Form will be validated on user action.
hideError: .onValueChanged // Error for field wil be hidden on field value change.
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,
Expand All @@ -74,6 +76,7 @@ struct ContentView: View {
// Validate form on user action.
print("Form is valid: \(proxy.validate())")
}
.disabled(isAllFieldValid == false) // Use isAllFieldValid to automatically disable the action button
}
}
}
Expand All @@ -92,6 +95,9 @@ Error for each field gets hidden at one of three specific times:
* `onFocus` - field with error is focused..
* `onFucusLost` - field with error lost focus.

### Is All Field Valid
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.

### Custom Validation Rules
One of two ways:
1. Adopt protocol `ValidationRule`:
Expand Down Expand Up @@ -224,7 +230,7 @@ FormView doesn't use any external dependencies.
dependencies: [
.package(
url: "https://github.com/MobileUpLLC/FormView",
.upToNextMajor(from: "1.1.2")
.upToNextMajor(from: "1.3.0")
)
]
```
Expand Down
2 changes: 2 additions & 0 deletions Sources/FormView/FormField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct FormField<Content: View>: View {
@ViewBuilder private let content: ([ValidationRule]) -> Content

@State private var failedValidationRules: [ValidationRule] = []
private var isValid: Bool { failedValidationRules.isEmpty && value.isEmpty == false }
Comment thread
reykich marked this conversation as resolved.
Outdated

// Fields Focus
@FocusState private var isFocused: Bool
Expand Down Expand Up @@ -57,6 +58,7 @@ public struct FormField<Content: View>: View {
}
]
)
.preference(key: FieldsValidationKey.self, value: [isValid])
.focused($isFocused)

// Fields Validation
Expand Down
6 changes: 6 additions & 0 deletions Sources/FormView/FormView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ private class FormStateHandler: ObservableObject {

public struct FormView<Content: View>: View {
@StateObject private var formStateHandler = FormStateHandler()
@Binding private var isAllFieldValid: Bool
@ViewBuilder private let content: (FormValidator) -> Content

private let errorHideBehaviour: ErrorHideBehaviour
Expand All @@ -71,11 +72,13 @@ public struct FormView<Content: View>: View {
public init(
validate: [ValidationBehaviour] = [.manual],
hideError: ErrorHideBehaviour = .onValueChanged,
isAllFieldValid: Binding<Bool> = .constant(true),
@ViewBuilder content: @escaping (FormValidator) -> Content
) {
self.content = content
self.validationBehaviour = validate
self.errorHideBehaviour = hideError
self._isAllFieldValid = isAllFieldValid
}

public var body: some View {
Expand All @@ -85,6 +88,9 @@ public struct FormView<Content: View>: View {
.onPreferenceChange(FieldStatesKey.self) { [weak formStateHandler] newStates in
formStateHandler?.updateFieldStates(newStates: newStates)
}
.onPreferenceChange(FieldsValidationKey.self) { validationResults in
isAllFieldValid = validationResults.contains(false) == false
}
.onSubmit(of: .text) { [weak formStateHandler] in
formStateHandler?.submit()
}
Expand Down
16 changes: 16 additions & 0 deletions Sources/FormView/Preference/FieldsValidationKey.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// FieldsValidationKey.swift
// FormView
//
// Created by Victor Kostin on 31.03.2025.
//

import SwiftUI

struct FieldsValidationKey: PreferenceKey {
static var defaultValue: [Bool] = []

static func reduce(value: inout [Bool], nextValue: () -> [Bool]) {
value += nextValue()
}
}