Fix for MultiErrors, not a final solution, but a stopgap until we can refactor all of the forms

This commit is contained in:
Bruce, Matt R 2022-03-11 16:29:45 +00:00 committed by Hedden, Kyle Matthew
parent 3e6c2f1702
commit cd6b03b64b
12 changed files with 169 additions and 131 deletions

View File

@ -583,6 +583,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 */
@ -1167,6 +1169,8 @@
EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldEffectProtocol.swift; sourceTree = "<group>"; };
EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideFormFieldEffectModel.swift; sourceTree = "<group>"; };
EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableFormFieldEffectModel.swift; sourceTree = "<group>"; };
EAB14BC027D935F00012AB2C /* RuleCompareModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleCompareModelProtocol.swift; sourceTree = "<group>"; };
EAB14BC227D9378D0012AB2C /* RuleAnyModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleAnyModelProtocol.swift; sourceTree = "<group>"; };
EABFC1402763BB8D00E78B40 /* FormLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabel.swift; sourceTree = "<group>"; };
EABFC151276913E800E78B40 /* FormLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormLabelModel.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -1212,12 +1216,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;
@ -2669,6 +2675,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 */,
@ -2872,6 +2879,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 */,

View File

@ -94,9 +94,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
}

View File

@ -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)
}
}
}

View File

@ -10,5 +10,5 @@
import Foundation
public protocol FormRuleWatcherFieldProtocol {
func setValidity(_ valid: Bool, rule: RulesProtocol)
func setValidity(_ valid: Bool, errorMessage: String?)
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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 let firstFormField = fieldMolecules[fields[0]],
let secondFormField = fieldMolecules[fields[1]], fields.count > 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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)