FormFieldEffects Update

This commit is contained in:
Bruce, Matt R 2021-12-14 21:18:37 +00:00
parent 12331e284b
commit 87195296cc
40 changed files with 716 additions and 91 deletions

View File

@ -571,6 +571,13 @@
DBEFFA04225A829700230692 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB891E822253FA8500022516 /* Label.swift */; };
EA5124FD243601600051A3A4 /* BGImageHeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */; };
EA5124FF2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */; };
EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */; };
EA7E67762758365300ABF773 /* UIUpdatableModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */; };
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 */; };
EABFC1412763BB8D00E78B40 /* StateLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC1402763BB8D00E78B40 /* StateLabel.swift */; };
EABFC152276913E800E78B40 /* StateLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFC151276913E800E78B40 /* StateLabelModel.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -1141,6 +1148,13 @@
DBC4391A224421A0001AB423 /* CaretLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretLink.swift; sourceTree = "<group>"; };
EA5124FC243601600051A3A4 /* BGImageHeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButton.swift; sourceTree = "<group>"; };
EA5124FE2436018E0051A3A4 /* BGImageHeadlineBodyButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGImageHeadlineBodyButtonModel.swift; sourceTree = "<group>"; };
EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnableFormFieldEffectModel.swift; sourceTree = "<group>"; };
EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIUpdatableModelProtocol.swift; sourceTree = "<group>"; };
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>"; };
EABFC1402763BB8D00E78B40 /* StateLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateLabel.swift; sourceTree = "<group>"; };
EABFC151276913E800E78B40 /* StateLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateLabelModel.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -1206,6 +1220,7 @@
01C74D87224298E2009C25A3 /* FormUIHelpers */ = {
isa = PBXGroup;
children = (
EAA0CFAD275E7D5A00D65EB0 /* FormFieldEffect */,
011D95882404249B000E3791 /* FormHolderModelProtocol.swift */,
011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */,
011D95AA2405C553000E3791 /* FormItemProtocol.swift */,
@ -1213,6 +1228,7 @@
011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */,
011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */,
0105618A224BBE7700E1557D /* FormValidator.swift */,
EA7E67752758365300ABF773 /* UIUpdatableModelProtocol.swift */,
011D958A24042794000E3791 /* Rules */,
);
path = FormUIHelpers;
@ -1428,6 +1444,8 @@
94C2D9A823872E5E0006CF46 /* LabelAttributeImageModel.swift */,
94C2D9AA23872EB50006CF46 /* LabelAttributeActionModel.swift */,
DB891E822253FA8500022516 /* Label.swift */,
EABFC151276913E800E78B40 /* StateLabelModel.swift */,
EABFC1402763BB8D00E78B40 /* StateLabel.swift */,
);
path = Label;
sourceTree = "<group>";
@ -2400,6 +2418,17 @@
path = Alerts;
sourceTree = "<group>";
};
EAA0CFAD275E7D5A00D65EB0 /* FormFieldEffect */ = {
isa = PBXGroup;
children = (
EAA0CFAE275E7D8000D65EB0 /* FormFieldEffectProtocol.swift */,
EA7E67732758310500ABF773 /* EnableFormFieldEffectModel.swift */,
EAA0CFB2275E831E00D65EB0 /* DisableFormFieldEffectModel.swift */,
EAA0CFB0275E823A00D65EB0 /* HideFormFieldEffectModel.swift */,
);
path = FormFieldEffect;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -2538,6 +2567,7 @@
324FB6AA249366F3002552C7 /* ListLeftVariableNumberedListBodyTextModel.swift in Sources */,
5248BFED23F12E350059236A /* ListThreeColumnPlanDataDividerModel.swift in Sources */,
AA0A257824766C8A00862F64 /* ListLeftVariableIconWithRightCaretBodyTextModel.swift in Sources */,
EABFC152276913E800E78B40 /* StateLabelModel.swift in Sources */,
0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */,
0AF60F0926B3316E00AC3DB4 /* MVMCoreUIUtility+Extension.swift in Sources */,
8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */,
@ -2606,6 +2636,7 @@
D20C700B250BFDE40095B21C /* MVMCoreUITopAlertView+Extension.swift in Sources */,
D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */,
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */,
EA7E67742758310500ABF773 /* EnableFormFieldEffectModel.swift in Sources */,
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
942C378C2412F4FA0066E45E /* ModalMoleculeListTemplate.swift in Sources */,
BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */,
@ -2748,6 +2779,7 @@
D2CAC7CD251104FE00C75681 /* NotificationModel.swift in Sources */,
D2ED2803254B0E0300A1C293 /* MVMCoreAlertHandler.m in Sources */,
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */,
EAA0CFAF275E7D8000D65EB0 /* FormFieldEffectProtocol.swift in Sources */,
D20923592450ECE00044AD09 /* TableView.swift in Sources */,
BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */,
AA69AAF82445BF6800AF3D3B /* ListLeftVariableCheckboxBodyTextModel.swift in Sources */,
@ -2959,6 +2991,7 @@
013F801923FB4A8E00AD8013 /* UIContentMode+Extension.swift in Sources */,
AA104AC724472DB0004D2810 /* HeadersH1Button.swift in Sources */,
525239C22407BD1000454969 /* ListTwoColumnPriceDetails.swift in Sources */,
EABFC1412763BB8D00E78B40 /* StateLabel.swift in Sources */,
AA997252247530B100FC7472 /* ListLeftVariableIconAllTextLinks.swift in Sources */,
D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */,
D23EA7FB2475F09800D60C34 /* CarouselItemProtocol.swift in Sources */,
@ -2968,12 +3001,14 @@
27F973532466074500CAB5C5 /* PageBehaviorProtocol.swift in Sources */,
94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */,
D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */,
EAA0CFB3275E831E00D65EB0 /* DisableFormFieldEffectModel.swift in Sources */,
D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */,
D23EA7FE247EBBB700D60C34 /* NavigationLabelButtonModel.swift in Sources */,
D28A839123CD4FD400DFE4FC /* CornerLabelsModel.swift in Sources */,
012A88F123985E0100FE3DA1 /* Color.swift in Sources */,
D22D8393241C27B100D3DF69 /* TemplateModel.swift in Sources */,
012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */,
EAA0CFB1275E823A00D65EB0 /* HideFormFieldEffectModel.swift in Sources */,
D23A8FFB26123189007E14CE /* PageBehaviorModelProtocol.swift in Sources */,
52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */,
D2ED2812254B0EB800A1C293 /* MVMCoreTopAlertObject.m in Sources */,
@ -3040,6 +3075,7 @@
011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */,
D23A9004261234CE007E14CE /* PageBehaviorHandlerProtocol.swift in Sources */,
01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */,
EA7E67762758365300ABF773 /* UIUpdatableModelProtocol.swift in Sources */,
D2169301251E51E7002A6324 /* SectionListTemplate.swift in Sources */,
0A6682AA2435125F00AD3CA1 /* Styler.swift in Sources */,
D264FAA7243FE13B00D98315 /* RadioBox.swift in Sources */,

View File

@ -11,7 +11,7 @@ import UIKit
public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?)
open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol, EnableableModelProtocol {
open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -59,12 +59,6 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
public func setValidity(_ valid: Bool, group: FormGroupRule) {
enabled = valid
updateUI?()
}
/// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: ActionBlock?

View File

@ -8,7 +8,7 @@
import Foundation
open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol, EnableableModelProtocol {
open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -25,11 +25,6 @@ open class ImageButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGro
public var disabledTintColor: Color?
public var groupName: String = ""
public func setValidity(_ valid: Bool, group: FormGroupRule) {
enabled = valid
updateUI?()
}
public var updateUI: ActionBlock?

View File

@ -21,6 +21,7 @@
//--------------------------------------------------
public override func formFieldValue() -> AnyHashable? {
guard enabled else { return nil }
guard !options.isEmpty,
let index = selectedIndex
else { return nil }

View File

@ -25,7 +25,7 @@ import Foundation
//--------------------------------------------------
public override func formFieldValue() -> AnyHashable? {
guard enabled else { return nil }
guard !components.isEmpty && !selectedIndexes.isEmpty else { return nil }
return selectedRowText

View File

@ -17,11 +17,8 @@ import UIKit
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public private(set) var titleLabel: Label = {
let label = Label()
label.font = Styler.Font.RegularMicro.getFont()
label.textColor = .mvmBlack
public private(set) var titleLabel: StateLabel = {
let label = StateLabel()
label.setContentCompressionResistancePriority(.required, for: .vertical)
return label
}()
@ -29,10 +26,8 @@ import UIKit
public private(set) var entryFieldContainer = EntryFieldContainer()
/// Provides contextual information on the TextField.
public private(set) var feedbackLabel: Label = {
let label = Label()
label.font = Styler.Font.RegularMicro.getFont()
label.textColor = .mvmBlack
public private(set) var feedbackLabel: StateLabel = {
let label = StateLabel()
label.setContentCompressionResistancePriority(.required, for: .vertical)
return label
}()
@ -46,7 +41,6 @@ import UIKit
//--------------------------------------------------
// MARK: - Stored Properties
//--------------------------------------------------
public var isValid: Bool = false
/// Validate on each entry in the textField. Default: true
@ -60,10 +54,15 @@ import UIKit
public var isEnabled: Bool {
get { entryFieldContainer.isEnabled }
set (enabled) {
titleLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
feedbackLabel.textColor = enabled ? .mvmBlack : .mvmCoolGray3
entryFieldContainer.isEnabled = enabled
entryFieldModel?.enabled = enabled
if(entryFieldContainer.isEnabled != enabled){
titleLabel.isEnabled = enabled
feedbackLabel.isEnabled = enabled
entryFieldContainer.isEnabled = enabled
entryFieldModel?.enabled = enabled
if !enabled {
self.text = defaultText
}
}
}
}
@ -71,8 +70,11 @@ import UIKit
public var showError: Bool {
get { entryFieldContainer.showError }
set (error) {
feedback = error ? errorMessage : entryFieldModel?.feedback
feedbackLabel.textColor = error ? entryFieldModel?.errorTextColor?.uiColor ?? .mvmBlack : .mvmBlack
if error {
feedbackLabel.showError(message: self.errorMessage ?? "")
} else {
feedbackLabel.reset()
}
entryFieldContainer.showError = error
entryFieldModel?.showError = error
}
@ -104,11 +106,11 @@ import UIKit
public var title: String? {
get { titleLabel.text }
set {
titleLabel.text = newValue
titleLabel.set(text: newValue ?? "")
setAccessibilityString(newValue)
}
}
/// Override this to conveniently get/set the textfield(s).
public var text: String? {
get { nil }
@ -119,7 +121,7 @@ import UIKit
public var feedback: String? {
get { feedbackLabel.text }
set {
feedbackLabel.text = newValue
feedbackLabel.set(text: newValue ?? "")
feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true
}
}
@ -128,6 +130,9 @@ import UIKit
model as? EntryFieldModel
}
///This is the value of the entryFieldModel.text on set
///This is used to update the text value on a reset or when enabled is set to false
private var defaultText: String = ""
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
@ -299,10 +304,17 @@ import UIKit
super.set(with: model, delegateObject, additionalData)
self.delegateObject = delegateObject
guard let model = model as? EntryFieldModel else { return }
guard let model = entryFieldModel else { return }
entryFieldContainer.set(with: model, delegateObject, additionalData)
//setup the properties for setting models
//later with enabled/showError
titleLabel.setup(model: model.titleStateLabel, delegateObject, additionalData)
feedbackLabel.setup(model: model.feedbackStateLabel, delegateObject, additionalData)
isEnabled = model.enabled
defaultText = model.text ?? ""
model.updateUI = { [weak self] in
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let self = self else { return }
@ -313,6 +325,7 @@ import UIKit
} else if model.isValid ?? true && self.showError {
self.showError = false
}
self.isEnabled = model.enabled
})
}
@ -328,13 +341,10 @@ import UIKit
self.updateValidation(validState)
})
}
title = model.title
feedback = model.feedback
isEnabled = model.enabled
entryFieldContainer.disableAllBorders = model.hideBorders
accessibilityIdentifier = model.accessibilityIdentifier ?? model.fieldKey
if let isLocked = model.locked {
self.isLocked = isLocked
@ -364,3 +374,11 @@ extension EntryField {
// To Be Overriden
}
}
extension LabelModel {
public convenience init(fontStyle: Styler.Font, textColor: Color) {
self.init(text: "")
self.fontStyle = fontStyle
self.textColor = textColor
}
}

View File

@ -9,7 +9,8 @@
import Foundation
@objcMembers open class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol, EnableableModelProtocol {
@objcMembers open class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol, UIUpdatableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -18,8 +19,6 @@ import Foundation
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var title: String?
public var feedback: String?
public var shouldClearText: Bool = false
public var dynamicErrorMessage: String? {
didSet {
@ -40,6 +39,20 @@ import Foundation
public var baseValue: AnyHashable?
public var wasInitiallySelected: Bool = false
//text only
//Used for re-encoding what was decoded
private var title: String?
private var feedback: String?
//label models
//Used for re-encoding what was decoded
private var titleLabel: LabelModel?
private var feedbackLabel: LabelModel?
//used to drive the EntryFieldView UI
public var titleStateLabel: StateLabelModel
public var feedbackStateLabel: StateLabelModel
public var isValid: Bool? = true {
didSet { updateUI?() }
}
@ -70,6 +83,8 @@ import Foundation
case text
case fieldKey
case groupName
case titleLabel
case feedbackLabel
}
//--------------------------------------------------
@ -77,6 +92,8 @@ import Foundation
//--------------------------------------------------
public func formFieldValue() -> AnyHashable? {
guard enabled else { return nil }
if dynamicErrorMessage != nil {
dynamicErrorMessage = nil
}
@ -92,7 +109,7 @@ import Foundation
self.isValid = valid
updateUI?()
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
@ -100,6 +117,8 @@ import Foundation
public init(with text: String) {
self.text = text
baseValue = text
self.titleStateLabel = StateLabelModel(text: "")
self.feedbackStateLabel = StateLabelModel(text: "")
}
//--------------------------------------------------
@ -121,10 +140,24 @@ import Foundation
hideBorders = try typeContainer.decodeIfPresent(Bool.self, forKey: .hideBorders) ?? false
baseValue = text
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
titleLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .titleLabel)
feedbackLabel = try typeContainer.decodeIfPresent(LabelModel.self, forKey: .feedbackLabel)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
//Setup the stateLabelModels
if let titleLabel = titleLabel {
self.titleStateLabel = StateLabelModel(model: titleLabel)
} else {
self.titleStateLabel = StateLabelModel(text: title ?? "")
}
if let feedBackLabel = feedbackLabel {
self.feedbackStateLabel = StateLabelModel(model: feedBackLabel)
} else { //feedback is the model for the error
self.feedbackStateLabel = StateLabelModel(text: feedback ?? "")
}
}
public func encode(to encoder: Encoder) throws {
@ -133,7 +166,9 @@ import Foundation
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(title, forKey: .title)
try container.encodeIfPresent(titleLabel, forKey: .titleLabel)
try container.encodeIfPresent(feedback, forKey: .feedback)
try container.encodeIfPresent(feedbackLabel, forKey: .feedbackLabel)
try container.encodeIfPresent(text, forKey: .text)
try container.encodeIfPresent(locked, forKey: .locked)
try container.encodeIfPresent(showError, forKey: .showError)
@ -142,6 +177,7 @@ import Foundation
try container.encodeIfPresent(errorMessage, forKey: .errorMessage)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encode(enabled, forKey: .enabled)
try container.encode(hideBorders, forKey: .hideBorders)
}

View File

@ -423,6 +423,13 @@ import MVMCore
checkAndBypassAnimations(selected: model.selected)
}
model.updateUI = { [weak self] in
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let self = self else { return }
self.isEnabled = model.enabled
})
}
isEnabled = model.enabled
if (model.action != nil || model.offAction != nil) {

View File

@ -13,7 +13,8 @@
var selected: Bool { get set }
}
@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModelProtocol, FormFieldProtocol {
@objcMembers public class CheckboxModel: MoleculeModelProtocol, SelectableMoleculeModelProtocol, FormFieldProtocol, UIUpdatableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -22,7 +23,7 @@
public var backgroundColor: Color?
public var accessibilityIdentifier: String?
public var selected: Bool = false
public var enabled: Bool = true
public var enabled: Bool = true
public var animated: Bool = true
public var inverted: Bool = false
public var round: Bool = false
@ -42,6 +43,7 @@
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var updateUI: ActionBlock?
//--------------------------------------------------
// MARK: - Keys
@ -75,7 +77,10 @@
// MARK: - Form Validation
//--------------------------------------------------
public func formFieldValue() -> AnyHashable? { selected }
public func formFieldValue() -> AnyHashable? {
guard enabled else { return nil }
return selected
}
//--------------------------------------------------
// MARK: - Initializer

View File

@ -6,7 +6,7 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
@objcMembers public class RadioBoxModel: MoleculeModelProtocol {
@objcMembers public class RadioBoxModel: MoleculeModelProtocol, EnableableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------

View File

@ -20,6 +20,7 @@
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var enabled: Bool = true
//--------------------------------------------------
// MARK: - Methods
@ -39,6 +40,7 @@
private enum CodingKeys: String, CodingKey {
case moleculeName
case enabled
case selectedAccentColor
case backgroundColor
case accessibilityIdentifier
@ -63,6 +65,9 @@
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
self.enabled = enabled
}
baseValue = formFieldValue()
}

View File

@ -17,7 +17,8 @@
private var selectedRadioButton: RadioButton?
private var selectedRadioButtonModel: RadioButtonModel?
public var baseValue: AnyHashable?
public var enabled: Bool = true
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
@ -39,6 +40,7 @@
} else {
radioButton.isSelected = false
}
self.enabled = radioButtonModel.enabled
}
//--------------------------------------------------

View File

@ -7,7 +7,7 @@
//
@objcMembers public class RadioSwatchModel: MoleculeModelProtocol {
@objcMembers public class RadioSwatchModel: MoleculeModelProtocol, EnableableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------

View File

@ -19,7 +19,8 @@
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var enabled: Bool = true
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
@ -43,6 +44,7 @@
case swatches
case fieldKey
case groupName
case enabled
}
//--------------------------------------------------
@ -58,6 +60,9 @@
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
self.enabled = enabled
}
baseValue = formFieldValue()
}

View File

@ -7,7 +7,7 @@
//
public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableModelProtocol {
public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------

View File

@ -9,7 +9,7 @@
import UIKit
open class ArrowModel: MoleculeModelProtocol {
open class ArrowModel: MoleculeModelProtocol, EnableableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------

View File

@ -9,7 +9,7 @@
import Foundation
open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelProtocol {
open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelProtocol, EnableableModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------

View File

@ -238,7 +238,7 @@ public typealias ActionBlock = () -> ()
case left
}
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject? = nil, _ additionalData: [AnyHashable: Any]? = nil) {
clauses = []
text = nil
@ -584,9 +584,8 @@ public typealias ActionBlock = () -> ()
/// Will remove the values contained in attributedText.
func clearAttributes() {
guard let labelText = text,
let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: labelText.count))
guard let labelText = text, !labelText.isEmpty else { return }
guard let attributes = attributedText?.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: labelText.count))
else { return }
let attributedString = NSMutableAttributedString(string: labelText)

View File

@ -0,0 +1,70 @@
//
// StateLabel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/10/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
/// Subclass of label that helps with different states
public class StateLabel: Label {
//properties used in setting label
private var delegateObject: MVMCoreUIDelegateObject?
private var additionalData: [AnyHashable: Any]?
//models that drive the label UI
private var stateModel: StateLabelModel!
//public properties
public override var isEnabled: Bool {
didSet{
self.state = isEnabled ? .enabled : .disabled
}
}
public override func reset(){
super.reset()
self.state = .enabled
}
//current mode of label
public var state: StateLabelModel.State {
get {
return self.stateModel.state
}
set {
self.stateModel.state = newValue
self.set(with: stateModel.model, delegateObject, additionalData)
}
}
/// Used in setting up the Label for use
/// - Parameters:
/// - text: If there is no Model, there should be a TEXT only version of the label that will be applied to the default font / color styles
/// - model: Model takes priority over a text value. The model has its own text value that will be looked at to draw the screen, this model is used for both enabled/disabled models
/// - delegateObject: passed in from the creator
/// - additionalData: passed in from the creator
public func setup(model: StateLabelModel, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?){
self.additionalData = additionalData
self.delegateObject = delegateObject
self.stateModel = model
//default to enabled state
self.reset()
}
/// Use this to switch the label into a error state
/// - Parameter message: message to show in the errorModel
public func showError(message: String){
self.stateModel.set(text: message, for: .error)
self.state = .error
}
/// Text change that will update both enabledModel and disabledModel text values
/// - Parameter text: text you want to see
public func set(text: String?){
self.stateModel.set(text: text ?? "", for: .enabled)
}
}

View File

@ -0,0 +1,91 @@
//
// StateLabelModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/14/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public class StateLabelModel {
static let defaultFontStyle = Styler.Font.RegularMicro
static let defaultEnabledTextColor = Color(uiColor: .mvmBlack)
static let defaultDisabledTextColor = Color(uiColor: .mvmCoolGray3)
static let defaultErrorTextColor = Color(uiColor: .mvmBlack)
private var enabledModel: LabelModel
private var disabledModel = LabelModel(fontStyle: StateLabelModel.defaultFontStyle, textColor: StateLabelModel.defaultDisabledTextColor)
private var errorLabelModel = LabelModel(fontStyle: StateLabelModel.defaultFontStyle, textColor: StateLabelModel.defaultErrorTextColor)
public enum State {
case enabled
case disabled
case error
}
//current state
public var state: State = .enabled
//model is based on current state
public var model: LabelModel {
switch state {
case .enabled:
return enabledModel
case .disabled:
return disabledModel
case .error:
return errorLabelModel
}
}
//init
public init(model: LabelModel){
//ensure the fontStyle exist and if so,
//make sure the disabledModel font matches
//otherwise set it to defaultFontStyle
if let modelFontStyle = model.fontStyle {
self.disabledModel.fontStyle = modelFontStyle
} else {
model.fontStyle = StateLabelModel.defaultFontStyle
}
//ensure the textColor is set
//otherwise use the defaultEnabledTextColor
if model.textColor == nil {
model.textColor = StateLabelModel.defaultEnabledTextColor
}
//set the enabledModel to the model passed in
self.enabledModel = model
//make sure the enabled & disabled text match
self.disabledModel.text = self.enabledModel.text
}
public init(text: String){
//create the enabled model
self.enabledModel = LabelModel(fontStyle: StateLabelModel.defaultFontStyle, textColor: StateLabelModel.defaultEnabledTextColor)
self.enabledModel.text = text
//make sure the enabled & disabled text match
self.disabledModel.text = self.enabledModel.text
}
//methods
public func reset(){
self.state = .enabled
}
//set text for state
public func set(text:String, for state: State){
switch state {
case .enabled, .disabled:
self.enabledModel.text = text
self.disabledModel.text = text
case .error:
self.errorLabelModel.text = text
}
}
}

View File

@ -37,7 +37,8 @@ import UIKit
public var baseValue: AnyHashable?
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var enabled: Bool = true
public var selectable = false
public var selectedIndex: Int?

View File

@ -9,6 +9,6 @@
import Foundation
public protocol EnableableModelProtocol {
public protocol EnableableModelProtocol: AnyObject {
var enabled: Bool { get set }
}

View File

@ -0,0 +1,62 @@
//
// DisableFormFieldEffectModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/6/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public class DisableFormFieldEffectModel: FormFieldEffectProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "disableFormFieldEffect"
public var fieldKey: String = ""
public var activatedRuleIds: [String]?
public var rules: [RulesProtocol]
init(_ fieldKey: String, _ activatedRuleIds: [String], _ rules: [RulesProtocol]) {
self.fieldKey = fieldKey
self.activatedRuleIds = activatedRuleIds
self.rules = rules
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case fieldKey
case activatedRuleIds
case rules
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.fieldKey = try typeContainer.decode(String.self, forKey: .fieldKey)
self.activatedRuleIds = try typeContainer.decodeIfPresent([String].self, forKey: .activatedRuleIds)
self.rules = try typeContainer.decodeModels(codingKey: .rules)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fieldKey, forKey: .fieldKey)
try container.encode(activatedRuleIds, forKey: .activatedRuleIds)
try container.encodeModels(rules, forKey: .rules)
}
public func setEffect(validity: Bool, field: FormFieldProtocol, form: FormValidator, group: FormGroupRule) {
field.enabled = !validity
if let updateField = field as? UIUpdatableModelProtocol {
updateField.updateUI?()
}
}
}

View File

@ -0,0 +1,64 @@
//
// EnableFormFieldEffectModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/1/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public class EnableFormFieldEffectModel: FormFieldEffectProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "enableFormFieldEffect"
public var fieldKey: String = ""
public var activatedRuleIds: [String]?
public var rules: [RulesProtocol]
init(_ fieldKey: String, activatedRuleIds: [String], rules: [RulesProtocol]) {
self.fieldKey = fieldKey
self.activatedRuleIds = activatedRuleIds
self.rules = rules
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case fieldKey
case activatedRuleIds
case rules
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.fieldKey = try typeContainer.decode(String.self, forKey: .fieldKey)
self.activatedRuleIds = try typeContainer.decodeIfPresent([String].self, forKey: .activatedRuleIds)
self.rules = try typeContainer.decodeModels(codingKey: .rules)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fieldKey, forKey: .fieldKey)
try container.encode(activatedRuleIds, forKey: .activatedRuleIds)
try container.encodeModels(rules, forKey: .rules)
}
public func setEffect(validity: Bool, field: FormFieldProtocol,form: FormValidator, group: FormGroupRule) {
field.enabled = validity
if let updateField = field as? UIUpdatableModelProtocol {
updateField.updateUI?()
}
}
}

View File

@ -0,0 +1,34 @@
//
// FormFieldEffectProtocol.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/6/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol FormFieldEffectProtocol: ModelProtocol, RulesContainerProtocol {
/// This is the key to the form field that will take the effect
var fieldKey: String { get }
/// This is an array of ruleIds that are a property of RulesProtocol for the array of Validation rules within the FormGroupRule.rules
/// When this effect "rules" are valid (true), the Form will add the fieldKey to the corresponding Validation rule "fields" property.
/// When this effect "rules" are invalid (false), the Form will remove the fieldKey from the corresponding Validation rule "fields" property.
var activatedRuleIds: [String]? { get }
/// This function will execute the effect on the field passed in based on the validity. validity is the result of the array of rules run.
/// - Parameters:
/// - validity: This is run against the FormFieldAffect
/// - field: Form Field that is bound to the fieldKey
/// - form: Form that the formGroup is attached
/// - group: FormGroupRule that is attached to this effect
func setEffect(validity: Bool, field: FormFieldProtocol, form: FormValidator, group: FormGroupRule)
}
public extension FormFieldEffectProtocol {
var type: String { Self.identifier }
static var categoryCodingKey: String { "type" }
static var categoryName: String { "\(FormFieldEffectProtocol.self)" }
}

View File

@ -0,0 +1,62 @@
//
// HideFormFieldEffectModel.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/6/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public class HideFormFieldEffectModel: FormFieldEffectProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "hideFormFieldEffect"
public var fieldKey: String = ""
public var activatedRuleIds: [String]?
public var rules: [RulesProtocol]
init(_ fieldKey: String, _ activatedRuleIds: [String], _ rules: [RulesProtocol]) {
self.fieldKey = fieldKey
self.activatedRuleIds = activatedRuleIds
self.rules = rules
}
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case fieldKey
case activatedRuleIds
case rules
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.fieldKey = try typeContainer.decode(String.self, forKey: .fieldKey)
self.activatedRuleIds = try typeContainer.decodeIfPresent([String].self, forKey: .activatedRuleIds)
self.rules = try typeContainer.decodeModels(codingKey: .rules)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fieldKey, forKey: .fieldKey)
try container.encode(activatedRuleIds, forKey: .activatedRuleIds)
try container.encodeModels(rules, forKey: .rules)
}
public func setEffect(validity: Bool, field: FormFieldProtocol, form: FormValidator, group: FormGroupRule) {
field.enabled = validity
if let updateField = field as? UIUpdatableModelProtocol {
updateField.updateUI?()
}
}
}

View File

@ -9,6 +9,11 @@
import Foundation
public protocol FormGroupWatcherFieldProtocol: FormItemProtocol {
func setValidity(_ valid: Bool, group: FormGroupRule)
public protocol FormGroupWatcherFieldProtocol: FormItemProtocol, UIUpdatableModelProtocol {}
public extension FormGroupWatcherFieldProtocol {
func setValidity(_ valid: Bool){
self.enabled = valid;
updateUI?()
}
}

View File

@ -9,7 +9,7 @@
import Foundation
public protocol FormItemProtocol {
public protocol FormItemProtocol: EnableableModelProtocol {
/// The group name to used to group this item for validation, enableability, and value getting.
var groupName: String { get set }
}

View File

@ -11,6 +11,11 @@ import MVMCore
@objcMembers public class FormValidator: NSObject {
public enum ValidationError: Error {
case other(error: String)
}
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -80,38 +85,115 @@ import MVMCore
/// Validates all rule groups. Returns if valid
public func validate() -> Bool {
var valid = true
guard let formRules = formRules else { return valid }
for group in formRules {
let groupValid = validateGroup(group)
valid = valid && groupValid
var groupValid = false
do {
groupValid = try validateGroup(group)
} catch {
if let err = MVMCoreErrorObject.createErrorObject(for: error, location: "FormValidator"){
MVMCoreLoggingHandler.shared()?.addError(toLog: err)
fatalError(err.description)
}
}
valid = valid && groupValid
}
return valid
}
/// Validates a given rule group. Returns if valid
public func validateGroup(_ group: FormGroupRule) -> Bool {
// Validate each rule.
var valid = true
var previousValidity: [String: Bool] = [:]
for rule in group.rules {
let tuple = rule.validate(fields, previousValidity)
let isValidRule = tuple.valid
let returnedValidity = tuple.fieldValidity
previousValidity = previousValidity.merging(returnedValidity) { (_, new) in new }
valid = valid && isValidRule
}
/// Validates a FormGroupRule
/// - Parameters:
/// - group: FormGroupRule being validated
/// - counter: keeps track of how many times causes another group validation
/// - Returns: validity for the FormGroupRule.rules
public func validateGroup(_ group: FormGroupRule, counter: Int = 0) throws -> Bool {
let valid = group.validate(fields)
// Notify the group watchers of validity.
for watcher in groupWatchers {
if watcher.groupName == group.groupName {
watcher.setValidity(valid, group: group)
for watcher in groupWatchers.filter({$0.groupName == group.groupName}) {
watcher.setValidity(valid)
}
var ruleChange = false
//loop the effects
group.effects?.forEach({ effect in
//get the fieldKey for the effect
if let effected = fields[effect.fieldKey] {
//get the validity
let validity = effect.validate(fields)
//set the effect with the validation
effect.setEffect(validity: validity, field: effected, form: self, group: group)
//update the group form rules
if let ruleIds = effect.activatedRuleIds {
let didChange = self.updateRules(for: group, with: validity, for: effect.fieldKey, and: ruleIds)
if(didChange) {
ruleChange = didChange
}
}
}
})
if ruleChange {
if counter > 3 {
throw ValidationError.other(error: "Effect caused validation loop error")
} else {
return try self.validateGroup(group, counter: counter + 1)
}
} else {
return valid
}
}
/// Updates the Fields in which a specific rule within a FormGroupRule validates against.
/// - Parameters:
/// - group: FormGroupRule to update
/// - validity: If you the fieldKey should be added or removed from a rule
/// - fieldKey: FieldKey that will be added or removed from a rule
/// - ruleIds: Array of ruleIds to add or remove the fieldKey into the rule.fields property
public func updateRules(for group: FormGroupRule, with validity: Bool, for fieldKey: String, and ruleIds: [String]) -> Bool{
//update the group rules based on the validation of this rule to show/hide
var ruleChange = false
ruleIds.forEach { ruleId in
//only change a rule found
if var mutableRule = group.rules.first(where: { rule in return rule.ruleId == ruleId}) {
//make a copy of the fields
var mutableFields = mutableRule.fields
//if the validity is true
if validity {
//we need to ensure this fieldKey is included in the found rule
//therefore we mush append the fieldKey to the fields array
if !mutableRule.fields.contains(where: { field in return field == fieldKey }){
mutableFields.append(fieldKey)
mutableRule.fields = mutableFields
//a rule was changed, therefore we need
//to re-run the validation on the group
ruleChange = true
}
} else {
//the validity is false, therefore we must ensure
//that the fieldKey does NOT exist in the found
//rule fields
if let fieldKeyIndex = mutableFields.firstIndex(of: fieldKey) {
mutableFields.remove(at: fieldKeyIndex)
mutableRule.fields = mutableFields
//a rule was changed, therefore we need
//to re-run the validation on the group
ruleChange = true
}
}
}
}
return valid
//if any rules updated, run the validateGroup again
return ruleChange
}
}

View File

@ -9,22 +9,22 @@
import Foundation
open class FormGroupRule: Codable {
open class FormGroupRule: RulesContainerProtocol, Codable {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
var groupName: String
var rules: [RulesProtocol]
public var groupName: String
public var rules: [RulesProtocol]
public var effects: [FormFieldEffectProtocol]?;
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
init(_ groupName: String, _ rules: [RulesProtocol]) {
public init(_ groupName: String, _ rules: [RulesProtocol], _ effects: [FormFieldEffectProtocol]) {
self.groupName = groupName
self.rules = rules
self.effects = effects
}
//--------------------------------------------------
@ -34,6 +34,7 @@ open class FormGroupRule: Codable {
private enum CodingKeys: String, CodingKey {
case groupName
case rules
case effects
}
//--------------------------------------------------
@ -44,6 +45,7 @@ open class FormGroupRule: Codable {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.groupName = try typeContainer.decode(String.self, forKey: .groupName)
self.rules = try typeContainer.decodeModels(codingKey: .rules)
self.effects = try typeContainer.decodeModelsIfPresent(codingKey: .effects)
}
public func encode(to encoder: Encoder) throws {

View File

@ -15,6 +15,7 @@ public class RuleAllValueChangedModel: RulesProtocol {
public static var identifier: String = "allValueChanged"
public var type: String = RuleAllValueChangedModel.identifier
public var ruleId: String?
public var errorMessage: [String: String]?
public var fields: [String]

View File

@ -16,6 +16,7 @@ public class RuleAnyRequiredModel: RulesProtocol {
public static var identifier: String = "anyRequired"
public var type: String = RuleRequiredModel.identifier
public var ruleId: String?
public var fields: [String]
public var errorMessage: [String: String]?

View File

@ -16,6 +16,7 @@ public class RuleAnyValueChangedModel: RulesProtocol {
public static var identifier: String = "anyValueChanged"
public var type: String = RuleAnyValueChangedModel.identifier
public var ruleId: String?
public var errorMessage: [String: String]?
public var fields: [String]

View File

@ -16,6 +16,7 @@ public class RuleEqualsIgnoreCaseModel: RulesProtocol {
public static var identifier: String = "equalsIgnoreCase"
public var type: String = RuleEqualsIgnoreCaseModel.identifier
public var ruleId: String?
public var fields: [String]
public var errorMessage: [String: String]?

View File

@ -16,6 +16,7 @@ public class RuleEqualsModel: RulesProtocol {
public static var identifier: String = "equals"
public var type: String = RuleEqualsModel.identifier
public var ruleId: String?
public var fields: [String]
public var errorMessage: [String: String]?

View File

@ -6,7 +6,6 @@
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
public class RuleRegexModel: RulesProtocol {
//--------------------------------------------------
// MARK: - Properties
@ -14,6 +13,7 @@ public class RuleRegexModel: RulesProtocol {
public static var identifier: String = "regex"
public var type: String = RuleRegexModel.identifier
public var ruleId: String?
public var fields: [String]
public var regex: String
public var errorMessage: [String: String]?
@ -30,4 +30,5 @@ public class RuleRegexModel: RulesProtocol {
return false
}
}

View File

@ -16,6 +16,7 @@ public class RuleRequiredModel: RulesProtocol {
public static var identifier: String = "allRequired"
public var type: String = RuleRequiredModel.identifier
public var ruleId: String?
public var errorMessage: [String: String]?
public var fields: [String]

View File

@ -16,7 +16,11 @@ public enum RulesCodingKey: String, CodingKey {
public protocol RulesProtocol: ModelProtocol {
// The type of rule
var type: String { get }
//id for the rule
var ruleId: String? { get }
//error message to show for the rule
var errorMessage: [String: String]? { get }
// The fields that this rule applies to.
@ -56,3 +60,25 @@ public extension RulesProtocol {
return (valid: valid, fieldValidity: previousValidity)
}
}
public protocol RulesContainerProtocol{
var rules: [RulesProtocol] { get }
}
public extension RulesContainerProtocol {
func validate(_ fields: [String: FormFieldProtocol]) -> Bool {
// Validate each rule.
var valid = true
var previousValidity: [String: Bool] = [:]
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
}
}

View File

@ -0,0 +1,13 @@
//
// Updatable.swift
// MVMCoreUI
//
// Created by Matt Bruce on 12/1/21.
// Copyright © 2021 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol UIUpdatableModelProtocol {
var updateUI: ActionBlock? { get set }
}

View File

@ -243,5 +243,8 @@ open class CoreUIModelMapping: ModelMapping {
ModelRegistry.register(RuleEqualsModel.self)
ModelRegistry.register(RuleEqualsIgnoreCaseModel.self)
ModelRegistry.register(RuleRegexModel.self)
ModelRegistry.register(EnableFormFieldEffectModel.self)
ModelRegistry.register(DisableFormFieldEffectModel.self)
ModelRegistry.register(HideFormFieldEffectModel.self)
}
}