Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Validator
// Copyright © 2023 Space Code. All rights reserved.
//

#if os(macOS)
import AppKit

extension NSTextField: IUIValidatable {
/// The value of the text field to validate.
/// Returns an empty string if `text` is nil.
public var inputValue: String { stringValue }

/// The type of input for validation.
public typealias Input = String

/// Enables or disables automatic validation when the text changes.
///
/// - Parameter isEnabled: If true, validation is triggered on every text change.
/// If false, the target-action is removed.
public func validateOnInputChange(isEnabled: Bool) {
if isEnabled {
NotificationCenter.default.addObserver(
self,
selector: #selector(textFieldDidChange(_:)),
name: NSControl.textDidChangeNotification,
object: self
)
} else {
NotificationCenter.default.removeObserver(
self,
name: NSControl.textDidChangeNotification,
object: self
)
}
}

// MARK: Private

/// Called automatically when the text field changes (if `validateOnInputChange` is enabled).
/// Performs validation using all rules stored in `validationRules`.
///
/// - Parameter notification: The notification containing the text field.
@objc
private func textFieldDidChange(_ notification: Notification) {
guard notification.object as? NSTextField === self else { return }
validate(rules: validationRules)
}
}
#endif
73 changes: 73 additions & 0 deletions Tests/ValidatorUITests/UnitTests/AppKit/NSTextFieldTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// Validator
// Copyright © 2023 Space Code. All rights reserved.
//

import ValidatorCore
import ValidatorUI
import XCTest

#if canImport(AppKit)
import AppKit
#endif

#if os(macOS)
final class NSTextFieldTests: XCTestCase {
// MARK: Tests

@MainActor
func test_thatTextFieldValidationReturnsValid_whenInputValueIsValid() {
// given
let textField = NSTextField()

textField.validateOnInputChange(isEnabled: true)
textField.add(rule: LengthValidationRule(max: .max, error: String.error))

// when
textField.stringValue = String(String.text.prefix(.max))

var result: ValidationResult?

textField.validationHandler = { result = $0 }
textField.validate(rules: textField.validationRules)

// when
if case .valid = result {}
else { XCTFail("The result must be equal to the valid value") }
}

@MainActor
func test_thatTextFieldValidationReturnsInvalid_whenInputValueIsInvalid() {
// given
let textField = NSTextField()

textField.validateOnInputChange(isEnabled: true)
textField.add(rule: LengthValidationRule(max: .max, error: String.error))

// when
textField.stringValue = .text

var result: ValidationResult?

textField.validationHandler = { result = $0 }
textField.validate(rules: textField.validationRules)

// when
if case let .invalid(errors) = result {
XCTAssertEqual(errors.count, 1)
XCTAssertEqual(errors.first?.message, .error)
} else { XCTFail("The result must be equal to the invalid value") }
}
}

private extension String {
static let text: String = "lorem ipsum lorem ipsum lorem ipsum"
static let error: String = "error"
}

private extension Int {
static let min = 0
static let max = 10
}

#endif
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// Validator
// Copyright © 2025 Space Code. All rights reserved.
// Copyright © 2023 Space Code. All rights reserved.
//

import ValidatorCore
Expand Down
Loading