diff --git a/Sources/ValidatorUI/Classes/IUIValidatable.swift b/Sources/ValidatorUI/Classes/IUIValidatable.swift
index 283bbcf..f8012be 100644
--- a/Sources/ValidatorUI/Classes/IUIValidatable.swift
+++ b/Sources/ValidatorUI/Classes/IUIValidatable.swift
@@ -26,6 +26,12 @@ public protocol IUIValidatable: AnyObject {
/// The input value that needs to be validated.
var inputValue: Input { get }
+ /// The most recent validation result, if any.
+ ///
+ /// This value is updated automatically when calling `validate(rules:)`.
+ /// Implementations may use this property to drive UI updates.
+ var validationResult: ValidationResult? { get }
+
/// Validates the input value using a single rule.
///
/// - Parameter rule: A validation rule conforming to `IValidationRule`.
@@ -48,9 +54,10 @@ public protocol IUIValidatable: AnyObject {
// MARK: - Associated Object Keys
-// Keys used for storing associated objects (validation rules and handlers)
+// Keys used for storing associated objects (validation rules, handlers, and a validation result)
private nonisolated(unsafe) var kValidationRules: UInt8 = 0
private nonisolated(unsafe) var kValidationHandler: UInt8 = 0
+private nonisolated(unsafe) var kValidationResult: UInt8 = 0
// Validator instance shared for UI validation
// swiftlint:disable:next prefixed_toplevel_constant
@@ -78,6 +85,7 @@ public extension IUIValidatable {
func validate(rules: [any IValidationRule]) -> ValidationResult {
let result = validator.validate(input: inputValue, rules: rules)
validationHandler?(result)
+ validationResult = result
return result
}
@@ -88,6 +96,24 @@ public extension IUIValidatable {
validationRules.append(rule)
}
+ /// The most recent validation result, if any.
+ ///
+ /// This value is updated automatically when calling `validate(rules:)`.
+ /// Implementations may use this property to drive UI updates.
+ private(set) var validationResult: ValidationResult? {
+ get {
+ (objc_getAssociatedObject(self, &kValidationResult) as? AnyObject) as? ValidationResult
+ }
+ set {
+ objc_setAssociatedObject(
+ self,
+ &kValidationResult,
+ newValue as ValidationResult?,
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
+ )
+ }
+ }
+
/// The array of validation rules associated with this UI element.
var validationRules: [any IValidationRule] {
get {
diff --git a/Sources/ValidatorUI/Classes/SUI/Managers/FormField/FormFieldManager/FormFieldManager.swift b/Sources/ValidatorUI/Classes/SUI/Managers/FormField/FormFieldManager/FormFieldManager.swift
index 31105cf..c276722 100644
--- a/Sources/ValidatorUI/Classes/SUI/Managers/FormField/FormFieldManager/FormFieldManager.swift
+++ b/Sources/ValidatorUI/Classes/SUI/Managers/FormField/FormFieldManager/FormFieldManager.swift
@@ -40,7 +40,6 @@ public final class FormFieldManager: IFormFieldManager {
/// The manager subscribes to the validator's publisher so that any changes
/// in validation results automatically trigger re-evaluation of the form's overall validity.
public func append(validator: some IFormValidationContainer) {
- // Subscribe to validation updates for this field
validator
.publisher
.sink(receiveValue: { [weak self] _ in