refactored into FieldTypeHandlers first cut

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-05-09 09:10:46 -05:00
parent eab8e72785
commit 14c578edbd
11 changed files with 238 additions and 178 deletions

View File

@ -146,6 +146,10 @@
EAC58C0A2BED004E00BA39FA /* FieldType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C092BED004E00BA39FA /* FieldType.swift */; };
EAC58C0C2BED01D500BA39FA /* Telephone.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C0B2BED01D500BA39FA /* Telephone.swift */; };
EAC58C0E2BED021600BA39FA /* Password.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C0D2BED021600BA39FA /* Password.swift */; };
EAC58C122BED0DDD00BA39FA /* Text.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C112BED0DDD00BA39FA /* Text.swift */; };
EAC58C142BED0DEC00BA39FA /* Number.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C132BED0DEC00BA39FA /* Number.swift */; };
EAC58C162BED0E0300BA39FA /* InlineAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C152BED0E0300BA39FA /* InlineAction.swift */; };
EAC58C182BED0E2300BA39FA /* SecurityCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC58C172BED0E2300BA39FA /* SecurityCode.swift */; };
EAC71A1D2A2E155A00E47A9F /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */; };
EAC71A1F2A2E173D00E47A9F /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */; };
EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */; };
@ -347,6 +351,10 @@
EAC58C092BED004E00BA39FA /* FieldType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldType.swift; sourceTree = "<group>"; };
EAC58C0B2BED01D500BA39FA /* Telephone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Telephone.swift; sourceTree = "<group>"; };
EAC58C0D2BED021600BA39FA /* Password.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Password.swift; sourceTree = "<group>"; };
EAC58C112BED0DDD00BA39FA /* Text.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Text.swift; sourceTree = "<group>"; };
EAC58C132BED0DEC00BA39FA /* Number.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Number.swift; sourceTree = "<group>"; };
EAC58C152BED0E0300BA39FA /* InlineAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineAction.swift; sourceTree = "<group>"; };
EAC58C172BED0E2300BA39FA /* SecurityCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityCode.swift; sourceTree = "<group>"; };
EAC71A1C2A2E155A00E47A9F /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
EAC71A1E2A2E173D00E47A9F /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = "<group>"; };
EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.swift; sourceTree = "<group>"; };
@ -891,8 +899,12 @@
EAC58C092BED004E00BA39FA /* FieldType.swift */,
EAC58C052BED000200BA39FA /* CreditCard.swift */,
EAC58C072BED002D00BA39FA /* Date.swift */,
EAC58C0B2BED01D500BA39FA /* Telephone.swift */,
EAC58C152BED0E0300BA39FA /* InlineAction.swift */,
EAC58C132BED0DEC00BA39FA /* Number.swift */,
EAC58C0D2BED021600BA39FA /* Password.swift */,
EAC58C172BED0E2300BA39FA /* SecurityCode.swift */,
EAC58C0B2BED01D500BA39FA /* Telephone.swift */,
EAC58C112BED0DDD00BA39FA /* Text.swift */,
);
path = FieldTypes;
sourceTree = "<group>";
@ -1224,6 +1236,7 @@
EAB1D2CF28ABEF2B00DAE764 /* Typography+Base.swift in Sources */,
EA0D1C3B2A6AD51B00E5C127 /* Typogprahy+Styles.swift in Sources */,
EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */,
EAC58C162BED0E0300BA39FA /* InlineAction.swift in Sources */,
EA0D1C3D2A6AD57600E5C127 /* Typography+Enums.swift in Sources */,
EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */,
EAC58C0C2BED01D500BA39FA /* Telephone.swift in Sources */,
@ -1263,6 +1276,7 @@
EA985BF5296C60C000F2FF2E /* Icon.swift in Sources */,
EA3361AA288B25E40071C351 /* Disabling.swift in Sources */,
EA3361B6288B2A410071C351 /* Control.swift in Sources */,
EAC58C122BED0DDD00BA39FA /* Text.swift in Sources */,
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */,
EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */,
EA0D1C392A6AD4DF00E5C127 /* Typography+SpacingConfig.swift in Sources */,
@ -1279,7 +1293,9 @@
EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */,
EA3361A8288B23300071C351 /* UIColor.swift in Sources */,
EA2DC9B42BE2C6FE004F58C5 /* TextField.swift in Sources */,
EAC58C182BED0E2300BA39FA /* SecurityCode.swift in Sources */,
EAC9257D29119B5400091998 /* TextLink.swift in Sources */,
EAC58C142BED0DEC00BA39FA /* Number.swift in Sources */,
EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */,
EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */,
EAD068942A560C13002E3A2D /* LoaderLaunchable.swift in Sources */,

View File

@ -75,6 +75,29 @@ extension InputField {
}
}
class CreditCardHandler: NSObject, FieldTypeHandler {
static let shared = CreditCardHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = inputField.creditCardType.maxLength
$0.compareType = .equals
$0.errorMessage = "Enter a valid credit card."
}
inputField.rules.append(.init(rule))
}
}
}
internal func formatCreditCardNumber(_ number: String) -> String {
let formattedInput = number.filter { $0.isNumber } // Remove any existing slashes
return String.format(formattedInput, indices: creditCardType.separatorIndices, with: " ")

View File

@ -6,8 +6,33 @@
//
import Foundation
import UIKit
extension InputField {
class DateHandler: NSObject, FieldTypeHandler {
static let shared = DateHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = inputField.dateFormat.maxLength
$0.compareType = .equals
$0.errorMessage = "Enter a valid date."
}
inputField.rules.append(.init(rule))
}
}
}
public enum DateFormat: String, CaseIterable {
case mmyy
case mmddyy

View File

@ -9,7 +9,7 @@ import Foundation
import UIKit
extension InputField {
protocol FieldHandler: UITextFieldDelegate {
protocol FieldTypeHandler: UITextFieldDelegate {
var keyboardType: UIKeyboardType { get }
func configure(for inputField: InputField)
func appendRules(for inputField: InputField)
@ -18,7 +18,7 @@ extension InputField {
public enum FieldType: String, CaseIterable {
case text, number, inlineAction, password, creditCard, telephone, date, securityCode
func handler() -> FieldHandler {
func handler() -> FieldTypeHandler {
switch self {
case .text:
return TextHandler.shared
@ -43,150 +43,5 @@ extension InputField {
handler().keyboardType
}
internal func appendRules(for textField: InputField) {
handler().appendRules(for: textField)
}
}
}
extension InputField {
class TextHandler: NSObject, FieldHandler {
static let shared = TextHandler()
var keyboardType: UIKeyboardType { .default }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
class NumberHandler: NSObject, FieldHandler {
static let shared = NumberHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
class InlineActionHandler: NSObject, FieldHandler {
static let shared = InlineActionHandler()
var keyboardType: UIKeyboardType { .default }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
class PasswordHandler: NSObject, FieldHandler {
static let shared = PasswordHandler()
var keyboardType: UIKeyboardType { .default }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
class CreditCardHandler: NSObject, FieldHandler {
static let shared = CreditCardHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = inputField.creditCardType.maxLength
$0.compareType = .equals
$0.errorMessage = "Enter a valid credit card."
}
inputField.rules.append(.init(rule))
}
}
}
class TelephoneHandler: NSObject, FieldHandler {
static let shared = TelephoneHandler()
var keyboardType: UIKeyboardType { .phonePad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = "XXX-XXX-XXXX".count
$0.compareType = .equals
$0.errorMessage = "Enter a valid telephone."
}
inputField.rules.append(.init(rule))
}
}
}
class DateHandler: NSObject, FieldHandler {
static let shared = DateHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = inputField.dateFormat.maxLength
$0.compareType = .equals
$0.errorMessage = "Enter a valid date."
}
inputField.rules.append(.init(rule))
}
}
}
class SecurityCodeHandler: NSObject, FieldHandler {
static let shared = SecurityCodeHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
}

View File

@ -0,0 +1,27 @@
//
// InlineAction.swift
// VDS
//
// Created by Matt Bruce on 5/9/24.
//
import Foundation
import UIKit
extension InputField {
class InlineActionHandler: NSObject, FieldTypeHandler {
static let shared = InlineActionHandler()
var keyboardType: UIKeyboardType { .default }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
}

View File

@ -0,0 +1,27 @@
//
// Number.swift
// VDS
//
// Created by Matt Bruce on 5/9/24.
//
import Foundation
import UIKit
extension InputField {
class NumberHandler: NSObject, FieldTypeHandler {
static let shared = NumberHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
}

View File

@ -6,6 +6,7 @@
//
import Foundation
import UIKit
extension InputField {
@ -17,4 +18,18 @@ extension InputField {
}
}
class PasswordHandler: NSObject, FieldTypeHandler {
static let shared = PasswordHandler()
var keyboardType: UIKeyboardType { .default }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
}

View File

@ -0,0 +1,27 @@
//
// SecurityCode.swift
// VDS
//
// Created by Matt Bruce on 5/9/24.
//
import Foundation
import UIKit
extension InputField {
class SecurityCodeHandler: NSObject, FieldTypeHandler {
static let shared = SecurityCodeHandler()
var keyboardType: UIKeyboardType { .numberPad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
}

View File

@ -6,6 +6,7 @@
//
import Foundation
import UIKit
extension InputField {
@ -40,4 +41,27 @@ extension InputField {
return formattedNumber
}
class TelephoneHandler: NSObject, FieldTypeHandler {
static let shared = TelephoneHandler()
var keyboardType: UIKeyboardType { .phonePad }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith {
$0.maxLength = "XXX-XXX-XXXX".count
$0.compareType = .equals
$0.errorMessage = "Enter a valid telephone."
}
inputField.rules.append(.init(rule))
}
}
}
}

View File

@ -0,0 +1,25 @@
//
// Text.swift
// VDS
//
// Created by Matt Bruce on 5/9/24.
//
import Foundation
import UIKit
extension InputField {
class TextHandler: NSObject, FieldTypeHandler {
static let shared = TextHandler()
var keyboardType: UIKeyboardType { .default }
private override init() {
super.init()
}
func configure(for inputField: InputField) {}
func appendRules(for inputField: InputField) {}
}
}

View File

@ -349,7 +349,7 @@ open class InputField: EntryFieldBase {
override func updateRules() {
super.updateRules()
fieldType.appendRules(for: self)
fieldType.handler().appendRules(for: self)
}
/// Used to update any Accessibility properties.
@ -389,31 +389,6 @@ open class InputField: EntryFieldBase {
}
return super.resignFirstResponder()
}
//--------------------------------------------------
// MARK: - Private Methods
//--------------------------------------------------
internal func cursorPosition(textField: UITextField, range: NSRange, replacementString string: String, rawNumber: String, formattedNumber: String) -> UITextPosition? {
let start = range.location
let length = string.count
let newCursorLocation = start + length
// Adjust the cursor position to skip over formatting characters
var formattedCharacterCount = 0
for (index, character) in formattedNumber.enumerated() {
if index >= newCursorLocation + formattedCharacterCount {
break
}
if !character.isNumber {
formattedCharacterCount += 1
}
}
let finalCursorLocation = min(newCursorLocation + formattedCharacterCount, formattedNumber.count)
return textField.position(from: textField.beginningOfDocument, offset: finalCursorLocation)
}
//--------------------------------------------------
// MARK: - Password
//--------------------------------------------------
@ -491,8 +466,7 @@ extension InputField: UITextFieldDelegate {
textField.text = formattedNumber
// Calculate the new cursor position
if let newPosition = cursorPosition(textField: textField,
range: range,
if let newPosition = textField.cursorPosition(range: range,
replacementString: string,
rawNumber: rawNumber,
formattedNumber: formattedNumber) {
@ -551,8 +525,7 @@ extension InputField: UITextFieldDelegate {
textField.text = formattedNumber
// Calculate the new cursor position
if let newPosition = cursorPosition(textField: textField,
range: range,
if let newPosition = textField.cursorPosition(range: range,
replacementString: string,
rawNumber: rawNumber,
formattedNumber: formattedNumber) {
@ -592,3 +565,26 @@ extension String {
}
}
extension UITextField {
internal func cursorPosition(range: NSRange, replacementString string: String, rawNumber: String, formattedNumber: String) -> UITextPosition? {
let start = range.location
let length = string.count
let newCursorLocation = start + length
// Adjust the cursor position to skip over formatting characters
var formattedCharacterCount = 0
for (index, character) in formattedNumber.enumerated() {
if index >= newCursorLocation + formattedCharacterCount {
break
}
if !character.isNumber {
formattedCharacterCount += 1
}
}
let finalCursorLocation = min(newCursorLocation + formattedCharacterCount, formattedNumber.count)
return position(from: beginningOfDocument, offset: finalCursorLocation)
}
}