Form Cleaning

This commit is contained in:
Pfeil, Scott Robert 2020-03-24 17:20:19 -04:00
parent 438277b37f
commit 26a530af2c
26 changed files with 214 additions and 265 deletions

View File

@ -22,13 +22,12 @@
011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A0240453D0000E3791 /* RuleEqualsModel.swift */; };
011D95A3240453F8000E3791 /* RuleRegexModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A2240453F8000E3791 /* RuleRegexModel.swift */; };
011D95A5240455DC000E3791 /* FormGroupRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A4240455DC000E3791 /* FormGroupRule.swift */; };
011D95A924057AC7000E3791 /* FormActionFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A824057AC7000E3791 /* FormActionFieldProtocol.swift */; };
011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */; };
011D95AB2405C553000E3791 /* FormItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95AA2405C553000E3791 /* FormItemProtocol.swift */; };
011D95AD2406BB57000E3791 /* FormHolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */; };
011D95AF2407266E000E3791 /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D95AE2407266E000E3791 /* RadioButtonModel.swift */; };
011D9602240DA20A000E3791 /* ValidProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9601240DA20A000E3791 /* ValidProtocol.swift */; };
011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */; };
011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */; };
011D9628240EFA1E000E3791 /* MFViewController+Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D9627240EFA1E000E3791 /* MFViewController+Form.swift */; };
012A889C23889E8400FE3DA1 /* TemplateModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */; };
012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */; };
012A88B1238C880100FE3DA1 /* CarouselPagingModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A88B0238C880100FE3DA1 /* CarouselPagingModelProtocol.swift */; };
@ -52,7 +51,6 @@
01509D932327ECFB00EF99AA /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D922327ECFB00EF99AA /* ProgressBar.swift */; };
01509D952327ED1900EF99AA /* HeadlineBodyLinkToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01509D942327ED1900EF99AA /* HeadlineBodyLinkToggle.swift */; };
017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */; };
017BEB3C2361EA1D0024EF95 /* MFViewController+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */; };
017BEB48236230DB0024EF95 /* MoleculeViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */; };
017BEB7B236763000024EF95 /* LineModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB7A236763000024EF95 /* LineModel.swift */; };
017BEB7F23676E870024EF95 /* MoleculeObjectMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017BEB7E23676E870024EF95 /* MoleculeObjectMapping.swift */; };
@ -415,13 +413,12 @@
011D95A0240453D0000E3791 /* RuleEqualsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleEqualsModel.swift; sourceTree = "<group>"; };
011D95A2240453F8000E3791 /* RuleRegexModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleRegexModel.swift; sourceTree = "<group>"; };
011D95A4240455DC000E3791 /* FormGroupRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormGroupRule.swift; sourceTree = "<group>"; };
011D95A824057AC7000E3791 /* FormActionFieldProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormActionFieldProtocol.swift; sourceTree = "<group>"; };
011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormGroupWatcherFieldProtocol.swift; sourceTree = "<group>"; };
011D95AA2405C553000E3791 /* FormItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormItemProtocol.swift; sourceTree = "<group>"; };
011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormHolderProtocol.swift; sourceTree = "<group>"; };
011D95AE2407266E000E3791 /* RadioButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = "<group>"; };
011D9601240DA20A000E3791 /* ValidProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidProtocol.swift; sourceTree = "<group>"; };
011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormRuleWatcherFieldProtocol.swift; sourceTree = "<group>"; };
011D9625240EBB16000E3791 /* RadioButtonLabelModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonLabelModel.swift; sourceTree = "<group>"; };
011D9627240EFA1E000E3791 /* MFViewController+Form.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFViewController+Form.swift"; sourceTree = "<group>"; };
012A889B23889E8400FE3DA1 /* TemplateModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModelProtocol.swift; sourceTree = "<group>"; };
012A88AC238C418100FE3DA1 /* TemplateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateProtocol.swift; sourceTree = "<group>"; };
012A88AE238C626E00FE3DA1 /* CarouselModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselModel.swift; sourceTree = "<group>"; };
@ -445,7 +442,6 @@
01509D922327ECFB00EF99AA /* ProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
01509D942327ED1900EF99AA /* HeadlineBodyLinkToggle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeadlineBodyLinkToggle.swift; sourceTree = "<group>"; };
017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonLabel.swift; sourceTree = "<group>"; };
017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MFViewController+Model.swift"; sourceTree = "<group>"; };
017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeViewProtocol.swift; sourceTree = "<group>"; };
017BEB7A236763000024EF95 /* LineModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineModel.swift; sourceTree = "<group>"; };
017BEB7E23676E870024EF95 /* MoleculeObjectMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeObjectMapping.swift; sourceTree = "<group>"; };
@ -827,13 +823,13 @@
011D958A24042794000E3791 /* Rules */ = {
isa = PBXGroup;
children = (
011D95A4240455DC000E3791 /* FormGroupRule.swift */,
011D958424042432000E3791 /* RulesProtocol.swift */,
011D959A240451E3000E3791 /* RuleRequiredModel.swift */,
011D959C2404536F000E3791 /* RuleAnyValueChangedModel.swift */,
011D959E240453A1000E3791 /* RuleAllValueChangedModel.swift */,
011D95A0240453D0000E3791 /* RuleEqualsModel.swift */,
011D95A2240453F8000E3791 /* RuleRegexModel.swift */,
011D95A4240455DC000E3791 /* FormGroupRule.swift */,
0A69F610241BDEA700F7231B /* RuleAnyRequiredModel.swift */,
);
name = Rules;
@ -855,8 +851,8 @@
011D95AC2406BB57000E3791 /* FormHolderProtocol.swift */,
011D95AA2405C553000E3791 /* FormItemProtocol.swift */,
011D958624042492000E3791 /* FormFieldProtocol.swift */,
011D95A824057AC7000E3791 /* FormActionFieldProtocol.swift */,
011D9601240DA20A000E3791 /* ValidProtocol.swift */,
011D95A824057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift */,
011D9601240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift */,
0105618A224BBE7700E1557D /* FormValidator.swift */,
011D958A24042794000E3791 /* Rules */,
);
@ -1015,8 +1011,6 @@
children = (
D29DF16021E69996003B2FB9 /* MFViewController.h */,
D29DF15F21E69996003B2FB9 /* MFViewController.m */,
017BEB3B2361EA1D0024EF95 /* MFViewController+Model.swift */,
011D9627240EFA1E000E3791 /* MFViewController+Form.swift */,
D29DF28F21E7ADB8003B2FB9 /* MFScrollingViewController.h */,
D29DF29021E7ADB8003B2FB9 /* MFScrollingViewController.m */,
D29DF29121E7ADB8003B2FB9 /* MFProgrammaticScrollViewController.h */,
@ -1908,12 +1902,11 @@
D2E2A99423D8CCBC000B42E6 /* HeadlineBodyLinkModel.swift in Sources */,
01004F3022721C3800991ECC /* RadioButton.swift in Sources */,
D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */,
017BEB3C2361EA1D0024EF95 /* MFViewController+Model.swift in Sources */,
D236E5B7242007C500C38625 /* MVMControllerModelProtocol.swift in Sources */,
D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */,
D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */,
D2B18B7F2360913400A9AEDC /* Control.swift in Sources */,
011D95A924057AC7000E3791 /* FormActionFieldProtocol.swift in Sources */,
011D95A924057AC7000E3791 /* FormGroupWatcherFieldProtocol.swift in Sources */,
D236E5B4241FEB1000C38625 /* ListTwoColumnPriceDescription.swift in Sources */,
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */,
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
@ -1935,7 +1928,7 @@
012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */,
BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */,
D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */,
011D9602240DA20A000E3791 /* ValidProtocol.swift in Sources */,
011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */,
D260106323D0C05000764D80 /* StackItemModel.swift in Sources */,
D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */,
D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */,
@ -2122,7 +2115,6 @@
94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */,
D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */,
D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */,
011D9628240EFA1E000E3791 /* MFViewController+Form.swift in Sources */,
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */,
52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */,
D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */,

View File

@ -18,8 +18,7 @@ public enum ButtonSize: String, Codable {
case tiny
}
public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormActionFieldProtocol {
public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
public static var identifier: String = "button"
public var backgroundColor: Color?
public var title: String
@ -33,13 +32,14 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormAction
public var disabledFillColor: Color?
public var disabledTextColor: Color?
public var disabledBorderColor: Color?
public var groupName: String? = FormValidator.defaultGroupName
public var groupName: String = FormValidator.defaultGroupName
public func updateEnable(_ enabled: Bool) {
self.enabled = enabled
public func setValidity(_ valid: Bool, group: FormGroupRule) {
enabled = valid
updateUI?()
}
/// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: (() -> Void)?
init(with title: String, action: ActionModelProtocol) {

View File

@ -127,10 +127,12 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
setTitle(model.title, for: .normal)
model.updateUI = { [weak self] in
self?.enableField(model.enabled)
MVMCoreDispatchUtility.performBlock(onMainThread: {
self?.enableField(model.enabled)
})
}
FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate)
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
}
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {

View File

@ -81,7 +81,7 @@ import UIKit
} else {
isSelected = !isSelected
}
FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
setNeedsDisplay()
}
@ -130,7 +130,7 @@ import UIKit
isSelected = model.state
let radioButtonModel = RadioButtonSelectionHelper.setupForRadioButtonGroup(model,
formValidator: delegateObject?.formHolderDelegate?.formValidator)
FormValidator.setupValidation(molecule: radioButtonModel, delegate: delegateObject?.formHolderDelegate)
FormValidator.setupValidation(for: radioButtonModel, delegate: delegateObject?.formHolderDelegate)
}
public override func reset() {

View File

@ -20,11 +20,13 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
public var state: Bool = false
public var enabled: Bool = true
public var baseValue: AnyHashable?
public var groupName: String?
public var fieldKey: String?
/// The specific value to send to server. TODO: update this to be more generic.
public var fieldValue: String?
public var baseValue: AnyHashable?
public var groupName: String = FormValidator.defaultGroupName
public var fieldKey: String?
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
@ -34,8 +36,8 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
case backgroundColor
case state
case enabled
case fieldKey
case fieldValue
case fieldKey
case groupName
}
@ -45,6 +47,7 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
public init(_ state: Bool) {
self.state = state
baseValue = state
}
//--------------------------------------------------
@ -71,8 +74,12 @@ open class RadioButtonModel: MoleculeModelProtocol, FormFieldProtocol {
}
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
baseValue = state
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue)
}

View File

@ -10,10 +10,8 @@ import Foundation
import UIKit
@objcMembers public class RadioButtonSelectionHelper: FormFieldProtocol {
public var fieldKey: String?
public var groupName: String? = FormValidator.defaultGroupName
public var groupName: String = FormValidator.defaultGroupName
private var selectedRadioButton: RadioButton?
private var fieldGroupName: String?
public var baseValue: AnyHashable?

View File

@ -9,7 +9,7 @@
import Foundation
@objcMembers public class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, ValidProtocol {
@objcMembers public class EntryFieldModel: MoleculeModelProtocol, FormFieldProtocol, FormRuleWatcherFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
@ -26,9 +26,10 @@ import Foundation
public var isEnabled: Bool = true
public var isLocked: Bool?
public var isSelected: Bool?
public var fieldKey: String?
public var groupName: String? = FormValidator.defaultGroupName
public var text: String?
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var isValid: Bool? {
@ -36,9 +37,12 @@ import Foundation
updateUI?()
}
}
/// Temporary binding mechanism for the view to update on enable changes.
public var updateUI: (() -> Void)?
public func setValidity(_ isValid: Bool) {
self.isValid = isValid
public func setValidity(_ valid: Bool, rule: RulesProtocol) {
self.isValid = valid
}
//--------------------------------------------------
@ -54,10 +58,10 @@ import Foundation
case errorMessage = "errorMsg"
case isLocked
case isSelected
case fieldKey
case isValid
case isRequired = "required"
case text
case fieldKey
case groupName
}
@ -69,6 +73,11 @@ import Foundation
// MARK: - Initializers
//--------------------------------------------------
public init(with text: String) {
self.text = text
baseValue = text
}
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
@ -78,12 +87,13 @@ import Foundation
isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true
isLocked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isLocked)
isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .isSelected)
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
isValid = try typeContainer.decodeIfPresent(Bool.self, forKey: .isValid)
text = try typeContainer.decodeIfPresent(String.self, forKey: .text)
baseValue = text
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
self.groupName = groupName
}
}

View File

@ -232,7 +232,7 @@ import UIKit
/// Validates the text of the entry field.
@objc public func validateTextField() {
text = textField.text
FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
}
@objc public func updateValidation(_ isValid: Bool) {
@ -279,12 +279,14 @@ import UIKit
guard let model = model as? TextEntryFieldModel else { return }
model.updateUI = { [weak self] in
if self?.isSelected ?? false {
self?.updateValidation(model.isValid ?? true)
}
MVMCoreDispatchUtility.performBlock(onMainThread: {
if self?.isSelected ?? false {
self?.updateValidation(model.isValid ?? true)
}
})
}
self.delegateObject = delegateObject
FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate)
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
textColor.enabled = model.enabledTextColor?.uiColor
textColor.disabled = model.disabledTextColor?.uiColor
text = model.text

View File

@ -129,7 +129,7 @@ import MVMCore
(model as? CheckboxModel)?.isChecked = isSelected
shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
updateAccessibilityLabel()
}
}
@ -398,7 +398,7 @@ import MVMCore
super.set(with: model, delegateObject, additionalData)
guard let model = model as? CheckboxModel else { return }
self.delegateObject = delegateObject
FormValidator.setupValidation(molecule: model, delegate: delegateObject?.formHolderDelegate)
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
if let fieldKey = model.fieldKey {
self.fieldKey = fieldKey

View File

@ -29,10 +29,8 @@ import Foundation
public var disabledCheckColor: Color = Color(uiColor: .mvmCoolGray3)
public var action: ActionModelProtocol?
public var fieldKey: String?
public var fieldValue: JSONValue?
public var groupName: String? = FormValidator.defaultGroupName
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
//--------------------------------------------------
@ -41,8 +39,6 @@ import Foundation
private enum CodingKeys: String, CodingKey {
case moleculeName
case fieldKey
case fieldValue
case isChecked = "checked"
case isEnabled = "enabled"
case isAnimated = "animated"
@ -56,23 +52,25 @@ import Foundation
case disabledCheckColor
case disabledBorderColor
case action
case fieldKey
case groupName
}
init(isChecked: Bool = false) {}
public func formFieldValue() -> AnyHashable? {
return isChecked
}
public init(isChecked: Bool = false) {
self.isChecked = isChecked
baseValue = isChecked
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
fieldValue = try typeContainer.decodeIfPresent(JSONValue.self, forKey: .fieldValue)
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) ?? 1
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) ?? Color(uiColor: .black)
checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) ?? Color(uiColor: .black)
@ -86,6 +84,9 @@ import Foundation
isRound = try typeContainer.decodeIfPresent(Bool.self, forKey: .isRound) ?? false
isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
baseValue = isChecked
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
@ -95,7 +96,6 @@ import Foundation
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encodeIfPresent(fieldValue, forKey: .fieldValue)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(borderColor, forKey: .borderColor)
try container.encode(borderWidth, forKey: .borderWidth)

View File

@ -102,7 +102,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
}
(model as? ToggleModel)?.state = isOn
FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
_ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate)
accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff")
setNeedsLayout()
layoutIfNeeded()
@ -343,7 +343,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
self.model = model
self.delegateObject = delegateObject
FormValidator.setupValidation(molecule: toggleModel, delegate: delegateObject?.formHolderDelegate)
FormValidator.setupValidation(for: toggleModel, delegate: delegateObject?.formHolderDelegate)
guard let model = model as? ToggleModel else { return }
if let color = model.onTintColor?.uiColor {
containerTintColor?.on = color

View File

@ -17,14 +17,15 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
public var enabled: Bool = true
public var action: ActionModelProtocol?
public var alternateAction: ActionModelProtocol?
public var fieldKey: String?
public var groupName: String? = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
public var onTintColor: Color?
public var offTintColor: Color?
public var onKnobTintColor: Color?
public var offKnobTintColor: Color?
public var fieldKey: String?
public var groupName: String = FormValidator.defaultGroupName
public var baseValue: AnyHashable?
private enum CodingKeys: String, CodingKey {
case moleculeName
case state
@ -32,13 +33,13 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
case enabled
case action
case backgroundColor
case fieldKey
case alternateAction
case groupName
case onTintColor
case offTintColor
case onKnobTintColor
case offKnobTintColor
case fieldKey
case groupName
}
public func formFieldValue() -> AnyHashable? {
@ -47,6 +48,7 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
public init(_ state: Bool) {
self.state = state
baseValue = state
}
required public init(from decoder: Decoder) throws {
@ -63,14 +65,16 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
onTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onTintColor)
offTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .offTintColor)
onKnobTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .onKnobTintColor)
offKnobTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .offKnobTintColor)
baseValue = state
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) {
self.groupName = groupName
}
}
public func encode(to encoder: Encoder) throws {
@ -80,11 +84,11 @@ public class ToggleModel: MoleculeModelProtocol, FormFieldProtocol, EnableableMo
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(state, forKey: .state)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encodeIfPresent(onTintColor, forKey: .onTintColor)
try container.encodeIfPresent(onKnobTintColor, forKey: .onKnobTintColor)
try container.encodeIfPresent(onKnobTintColor, forKey: .onKnobTintColor)
try container.encodeIfPresent(offKnobTintColor, forKey: .offKnobTintColor)
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
try container.encodeIfPresent(groupName, forKey: .groupName)
}
}

View File

@ -170,8 +170,8 @@ import UIKit
navigationModel.line = LineModel(type: .none)
}
pageModel?.navigationItem = navigationModel
if self.formValidator == nil {
let rules = pageModel?.formRules
if self.formValidator == nil,
let rules = pageModel?.formRules {
self.formValidator = FormValidator(rules)
}
}
@ -244,7 +244,7 @@ import UIKit
}
open func updateViews() {
formValidator?.validate()
_ = formValidator?.validate()
}
override open func viewDidLoad() {

View File

@ -1,14 +0,0 @@
//
// FormActionFieldProtocol.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 1/31/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol FormActionFieldProtocol: EnableableModelProtocol, FormItemProtocol {
func updateEnable(_ enabled: Bool)
var updateUI: (() -> Void)? { get set }
}

View File

@ -5,13 +5,19 @@
// Created by Suresh, Kamlesh on 1/31/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// Form fields are items can be interacted with. They have value, and may need to be validated.
import Foundation
public protocol FormFieldProtocol: FormItemProtocol {
/// How the validator identifies the field when validating rules.
var fieldKey: String? { get set }
/// A place to store the initial value of the field for checking if the value has changed.
var baseValue: AnyHashable? { get set }
/// Returns the value of the field. Used for validations and possibly for sending to server.
func formFieldValue() -> AnyHashable?
}

View File

@ -0,0 +1,14 @@
//
// FormGroupWatcherFieldProtocol.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 1/31/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// Fields that want to be informed of group validity.
import Foundation
public protocol FormGroupWatcherFieldProtocol: FormItemProtocol {
func setValidity(_ valid: Bool, group: FormGroupRule)
}

View File

@ -5,7 +5,7 @@
// Created by Suresh, Kamlesh on 2/5/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// A protocol for the model of the delegate object that holds the form validator. The rules are stored in the model.
// A protocol for the model of the delegate object that holds the form validator. The rules that need to be validated are stored in the model.
import Foundation

View File

@ -5,15 +5,11 @@
// Created by Suresh, Kamlesh on 2/25/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// The base form item protocol. Shared by different types of items. In our case, the models (not views) are form items which are registered with the validator.
import Foundation
public protocol FormItemProtocol {
static var defaultGroupName: String? { get }
var groupName: String? { get set }
}
extension FormItemProtocol{
public static var defaultGroupName: String? {
return "default"
}
/// The group name to used to group this item for validation, enableability, and value getting.
var groupName: String { get set }
}

View File

@ -0,0 +1,14 @@
//
// FormRuleWatcherFieldProtocol.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 3/2/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// Fields that want to be informed of rule validity.
import Foundation
public protocol FormRuleWatcherFieldProtocol {
func setValidity(_ valid: Bool, rule: RulesProtocol)
}

View File

@ -12,103 +12,118 @@ import MVMCore
@objcMembers public class FormValidator: NSObject {
static var defaultGroupName: String = "default"
var extraValidationBlock: (() -> Bool)?
var formRules: [FormGroupRule]?
var formRules: [FormGroupRule]
weak var delegate: FormHolderProtocol?
var fieldMolecules: [String: FormFieldProtocol] = [:]
var formActionMolecules: [FormActionFieldProtocol] = []
var fields: [String: FormFieldProtocol] = [:]
var groupWatchers: [FormGroupWatcherFieldProtocol] = []
var radioButtonsModelByGroup: [String: RadioButtonSelectionHelper] = [:]
public init(_ formRules: [FormGroupRule]?) {
public init(_ formRules: [FormGroupRule]) {
self.formRules = formRules
}
public func insertMolecule(_ molecule: FormItemProtocol) {
if var molecule = molecule as? FormFieldProtocol,
let fieldKey = molecule.fieldKey {
if molecule.baseValue == nil {
molecule.baseValue = molecule.formFieldValue()
}
fieldMolecules[fieldKey] = molecule
}
if let molecule = molecule as? FormActionFieldProtocol {
formActionMolecules.append(molecule)
/// Adds the form field to the validator.
public func add(_ field: FormFieldProtocol) {
if let fieldKey = field.fieldKey {
fields[fieldKey] = field
}
}
public static func setupValidation(molecule: FormItemProtocol, delegate: FormHolderProtocol?) {
/// Adds the form action to the validator.
public func add(_ action: FormGroupWatcherFieldProtocol) {
groupWatchers.append(action)
}
/// Determines the type of item and adds it.
private func insert(_ item: FormItemProtocol) {
if let item = item as? FormFieldProtocol {
add(item)
} else if let item = item as? FormGroupWatcherFieldProtocol {
add(item)
}
}
/// Convenience function. Gets the form validator from the holder and sets up the item with it.
public static func setupValidation(for item: FormItemProtocol, delegate: FormHolderProtocol?) {
if let validator = delegate?.formValidator {
validator.delegate = delegate
validator.insertMolecule(molecule)
validator.insert(item)
}
}
/// Convenience function. Gets the form validator from the holder and asks it to validate.
public static func validate(delegate: FormHolderProtocol?) -> Bool? {
return delegate?.formValidator?.validate()
}
/// Validates all rule groups. Returns if valid
public func validate() -> Bool {
var valid = true
for group in formRules {
valid = valid && validateGroup(group)
}
return valid
}
/// Validates a given rule group. Returns if valid
public func validateGroup(_ group: FormGroupRule) -> Bool {
// Validate each rule.
var valid = true
for rule in group.rules {
valid = valid && validateRule(rule)
}
// Notify the group watchers of validity.
for watcher in groupWatchers {
if watcher.groupName == group.groupName {
watcher.setValidity(valid, group: group)
}
}
return valid
}
public static func getFormValidatorFor(delegate: FormHolderProtocol?) -> FormValidator? {
return delegate?.formValidator
}
public static func validate(delegate: FormHolderProtocol?) {
delegate?.formValidator?.validate()
}
public func validate() {
guard let formRules = formRules else {
return
}
formActionMolecules.forEach { (actionModel) in
if let groupName = actionModel.groupName,
let formRule = formRules.first(where: { $0.groupName == groupName }) {
validate(groupName, actionModel, formRule.rules)
}
}
}
public func validate(_ groupName: String, _ actionModel: FormActionFieldProtocol, _ rules: [RulesProtocol]) {
/// Validates a given rule. Returns if valid.
public func validateRule(_ rule: RulesProtocol) -> Bool {
var valid = true
for rule in rules {
valid = valid && rule.isValid(fieldMolecules)
if !valid {
break
}
for formKey in rule.fields {
guard let formField = fields[formKey] else { continue }
let fieldValidity = rule.isValid(formField)
(formField as? FormRuleWatcherFieldProtocol)?.setValidity(fieldValidity, rule: rule)
valid = valid && fieldValidity
}
actionModel.updateEnable(valid)
}
public func formField(for fieldKey: String) -> FormFieldProtocol? {
return fieldMolecules[fieldKey]
return valid
}
}
// mark Form params
// mark Form params
// TODO: Temporary hacks, rewrite architecture to support this.
@objc public extension FormValidator {
@objc func addFormParams(requestParameters: MVMCoreRequestParameters) {
let formButton = getFormButton(forPageType: requestParameters.pageType)
let groupName = formButton?.groupName ?? FormValidator.defaultGroupName
let groupName = getGroupName(forPageType: requestParameters.pageType) ?? FormValidator.defaultGroupName
let formParams = self.getFormParams(forGroup: groupName)
requestParameters.add(formParams)
}
@objc func getFormParams( forGroup groupName: String) -> [String: Any] {
var extraParam: [String: Any] = [:]
MVMCoreDispatchUtility.performSyncBlock(onMainThread: {
for (fieldKey, molecule) in self.fieldMolecules {
if let formFieldValue = molecule.formFieldValue(),
groupName == molecule.groupName {
extraParam[fieldKey] = formFieldValue
}
for (fieldKey, field) in fields {
if let formFieldValue = field.formFieldValue(),
groupName == field.groupName {
extraParam[fieldKey] = formFieldValue
}
})
}
return extraParam
}
}
// Temporary
// TODO: Temporary hacks, rewrite architecture to support this.
public extension FormValidator {
func getFormButton(forPageType pageType: String?) -> ButtonModel? {
for actionItem in formActionMolecules {
func getGroupName(forPageType pageType: String?) -> String? {
for actionItem in groupWatchers {
if let buttonModel = actionItem as? ButtonModel,
pageType == (buttonModel.action as? ActionOpenPageModel)?.pageType {
return buttonModel
return buttonModel.groupName
}
}
return nil

View File

@ -5,6 +5,7 @@
// Created by Suresh, Kamlesh on 2/24/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// Used by the form validator for storing the rules for a particular group.
import Foundation

View File

@ -14,16 +14,19 @@ public enum RulesCodingKey: String, CodingKey {
}
public protocol RulesProtocol: ModelProtocol {
var type: String { get set }
// The type of rule
var type: String { get }
// The fields that this rule applies to.
var fields: [String] { get set }
func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool
// Returns if a given field is valid according to the rule
func isValid(_ formField: FormFieldProtocol) -> Bool
func setValid(_ formField: FormFieldProtocol, _ isValid: Bool)
}
public extension RulesProtocol {
var ruleType: String? {
var type: String {
get { return Self.identifier }
}
@ -34,23 +37,4 @@ public extension RulesProtocol {
static var categoryName: String {
return "\(RulesProtocol.self)"
}
func isValid(_ fieldMolecules: [String: FormFieldProtocol]) -> Bool {
var valid = true
for formKey in fields {
guard let formField = fieldMolecules[formKey] else { continue }
let fieldValidity = isValid(formField)
setValid(formField, fieldValidity)
valid = valid && fieldValidity
}
return valid
}
func setValid(_ formField: FormFieldProtocol, _ isValid: Bool) {
guard let formFieldValid = formField as? ValidProtocol else { return }
formFieldValid.setValidity(isValid)
}
}

View File

@ -1,15 +0,0 @@
//
// ValidProtocol.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 3/2/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public protocol ValidProtocol {
var isValid: Bool? { get set }
func setValidity(_ isValid: Bool)
var updateUI: (() -> Void)? { get set }
}

View File

@ -1,22 +0,0 @@
//
// MFViewController+Form.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 3/3/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
extension MFViewController: ObservingTextFieldDelegate {
}
public extension MFViewController {
@objc func startValidation() {
(self as? FormHolderProtocol)?.formValidator?.validate()
}
@objc func addFormParams(requestParameters: MVMCoreRequestParameters) {
(self as? FormHolderProtocol)?.formValidator?.addFormParams(requestParameters: requestParameters)
}
}

View File

@ -1,40 +0,0 @@
//
// MFViewController+Model.swift
// MVMCoreUI
//
// Created by Suresh, Kamlesh on 10/24/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import Foundation
extension MFViewController: MoleculeDelegateProtocol {
public func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? {
guard let name = name else { return nil }
return loadObject?.modulesJSON?.optionalDictionaryForKey(name)
}
public func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? {
guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName),
let moleculeName = moduleJSON.optionalStringForKey("moleculeName"),
let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self)
else { return nil }
do {
return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol
} catch {
MVMCoreUILoggingHandler.logDebugMessage(withDelegate: "error: \(error)")
}
return nil
}
// These are required because swift will call the extension function otherwise.
public func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {}
@objc public func addMolecules(_ molecules: [[AnyHashable: Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {}
@objc public func addMolecules(_ molecules: [[AnyHashable : Any]], indexPath: IndexPath, animation: UITableView.RowAnimation) {}
@objc public func removeMolecules(_ molecules: [[AnyHashable: Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {}
}

View File

@ -467,9 +467,6 @@
- (void)newDataBuildAndUpdate {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[self newDataBuildScreen];
dispatch_async(dispatch_get_main_queue(), ^{
[self startValidation];
});
self.needToUpdateUI = YES;
[self.view setNeedsLayout];
}];
@ -666,8 +663,6 @@
if (requestParameters.openSupportPanel) {
[[MVMCoreUISession sharedGlobal].splitViewController.rightPanel willOpenWithActionInformation:actionInformation];
}
[self addFormParamsWithRequestParameters:requestParameters];
requestParameters.parentPageType = [self.loadObject.pageJSON stringForKey:@"parentPageType"];
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:[self delegateObject]];
}