Skip to content

Commit

Permalink
Finalize documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Supereg committed Nov 4, 2023
1 parent 3e76afb commit 7cf51a6
Show file tree
Hide file tree
Showing 18 changed files with 145 additions and 269 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ extension EnvironmentValues {
self[ValidationDebounceDurationKey.self] = newValue
}

Check warning on line 30 in Sources/SpeziValidation/Configuration/ValidationDebounceDuration.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SpeziValidation/Configuration/ValidationDebounceDuration.swift#L28-L30

Added lines #L28 - L30 were not covered by tests
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI

extension ValidationEngine {
/// The configuration of a ``ValidationEngine``.
public struct Configuration: OptionSet, EnvironmentKey, Equatable { // TODO do we have to test those?
public struct Configuration: OptionSet, EnvironmentKey, Equatable {
/// This configuration controls the behavior of the ``ValidationEngine/displayedValidationResults`` property.
///
/// If ``ValidationEngine/submit(input:debounce:)`` is called with empty input and this option is set, then the
Expand Down
78 changes: 0 additions & 78 deletions Sources/SpeziValidation/FocusStateTest.swift

This file was deleted.

18 changes: 16 additions & 2 deletions Sources/SpeziValidation/SpeziValidation.docc/SpeziValidation.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,24 @@ SPDX-License-Identifier: MIT

## Topics

### Using Validation
### Performing Validation

- ``ValidationRule``
- ``SwiftUI/View/validate(input:rules:)-5dac4``
- ``SwiftUI/View/validate(input:rules:)-9vks0``

### Managing Validation

- ``ValidationEngine``
- ``ValidationState``
- ``SwiftUI/View/receiveValidation(in:)``

### Configuration

- ``SwiftUI/EnvironmentValues/validationConfiguration``
- ``SwiftUI/EnvironmentValues/validationDebounce``

### Visualizing Validation

- ``VerifiableTextField``
- ``ValidationResultsView``
- ``FailedValidationResult``
4 changes: 0 additions & 4 deletions Sources/SpeziValidation/ValidationEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ import SwiftUI
/// to manage the evaluation of your ``ValidationRule``s. The Engine provides easy access to bindings for current validity state of a the
/// processed input and a the respective recovery suggestions for failed ``ValidationRule``s.
/// The state of the `ValidationEngine` is updated on each invocation of ``runValidation(input:)`` or ``submit(input:debounce:)``.
///
/// TODO: docs on how to use it directly?
///
/// TODO: docs on how to consume outputs!
@Observable
public class ValidationEngine: Identifiable {
/// Determines the source of the last validation run.
Expand Down
71 changes: 26 additions & 45 deletions Sources/SpeziValidation/ValidationModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,19 @@ extension View {
/// This modifier can be used to validate a `String` input against a set of ``ValidationRule``s.
///
/// Validation is managed through a ``ValidationEngine`` instance that is injected as an `Observable` into the
/// environment. The modifier automatically calls ``ValidationEngine/submit(input:debounce:)`` on a change of the input.
/// environment.
///
/// Below is a short code example on how to use the modifier. We rely on ``VerifiableTextField`` to visualize potential validation errors.
/// ```swift
/// @State var phrase: String = ""
///
/// var body: some View {
/// Form {
/// VerifiableTextView("your favorite phrase", text: $phrase)
/// .validate(input: phrase, rules: .nonEmpty)
/// }
/// }
/// ```
///
/// - Parameters:
/// - value: The current value to validate.
Expand All @@ -68,56 +80,25 @@ extension View {
/// This modifier can be used to validate a `String` input against a set of ``ValidationRule``s.
///
/// Validation is managed through a ``ValidationEngine`` instance that is injected as an `Observable` into the
/// environment. The modifier automatically calls ``ValidationEngine/submit(input:debounce:)`` on a change of the input.
///
/// - Parameters:
/// - value: The current value to validate.
/// - rules: An variadic array of ``ValidationRule``s.
/// - Returns: The modified view.
public func validate(input value: String, rules: ValidationRule...) -> some View {
validate(input: value, rules: rules)
}

/*
TODO: remove?
/// Validate an input against a set of validation rules with automatic focus management.
///
/// This modifier can be used to validate a `String` input against a set of ``ValidationRule``s.
///
/// Validation is managed through a ``ValidationEngine`` instance that is injected as an `Observable` into the
/// environment. The modifier automatically calls ``ValidationEngine/submit(input:debounce:)`` on a change of the input.
/// environment.
///
/// - Parameters:
/// - value: The current value to validate.
/// - fieldIdentifier: The field identifier of the field that receives focus if validation fails.
/// - rules: An array of ``ValidationRule``s.
/// - Returns: The modified view.
public func validate<FocusValue: Hashable>(
input value: String,
field fieldIdentifier: FocusValue,
rules: [ValidationRule]
) -> some View {
modifier(ValidationModifier(input: value, field: fieldIdentifier, rules: rules))
}

/// Validate an input against a set of validation rules with automatic focus management.
///
/// This modifier can be used to validate a `String` input against a set of ``ValidationRule``s.
/// Below is a short code example on how to use the modifier. We rely on ``VerifiableTextField`` to visualize potential validation errors.
/// ```swift
/// @State var phrase: String = ""
///
/// Validation is managed through a ``ValidationEngine`` instance that is injected as an `Observable` into the
/// environment. The modifier automatically calls ``ValidationEngine/submit(input:debounce:)`` on a change of the input.
/// var body: some View {
/// Form {
/// VerifiableTextView("your favorite phrase", text: $phrase)
/// .validate(input: phrase, rules: .nonEmpty)
/// }
/// }
/// ```
///
/// - Parameters:
/// - value: The current value to validate.
/// - fieldIdentifier: The field identifier of the field that receives focus if validation fails.
/// - rules: An variadic array of ``ValidationRule``s.
/// - Returns: The modified view.
public func validate<FocusValue: Hashable>(
input value: String,
field fieldIdentifier: FocusValue,
rules: ValidationRule...
) -> some View {
validate(input: value, field: fieldIdentifier, rules: rules)
public func validate(input value: String, rules: ValidationRule...) -> some View {
validate(input: value, rules: rules)
}
*/
}
12 changes: 3 additions & 9 deletions Sources/SpeziValidation/ValidationRule+Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,7 @@ extension ValidationRule {

/// A `ValidationRule` that requires a password of at least 8 characters for minimal password complexity.
///
/// An application must make sure that users choose sufficiently secure passwords while at the same time ensuring that
/// usability is not affected due to too complex restrictions. This basic motivation stems from `ORP.4.A22 Regulating Password Quality`
/// of the [IT-Grundschutz Compendium](https://www.bsi.bund.de/EN/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/IT-Grundschutz/it-grundschutz_node.html)
/// of the German Federal Office for Information Security.
/// We propose to use the password length as the sole factor to determine password complexity. We rely on the
/// recommendations of NIST who discuss the [Strength of Memorized Secrets](https://pages.nist.gov/800-63-3/sp800-63b.html#appA)
/// great detail and recommend against password rules that mandated a certain mix of character types.
/// See ``ValidationRule`` for a discussion and recommendation on password complexity rules.
public static let minimalPassword: ValidationRule = {
guard let regex = try? Regex(#".{8,}"#) else {
fatalError("Failed to build the minimalPassword validation rule!")

Check warning on line 65 in Sources/SpeziValidation/ValidationRule+Defaults.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SpeziValidation/ValidationRule+Defaults.swift#L65

Added line #L65 was not covered by tests
Expand All @@ -80,7 +74,7 @@ extension ValidationRule {

/// A `ValidationRule` that requires a password of at least 10 characters for improved password complexity.
///
/// See ``minimalPassword`` for a discussion and recommendation on password complexity rules.
/// See ``ValidationRule`` for a discussion and recommendation on password complexity rules.
public static let mediumPassword: ValidationRule = {
guard let regex = try? Regex(#".{10,}"#) else {
fatalError("Failed to build the mediumPassword validation rule!")
Expand All @@ -95,7 +89,7 @@ extension ValidationRule {

/// A `ValidationRule` that requires a password of at least 10 characters for extended password complexity.
///
/// See ``minimalPassword`` for a discussion and recommendation on password complexity rules.
/// See ``ValidationRule`` for a discussion and recommendation on password complexity rules.
public static let strongPassword: ValidationRule = {
guard let regex = try? Regex(#".{12,}"#) else {
fatalError("Failed to build the strongPassword validation rule!")
Expand Down
29 changes: 29 additions & 0 deletions Sources/SpeziValidation/ValidationRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,38 @@ enum CascadingValidationEffect {
/// )
/// ```
///
/// Use the ``SwiftUI/View/validate(input:rules:)-5dac4`` modifier to apply a validation rule to a given `String` input.
///
/// ### Discussion on security-related client-side Validation
///
/// This discussion section briefly touches on important aspects when doing security-related, client-side validation and highlights
/// the importance of server-side validation to properly enforce restrictions.
///
/// - Important: Never rely on security-relevant validations with `ValidationRule`. These are client-side validations only!
/// Security-related validations MUST be checked at the server side (e.g., password length) and are just checked
/// on client-side for visualization.
///
/// #### Password Validation
///
/// An application must make sure that users choose sufficiently secure passwords
/// while ensuring at the same time that usability is not affected due to too complex restrictions.
/// This basic motivation stems from the section `ORP.4.A22 Regulating Password Quality`
/// of the [IT-Grundschutz Compendium](https://www.bsi.bund.de/EN/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/IT-Grundschutz/it-grundschutz_node.html)
/// of the German Federal Office for Information Security.
/// We propose to use the password length as the sole factor to determine password complexity. We rely on the
/// recommendations of NIST who discuss the [Strength of Memorized Secrets](https://pages.nist.gov/800-63-3/sp800-63b.html#appA)
/// in great detail and recommend against password rules that mandated a certain mix of character types.
///
/// ## Topics
///
/// ### Builtin Rules
/// - ``nonEmpty``
/// - ``unicodeLettersOnly``
/// - ``asciiLettersOnly``
/// - ``minimalEmail``
/// - ``minimalPassword``
/// - ``mediumPassword``
/// - ``strongPassword``
public struct ValidationRule: Identifiable, @unchecked Sendable, Equatable {
// we guarantee that the closure is only executed on the main thread
/// A unique identifier for the ``ValidationRule``. Can be used to, e.g., match a ``FailedValidationResult`` to the ValidationRule.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ extension View {
/// By supplying a binding to your declared ``ValidationState`` property, you can receive all changes to the
/// validation state of your child views.
///
/// - Note: This version of the modifier uses a [FocusState](https://developer.apple.com/documentation/SwiftUI/FocusState)
/// value of `Never`. Meaning, it will only capture validation modifier that do not specify a focus value.
/// When calling the ``ValidationContext/validateSubviews(switchFocus:)`` focus automatically switches to the
/// first field that failed validation.
///
/// - Parameter state: The binding to the ``ValidationState``.
/// - Returns: The modified view.
Expand All @@ -40,44 +40,4 @@ extension View {
state.wrappedValue = ValidationContext(entries: entries)
}
}
/*
TODO: remove?
/// Receive validation state of all subviews.
///
/// By supplying a binding to your declared ``ValidationState`` property, you can receive all changes to the
/// validation state of your child views.
///
/// - Note: While this modifier collects all validation state with the respective focus state value type, it doesn't
/// require to supply a [FocusState](https://developer.apple.com/documentation/SwiftUI/FocusState)
/// and, therefore, doesn't automatically switch focus on a failed validation.
/// For more information refer to the ``SwiftUI/View/receiveValidation(in:focus:)`` modifier.
///
/// - Parameter state: The binding to the ``ValidationState``.
/// - Returns: The modified view.
public func receiveValidation<Value>(in state: ValidationState<Value>.Binding) -> some View {
onPreferenceChange(CapturedValidationStateKey<Value>.self) { entries in
state.wrappedValue = ValidationContext(entries: entries)
}
}
*/
/*
/// Receive validation state of all subviews.
///
/// By supplying a binding to your declared ``ValidationState`` property, you can receive all changes to the
/// validation state of your child views.
///
/// This modifier uses the supplied [FocusState](https://developer.apple.com/documentation/SwiftUI/FocusState)
/// binding to automatically set focus to the first field that failed validation, once you manually
/// call ``ValidationContext/validateSubviews(switchFocus:)`` on your validation state property.
///
/// - Parameters:
/// - state: The binding to the ``ValidationState``.
/// - focus: A [FocusState](https://developer.apple.com/documentation/SwiftUI/FocusState) binding that will
/// be used to automatically set focus to the first field that failed validation.
/// - Returns: The modified view.
public func receiveValidation<Value>(in state: ValidationState<Value>.Binding, focus: FocusState<Value?>.Binding) -> some View {
onPreferenceChange(CapturedValidationStateKey<Value>.self) { entries in
state.wrappedValue = ValidationContext(entries: entries, focus: focus)
}
}*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public struct ValidationContext {
}
}

Check warning on line 55 in Sources/SpeziValidation/ValidationState/ValidationContext.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SpeziValidation/ValidationState/ValidationContext.swift#L51-L55

Added lines #L51 - L55 were not covered by tests

/// Flag that indicates if ``displayedValidationResults`` returns any ``FailedValidationResult``.
/// Flag that indicates if ``allDisplayedValidationResults`` returns any results.
///
/// Please refer to the documentation of ``ValidationEngine/isDisplayingValidationErrors``.
@MainActor
Expand Down
Loading

0 comments on commit 7cf51a6

Please sign in to comment.