Skip to content

Commit bb9a3ba

Browse files
committed
Implement UIValidator package
- Remove `AnyValidationRule` - Get rid of support for older operating systems
1 parent 38287fa commit bb9a3ba

15 files changed

Lines changed: 234 additions & 122 deletions

File tree

.swiftpm/xcode/xcshareddata/xcschemes/Validator-Package.xcscheme

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,6 @@
6565
BlueprintName = "ValidatorCore"
6666
ReferencedContainer = "container:">
6767
</BuildableReference>
68-
<BuildableReference
69-
BuildableIdentifier = "primary"
70-
BlueprintIdentifier = "ValidatorUI"
71-
BuildableName = "ValidatorUI"
72-
BlueprintName = "ValidatorUI"
73-
ReferencedContainer = "container:">
74-
</BuildableReference>
7568
</CodeCoverageTargets>
7669
<Testables>
7770
<TestableReference
@@ -84,6 +77,26 @@
8477
ReferencedContainer = "container:">
8578
</BuildableReference>
8679
</TestableReference>
80+
<TestableReference
81+
skipped = "NO">
82+
<BuildableReference
83+
BuildableIdentifier = "primary"
84+
BlueprintIdentifier = "ValidatorCoreTests"
85+
BuildableName = "ValidatorCoreTests"
86+
BlueprintName = "ValidatorCoreTests"
87+
ReferencedContainer = "container:">
88+
</BuildableReference>
89+
</TestableReference>
90+
<TestableReference
91+
skipped = "NO">
92+
<BuildableReference
93+
BuildableIdentifier = "primary"
94+
BlueprintIdentifier = "ValidatorUITests"
95+
BuildableName = "ValidatorUITests"
96+
BlueprintName = "ValidatorUITests"
97+
ReferencedContainer = "container:">
98+
</BuildableReference>
99+
</TestableReference>
87100
</Testables>
88101
</TestAction>
89102
<LaunchAction

Package.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
// swift-tools-version: 5.5
1+
// swift-tools-version: 5.7
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
55

66
let package = Package(
77
name: "Validator",
88
platforms: [
9-
.iOS(.v13),
10-
.macOS(.v10_13),
11-
.watchOS(.v7),
12-
.tvOS(.v13),
9+
.iOS(.v16),
10+
.macOS(.v13),
11+
.watchOS(.v9),
12+
.tvOS(.v16),
1313
],
1414
products: [
1515
.library(name: "ValidatorCore", targets: ["ValidatorCore"]),
@@ -19,6 +19,7 @@ let package = Package(
1919
targets: [
2020
.target(name: "ValidatorCore", dependencies: []),
2121
.target(name: "ValidatorUI", dependencies: ["ValidatorCore"]),
22-
.testTarget(name: "ValidatorTests", dependencies: ["ValidatorCore"]),
22+
.testTarget(name: "ValidatorCoreTests", dependencies: ["ValidatorCore"]),
23+
.testTarget(name: "ValidatorUITests", dependencies: ["ValidatorCore", "ValidatorUI"]),
2324
]
2425
)

Sources/ValidatorCore/Classes/Extensions/IValidationRule+Erase.swift

Lines changed: 0 additions & 12 deletions
This file was deleted.

Sources/ValidatorCore/Classes/Rules/AnyValidationRule.swift

Lines changed: 0 additions & 27 deletions
This file was deleted.

Sources/ValidatorCore/Classes/Rules/LengthValidationRule.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public struct LengthValidationRule: IValidationRule {
2424

2525
// MARK: Initialization
2626

27-
public init(min: Int, max: Int, error: IValidationError) {
27+
public init(min: Int = .zero, max: Int = .max, error: IValidationError) {
2828
self.min = min
2929
self.max = max
3030
self.error = error

Sources/ValidatorCore/IValidator.swift

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,6 @@ public protocol IValidator {
1515
/// - rule: The validation rule.
1616
///
1717
/// - Returns: A validation result.
18-
func validate<T: IValidationRule>(input: T.Input, rule: T) -> ValidationResult
19-
20-
/// Validates an input value.
21-
///
22-
/// - Parameters:
23-
/// - input: The input value.
24-
/// - rules: The validation rules array.
25-
///
26-
/// - Returns: A validation result.
27-
func validate<T>(input: T, rules: [AnyValidationRule<T>]) -> ValidationResult
28-
29-
/// Validates an input value.
30-
///
31-
/// - Parameters:
32-
/// - input: The input value.
33-
/// - rule: The validation rule.
34-
///
35-
/// - Returns: A validation result.
36-
@available(macOS 13.0, iOS 16, tvOS 16, watchOS 9, *)
3718
func validate<T>(input: T, rule: some IValidationRule<T>) -> ValidationResult
3819

3920
/// Validates an input value.
@@ -43,6 +24,5 @@ public protocol IValidator {
4324
/// - rules: The validation rules array.
4425
///
4526
/// - Returns: A validation result.
46-
@available(macOS 13.0, iOS 16, tvOS 16, watchOS 9, *)
4727
func validate<T>(input: T, rules: [any IValidationRule<T>]) -> ValidationResult
4828
}

Sources/ValidatorCore/Validator.swift

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,10 @@ public final class Validator {
1414
// MARK: IValidator
1515

1616
extension Validator: IValidator {
17-
public func validate<T>(input: T.Input, rule: T) -> ValidationResult where T: IValidationRule {
18-
validate(input: input, rules: [rule.eraseToAnyValidationRule()])
17+
public func validate<T>(input: T, rule: some IValidationRule<T>) -> ValidationResult {
18+
validate(input: input, rules: [rule])
1919
}
2020

21-
public func validate<T>(input: T, rules: [AnyValidationRule<T>]) -> ValidationResult {
22-
let errors = rules
23-
.filter { !$0.validate(input: input) }
24-
.map(\.error)
25-
26-
return errors.isEmpty ? .valid : ValidationResult.invalid(errors: errors)
27-
}
28-
29-
@available(macOS 13.0, iOS 16, tvOS 16, watchOS 9, *)
3021
public func validate<T>(input: T, rules: [any IValidationRule<T>]) -> ValidationResult {
3122
let errors = rules
3223
.filter { !self.validate(input: input, rule: $0) }
@@ -35,8 +26,7 @@ extension Validator: IValidator {
3526
return errors.isEmpty ? .valid : ValidationResult.invalid(errors: errors)
3627
}
3728

38-
@available(macOS 13.0, iOS 16, tvOS 16, watchOS 9, *)
39-
func validate<T>(input: T, rule: some IValidationRule<T>) -> Bool {
29+
private func validate<T>(input: T, rule: some IValidationRule<T>) -> Bool {
4030
rule.validate(input: input)
4131
}
4232
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// Validator
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
#if os(iOS)
7+
import UIKit
8+
9+
extension UITextField: IUIValidatable {
10+
public var inputValue: String { text ?? "" }
11+
12+
public typealias Input = String
13+
14+
public func validateOnInputChange(isEnabled: Bool) {
15+
if isEnabled {
16+
addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
17+
} else {
18+
removeTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
19+
}
20+
}
21+
22+
// MARK: Private
23+
24+
@objc
25+
private func textFieldDidChange(_: UITextField) {
26+
validate(rules: validationRules)
27+
}
28+
}
29+
#endif
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// Validator
3+
// Copyright © 2023 Space Code. All rights reserved.
4+
//
5+
6+
// swiftlint:disable prefixed_toplevel_constant
7+
8+
import Foundation
9+
import ValidatorCore
10+
11+
// MARK: - IUIValidatable
12+
13+
public protocol IUIValidatable: AnyObject {
14+
associatedtype Input
15+
16+
/// The input value.
17+
var inputValue: Input { get }
18+
19+
/// Validates an input value.
20+
///
21+
/// - Parameters:
22+
/// - rule: The validation rule.
23+
///
24+
/// - Returns: A validation result.
25+
func validate<T>(rule: some IValidationRule<T>) -> ValidationResult where T == Input
26+
27+
/// Validates an input value.
28+
///
29+
/// - Parameters:
30+
/// - rules: The validation rules array.
31+
///
32+
/// - Returns: A validation result.
33+
func validate<T>(rules: [any IValidationRule<T>]) -> ValidationResult where T == Input
34+
35+
/// Validates an input value.
36+
///
37+
/// - Parameter isEnabled: The
38+
func validateOnInputChange(isEnabled: Bool)
39+
}
40+
41+
private var kValidationRules: UInt8 = 0
42+
private var kValidationHandler: UInt8 = 0
43+
44+
private let validator = Validator()
45+
46+
public extension IUIValidatable {
47+
@discardableResult
48+
func validate<T>(rule: some IValidationRule<T>) -> ValidationResult where T == Input {
49+
let result = validator.validate(input: inputValue, rule: rule)
50+
validationHandler?(result)
51+
return result
52+
}
53+
54+
@discardableResult
55+
func validate<T>(rules: [any IValidationRule<T>]) -> ValidationResult where T == Input {
56+
let result = validator.validate(input: inputValue, rules: rules)
57+
validationHandler?(result)
58+
return result
59+
}
60+
61+
func add<T>(rule: some IValidationRule<T>) where T == Input {
62+
validationRules.append(rule)
63+
}
64+
65+
var validationRules: [any IValidationRule<Input>] {
66+
get {
67+
(objc_getAssociatedObject(self, &kValidationRules) as? AnyObject) as? [any IValidationRule<Input>] ?? []
68+
}
69+
set {
70+
objc_setAssociatedObject(
71+
self,
72+
&kValidationRules,
73+
newValue as [any IValidationRule<Input>],
74+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
75+
)
76+
}
77+
}
78+
79+
var validationHandler: ((ValidationResult) -> Void)? {
80+
get {
81+
objc_getAssociatedObject(self, &kValidationHandler) as? ((ValidationResult) -> Void)
82+
}
83+
set {
84+
if let newValue = newValue {
85+
objc_setAssociatedObject(self, &kValidationHandler, newValue as AnyObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
86+
}
87+
}
88+
}
89+
}

Sources/ValidatorUI/ValidatorUI.swift

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)