diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index a329075f..4ec18455 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -584,6 +584,8 @@ EAA0CFAF275E7D8000D65EB0 /* FormFieldEffectProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */; }; EAA0CFB1275E823A00D65EB0 /* HideFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */; }; EAA0CFB3275E831E00D65EB0 /* DisableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */; }; + EAB14BC127D935F00012AB2C /* RuleCompareModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */; }; + EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */; }; EABFC1412763BB8D00E78B40 /* FormLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* FormLabel.swift */; }; EABFC152276913E800E78B40 /* FormLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* FormLabelModel.swift */; }; /* End PBXBuildFile section */ @@ -1169,6 +1171,8 @@ EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldEffectProtocol.swift; sourceTree = ""; }; EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideFormFieldEffectModel.swift; sourceTree = ""; }; EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableFormFieldEffectModel.swift; sourceTree = ""; }; + EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleCompareModelProtocol.swift; sourceTree = ""; }; + EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = ""; }; EABFC1402763BB8D00E78B40 /* FormLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabel.swift; sourceTree = ""; }; EABFC151276913E800E78B40 /* FormLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabelModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1214,12 +1218,14 @@ children = ( 011D95A4240455DC000E3791 /* FormGroupRule.swift */, 011D958424042432000E3791 /* RulesProtocol.swift */, - 011D959A240451E3000E3791 /* RuleRequiredModel.swift */, - 011D959C2404536F000E3791 /* RuleAnyValueChangedModel.swift */, + EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */, + EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */, 011D959E240453A1000E3791 /* RuleAllValueChangedModel.swift */, - 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */, + 011D959A240451E3000E3791 /* RuleRequiredModel.swift */, 011D95A2240453F8000E3791 /* RuleRegexModel.swift */, + 011D959C2404536F000E3791 /* RuleAnyValueChangedModel.swift */, 0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */, + 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */, 0A849EFD246F1775009F277F /* RuleEqualsIgnoreCaseModel.swift */, ); name = Rules; @@ -2672,6 +2678,7 @@ D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */, D2B18B7F2360913400A9AEDC /* Control.swift in Sources */, D253BB8A24574CC5002DE544 /* StackModel.swift in Sources */, + EAB14BC127D935F00012AB2C /* RuleCompareModelProtocol.swift in Sources */, 011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */, BB2BF0EA2452A9BB001D0FC2 /* ListDeviceComplexButtonSmall.swift in Sources */, D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */, @@ -2876,6 +2883,7 @@ BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */, 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */, 323AC96C24C837FF00F8E4C4 /* ListThreeColumnBillChanges.swift in Sources */, + EAB14BC327D9378D0012AB2C /* RuleAnyModelProtocol.swift in Sources */, 0A0FEC7825D42A8500AF2548 /* BaseItemPickerEntryFieldModel.swift in Sources */, D28A837923C7D5BC00DFE4FC /* PageModelProtocol.swift in Sources */, D2351C7C24A4D4C3007DF0BC /* ListRightVariableToggleAllTextAndLinks.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift index 912b0891..399cedac 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitBox.swift @@ -38,29 +38,6 @@ import UIKit // Default dimensions of the DigitBox static let size: CGSize = CGSize(width: 39, height: 44) - //-------------------------------------------------- - // MARK: - Computed Properties - //-------------------------------------------------- - - public override var showError: Bool { - get { super.showError } - set (error) { - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - self.borderStrokeColor = error ? .mvmOrange : .mvmCoolGray3 - - let barHeight: CGFloat = self.showError ? 4 : 1 - self.bottomBar?.frame = CGRect(x: 0, y: self.bounds.height - barHeight, width: self.bounds.width, height: barHeight) - self.bottomBar?.backgroundColor = self.showError ? UIColor.mvmOrange.cgColor : UIColor.mvmBlack.cgColor - - self.setNeedsDisplay() - self.layoutIfNeeded() - } - super.showError = error - } - } - //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift index c70d458e..a0de9d1f 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/DigitEntryField.swift @@ -105,7 +105,7 @@ import UIKit super.isLocked = locked } } - + public override var placeholder: String? { get { var string = "" @@ -311,7 +311,6 @@ import UIKit //-------------------------------------------------- @objc override func startEditing() { - selectedDigitBox?.isSelected = true selectedDigitBox?.digitField.becomeFirstResponder() } @@ -328,7 +327,6 @@ import UIKit } @objc public override func dismissFieldInput(_ sender: Any?) { - digitBoxes.forEach { if $0.isSelected { $0.digitField.resignFirstResponder() @@ -398,7 +396,6 @@ extension DigitEntryField { digitEntryModel?.text = text return false } - return true } @@ -411,9 +408,9 @@ extension DigitEntryField { } @objc public func textFieldDidBeginEditing(_ textField: UITextField) { - digitBoxes.forEach { if $0.digitField === textField { + startEditing() selectedDigitBox = $0 $0.isSelected = true return @@ -429,15 +426,15 @@ extension DigitEntryField { } @objc public func textFieldDidEndEditing(_ textField: UITextField) { - // There should only be one digitBox to deselect. selectedDigitBox?.isSelected = false selectedDigitBox = nil if !switchFieldsAutomatically && validateWhenDoneEditing { validateText() + endInputing() } - + proprietorTextDelegate?.textFieldDidEndEditing?(textField) } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift index de7dfd74..405a0081 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/EntryFieldModel.swift @@ -96,9 +96,9 @@ import Foundation return text } - public func setValidity(_ valid: Bool, rule: RulesProtocol) { + public func setValidity(_ valid: Bool, errorMessage: String?) { - if let fieldKey = fieldKey, let ruleErrorMessage = rule.errorMessage?[fieldKey] { + if let ruleErrorMessage = errorMessage, fieldKey != nil { self.errorMessage = ruleErrorMessage } diff --git a/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift index 2c95f942..01835ed9 100644 --- a/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormFieldProtocol.swift @@ -29,3 +29,19 @@ public extension FormFieldProtocol { var baseValue: AnyHashable? { nil } } + +public class FormFieldValidity{ + public var fieldKey: String + public var valid: Bool = true + public var errorMessages: [String] = [] + + public init(_ fieldKey: String){ + self.fieldKey = fieldKey + } + + public func addError(message: String?){ + if let message = message { + self.errorMessages.append(message) + } + } +} diff --git a/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift b/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift index 3fc47d25..289bcb39 100644 --- a/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/FormRuleWatcherFieldProtocol.swift @@ -10,5 +10,5 @@ import Foundation public protocol FormRuleWatcherFieldProtocol { - func setValidity(_ valid: Bool, rule: RulesProtocol) + func setValidity(_ valid: Bool, errorMessage: String?) } diff --git a/MVMCoreUI/FormUIHelpers/FormValidator.swift b/MVMCoreUI/FormUIHelpers/FormValidator.swift index 42c385e8..bf2f8c2f 100644 --- a/MVMCoreUI/FormUIHelpers/FormValidator.swift +++ b/MVMCoreUI/FormUIHelpers/FormValidator.swift @@ -133,11 +133,10 @@ import MVMCore let tuple = group.validate(fields) //set the validity for the fields - group.rules.forEach { rule in - for formKey in rule.fields { - guard let formField = fields[formKey] as? FormRuleWatcherFieldProtocol, - let fieldValidity = tuple.fieldValidity[formKey] else { continue } - formField.setValidity(fieldValidity, rule: rule) + fields.forEach { (key: String, value: FormFieldProtocol) in + if let formField = value as? FormRuleWatcherFieldProtocol, + let fieldValidity = tuple.fieldValidity[key] { + formField.setValidity(fieldValidity.valid, errorMessage: fieldValidity.errorMessages.last) } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyModelProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyModelProtocol.swift new file mode 100644 index 00000000..bdfd937c --- /dev/null +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyModelProtocol.swift @@ -0,0 +1,45 @@ +// +// RuleAnyModelProtocol.swift +// MVMCoreUI +// +// Created by Matt Bruce on 3/9/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// RuleAnyModelProtocol was abstracted to 1 place since this code was currently +/// duplicated in 2 classes. +/// This protocol should be used for the rules that need to Loop through all of the fields +/// and if ANY formField's isValid == TRUE, the Rule is then TRUE +public protocol RuleAnyModelProtocol: RulesProtocol{} + +extension RuleAnyModelProtocol { + + /// Overriding the RulesProtocol default implementation + public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: FormFieldValidity]) -> (valid: Bool, fieldValidity: [String: FormFieldValidity]) { + + for formKey in fields { + guard let formField = fieldMolecules[formKey] else { continue } + + var fieldValidity = isValid(formField) + // If past rule is invalid for a field, the current rule should not flip the validity of a field + if let validity = previousFieldValidity[formKey], !validity.valid, fieldValidity { + fieldValidity = false + } + + // If TRUE the RULE is TRUE, even if there are many fields to check. + if fieldValidity { + return (true, previousFieldValidity) + } + } + + // if the rule breaks all fields should be set to false + fields.forEach { (formKey) in + previousFieldValidity[formKey]?.valid = false + previousFieldValidity[formKey]?.addError(message: errorMessage?[formKey]) + } + return (valid: false, fieldValidity: previousFieldValidity) + } + +} diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift index 2b958d3b..38dd6669 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyRequiredModel.swift @@ -8,8 +8,7 @@ import UIKit - -public class RuleAnyRequiredModel: RulesProtocol { +public class RuleAnyRequiredModel: RuleAnyModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -37,27 +36,4 @@ public class RuleAnyRequiredModel: RulesProtocol { return false } - public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) { - - var previousValidity: [String: Bool] = [:] - for formKey in fields { - guard let formField = fieldMolecules[formKey] else { continue } - - var fieldValidity = isValid(formField) - // If past rule is invalid for a field, the current rule should not flip the validity of a field - if let validity = previousFieldValidity[formKey], !validity, fieldValidity { - fieldValidity = false - } - - if fieldValidity { - return (fieldValidity, previousValidity) - } - } - - // if the rule breaks all fields should be set to false - fields.forEach { (formKey) in - previousValidity[formKey] = false - } - return (valid: false, fieldValidity: previousValidity) - } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift index 09e91ceb..1d2c8faf 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleAnyValueChangedModel.swift @@ -8,8 +8,7 @@ import Foundation - -public class RuleAnyValueChangedModel: RulesProtocol { +public class RuleAnyValueChangedModel: RuleAnyModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -27,26 +26,4 @@ public class RuleAnyValueChangedModel: RulesProtocol { public func isValid(_ formField: FormFieldProtocol) -> Bool { return formField.baseValue != formField.formFieldValue() } - - public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) { - var previousValidity: [String: Bool] = [:] - for formKey in fields { - guard let formField = fieldMolecules[formKey] else { continue } - var fieldValidity = isValid(formField) - // If past rule is invalid for a field, the current rule should not flip the validity of a field - if let validity = previousFieldValidity[formKey], !validity, fieldValidity { - fieldValidity = false - } - - if fieldValidity { - return (true, previousValidity) - } - } - - // if the rule breaks all fields should be set to false - fields.forEach { (formKey) in - previousValidity[formKey] = false - } - return (valid: false, fieldValidity: previousValidity) - } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleCompareModelProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleCompareModelProtocol.swift new file mode 100644 index 00000000..42022636 --- /dev/null +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleCompareModelProtocol.swift @@ -0,0 +1,55 @@ +// +// RuleCompareModelProtocol.swift +// MVMCoreUI +// +// Created by Matt Bruce on 3/9/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation + +/// RuleCompareModelProtocol is meant to be used for rules that compare +/// 2 FormField's values. It is up to the Implementing class to determine what +/// occurs within the "compare" method that is required. This can be anything +/// that returns a Bool. More than likley it will be a <,>, <=, =>, == comparison. +public protocol RuleCompareModelProtocol: RulesProtocol { + associatedtype CompareType + func compare(lhs: CompareType?, rhs: CompareType?) -> Bool +} + +extension RuleCompareModelProtocol { + + /// This overrides the RulesProtocol default implementation to then use your class implementation's "compare" method. + /// A requirement of this rule is that the fields array contains at least 2 fieldKeys and it will pull out these formFields with ONLY + /// the first 2 keys to send to the compare method your class will be implementing. + public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: FormFieldValidity]) -> (valid: Bool, fieldValidity: [String: FormFieldValidity]) { + var valid = false + + guard fields.count > 1, let firstFormField = fieldMolecules[fields[0]], + let secondFormField = fieldMolecules[fields[1]] else { + return (valid: true, previousFieldValidity) + } + + let result = compare(lhs: firstFormField.formFieldValue() as? CompareType, rhs: secondFormField.formFieldValue() as? CompareType) + + let formKey = fields[1] + + //only check the 2nd value + if result { + valid = true + + // If past rule is invalid for a field, the current rule should not flip the validity of a field + if let validity = previousFieldValidity[formKey], !validity.valid, valid { + valid = false + } + previousFieldValidity[formKey]?.valid = valid + + } else { //false + previousFieldValidity[formKey]?.valid = valid + previousFieldValidity[formKey]?.addError(message: errorMessage?[formKey]) + } + + return (valid: valid, fieldValidity: previousFieldValidity) + + } +} diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift index 95149551..4149737e 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsIgnoreCaseModel.swift @@ -9,7 +9,7 @@ import Foundation -public class RuleEqualsIgnoreCaseModel: RulesProtocol { +public class RuleEqualsIgnoreCaseModel: RuleCompareModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -27,35 +27,12 @@ public class RuleEqualsIgnoreCaseModel: RulesProtocol { public func isValid(_ formField: FormFieldProtocol) -> Bool { return false } - - public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) { - var valid = false - var compareText: String? - - var previousValidity: [String: Bool] = [:] - for formKey in fields { - guard let formField = fieldMolecules[formKey] else { continue } - - guard let compareString = compareText else { - compareText = formField.formFieldValue() as? String - continue - } - - if let fieldValue = formField.formFieldValue() as? String, - compareString.caseInsensitiveCompare(fieldValue) == .orderedSame { - valid = true - - var fieldValidity = valid - // If past rule is invalid for a field, the current rule should not flip the validity of a field - if let validity = previousFieldValidity[formKey], !validity, fieldValidity { - fieldValidity = false - } - previousValidity[formKey] = valid - break - } - - previousValidity[formKey] = valid + + ///RuleCompareModelProtocol Method + public func compare(lhs: String?, rhs: String?) -> Bool { + guard let rhs = rhs else { + return false } - return (valid: valid, fieldValidity: previousValidity) + return lhs?.caseInsensitiveCompare(rhs) == .orderedSame } } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift index aa07cb29..c8a8aa56 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RuleEqualsModel.swift @@ -8,8 +8,7 @@ import Foundation - -public class RuleEqualsModel: RulesProtocol { +public class RuleEqualsModel: RuleCompareModelProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -27,33 +26,10 @@ public class RuleEqualsModel: RulesProtocol { public func isValid(_ formField: FormFieldProtocol) -> Bool { return false } - - public func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) { - var valid = true - var compareValue: AnyHashable? - var previousValidity: [String: Bool] = [:] - - for formKey in fields { - guard let formField = fieldMolecules[formKey] else { continue } - - if compareValue == nil { - compareValue = formField.formFieldValue() - continue - } - - if compareValue != formField.formFieldValue() { - valid = false - previousValidity[formKey] = valid - break - } else { - var fieldValidity = valid - // If past rule is invalid for a field, the current rule should not flip the validity of a field - if let validity = previousFieldValidity[formKey], !validity, fieldValidity { - fieldValidity = false - } - previousValidity[formKey] = valid - } - } - return (valid: valid, fieldValidity: previousValidity) + + ///RuleCompareModelProtocol Method + public func compare(lhs: AnyHashable?, rhs: AnyHashable?) -> Bool { + return lhs == rhs } + } diff --git a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift index 8e158676..d1326ba9 100644 --- a/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift +++ b/MVMCoreUI/FormUIHelpers/Rules/Rules/RulesProtocol.swift @@ -30,7 +30,7 @@ public protocol RulesProtocol: ModelProtocol { func isValid(_ formField: FormFieldProtocol) -> Bool // Validates the rule and returns the result. - func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) + func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: FormFieldValidity]) -> (valid: Bool, fieldValidity: [String: FormFieldValidity]) } public extension RulesProtocol { @@ -42,21 +42,30 @@ public extension RulesProtocol { static var categoryName: String { "\(RulesProtocol.self)" } // Individual rule can override the function to validate based on the rule type. - func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: Bool]) -> (valid: Bool, fieldValidity: [String: Bool]) { + func validate(_ fieldMolecules: [String: FormFieldProtocol],_ previousFieldValidity: [String: FormFieldValidity]) -> (valid: Bool, fieldValidity: [String: FormFieldValidity]) { var valid = true - var previousValidity: [String: Bool] = [:] for formKey in fields { guard let formField = fieldMolecules[formKey] else { continue } + //check the field isValid var fieldValidity = isValid(formField) + + //add the error message if it exists + if fieldValidity == false { + previousFieldValidity[formKey]?.addError(message: errorMessage?[formKey]) + } + // If past rule is invalid for a field, the current rule should not flip the validity of a field - if let validity = previousFieldValidity[formKey], !validity, fieldValidity { + if let validity = previousFieldValidity[formKey], !validity.valid, fieldValidity { fieldValidity = false } + //set the valid for the field + previousFieldValidity[formKey]?.valid = fieldValidity + + //set the full validity valid = valid && fieldValidity - previousValidity[formKey] = fieldValidity } - return (valid: valid, fieldValidity: previousValidity) + return (valid: valid, fieldValidity: previousFieldValidity) } } @@ -73,16 +82,16 @@ public extension RulesContainerProtocol { /// - Returns: Tuple(valid, fieldValidity) /// - valid: bool for all rules /// - fieldValidity: accumulation of all fieldKey: valid - func validate(_ fields: [String: FormFieldProtocol]) -> (valid: Bool, fieldValidity: [String:Bool] ) { + func validate(_ fields: [String: FormFieldProtocol]) -> (valid: Bool, fieldValidity: [String:FormFieldValidity]) { // Validate each rule. var valid = true - var previousValidity: [String: Bool] = [:] + var previousValidity: [String: FormFieldValidity] = [:] + fields.keys.forEach { key in + previousValidity[key] = FormFieldValidity(key) + } for rule in self.rules { //validate the rule against the fields let tuple = rule.validate(fields, previousValidity) - - //merge the fieldValidity - previousValidity = previousValidity.merging(tuple.fieldValidity) { (_, new) in new } valid = valid && tuple.valid } return (valid: valid, fieldValidity: previousValidity)