diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cb5e0f..720ea5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,12 @@ jobs: fail-fast: false matrix: include: + - xcode: "Xcode_16.0" + runsOn: macOS-14 + name: "macOS 14, Xcode 16.0, Swift 6.0" + - xcode: "Xcode_15.4" + runsOn: macOS-14 + name: "macOS 14, Xcode 15.4, Swift 5.10" - xcode: "Xcode_15.0" runsOn: macos-13 name: "macOS 13, Xcode 15.0, Swift 5.9.0" @@ -44,16 +50,12 @@ jobs: - uses: actions/checkout@v3 - name: ${{ matrix.name }} run: xcodebuild test -scheme "Validator-Package" -destination "platform=macOS" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 + - name: Upload test coverage reports to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main with: + scheme_name: Typhoon + filename: ${{ matrix.name }} token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: test_output/${{ matrix.name }}.xcresult - - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.name }} - path: test_output iOS: name: ${{ matrix.name }} @@ -65,6 +67,18 @@ jobs: fail-fast: false matrix: include: + - destination: "OS=18.1,name=iPhone 16 Pro" + name: "iOS 18.1" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - destination: "OS=18.0,name=iPhone 16 Pro" + name: "iOS 18.0" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - destination: "OS=17.5,name=iPhone 15 Pro" + name: "iOS 17.5" + xcode: "Xcode_15.4" + runsOn: macOS-14 - destination: "OS=17.0.1,name=iPhone 14 Pro" name: "iOS 17.0.1" xcode: "Xcode_15.0" @@ -92,6 +106,18 @@ jobs: fail-fast: false matrix: include: + - destination: "OS=18.1,name=Apple TV" + name: "tvOS 18.1" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - destination: "OS=18.0,name=Apple TV" + name: "tvOS 18.0" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - destination: "OS=17.5,name=Apple TV" + name: "tvOS 17.5" + xcode: "Xcode_15.4" + runsOn: macOS-14 - destination: "OS=17.0,name=Apple TV" name: "tvOS 17.0" xcode: "Xcode_15.0" @@ -104,16 +130,12 @@ jobs: - uses: actions/checkout@v3 - name: ${{ matrix.name }} run: xcodebuild test -scheme "Validator-Package" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 + - name: Upload test coverage reports to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main with: + scheme_name: Typhoon + filename: ${{ matrix.name }} token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: test_output/${{ matrix.name }}.xcresult - - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.name }} - path: test_output watchOS: name: ${{ matrix.name }} @@ -125,6 +147,18 @@ jobs: fail-fast: false matrix: include: + - destination: "OS=11.1,name=Apple Watch Series 10 (46mm)" + name: "watchOS 11.1" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - destination: "OS=11.0,name=Apple Watch Series 10 (46mm)" + name: "watchOS 11.0" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - destination: "OS=10.5,name=Apple Watch Series 9 (45mm)" + name: "watchOS 10.5" + xcode: "Xcode_15.4" + runsOn: macOS-14 - destination: "OS=10.0,name=Apple Watch Series 9 (45mm)" name: "watchOS 10.0" xcode: "Xcode_15.0" @@ -137,16 +171,12 @@ jobs: - uses: actions/checkout@v3 - name: ${{ matrix.name }} run: xcodebuild test -scheme "Validator-Package" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 + - name: Upload test coverage reports to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main with: + scheme_name: Typhoon + filename: ${{ matrix.name }} token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: test_output/${{ matrix.name }}.xcresult - - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.name }} - path: test_output spm: name: ${{ matrix.name }} @@ -158,10 +188,16 @@ jobs: fail-fast: false matrix: include: - - name: "Xcode 15" + - name: "macOS 14, SPM 6.0.2 Test" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - name: "macOS 14, SPM 6.0.0 Test" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - name: "macOS 14, SPM 5.9.0 Test" xcode: "Xcode_15.0" - runsOn: macos-13 - - name: "Xcode 14" + runsOn: macos-14 + - name: "macOS 13, SPM 5.8.1 Test" xcode: "Xcode_14.3.1" runsOn: macos-13 steps: @@ -186,7 +222,7 @@ jobs: discover-typos: name: Discover Typos - runs-on: macOS-12 + runs-on: macOS-13 env: DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer steps: diff --git a/.swiftlint.yml b/.swiftlint.yml index 850b436..c9247e2 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -3,6 +3,8 @@ excluded: - Package.swift - Package@swift-5.7.swift - Package@swift-5.8.swift + - Package@swift-5.9.swift + - Package@swift-5.10.swift - .build # Rules @@ -131,4 +133,4 @@ nesting: type_name: max_length: warning: 40 - error: 50 \ No newline at end of file + error: 50 diff --git a/CHANGELOG.md b/CHANGELOG.md index cfd58f2..15954b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +## [Unreleased] + +#### Added +- Increase the Swift version to 6.0. + - Added in Pull Request [#13](https://github.com/space-code/validator/pull/13). + #### 1.x Releases - `1.0.x` Releases - [1.0.0](#100) | [1.0.1](#101) diff --git a/Mintfile b/Mintfile index 1f32d33..ebeb1c1 100644 --- a/Mintfile +++ b/Mintfile @@ -1,2 +1,2 @@ -nicklockwood/SwiftFormat@0.47.12 -realm/SwiftLint@0.47.1 \ No newline at end of file +nicklockwood/SwiftFormat@0.54.0 +realm/SwiftLint@0.55.1 diff --git a/Package.swift b/Package.swift index 715d497..ea031d0 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.9 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Package@swift-5.10.swift b/Package@swift-5.10.swift new file mode 100644 index 0000000..65b9198 --- /dev/null +++ b/Package@swift-5.10.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Validator", + platforms: [ + .iOS(.v16), + .macOS(.v13), + .watchOS(.v9), + .tvOS(.v16), + .visionOS(.v1), + ], + products: [ + .library(name: "ValidatorCore", targets: ["ValidatorCore"]), + .library(name: "ValidatorUI", targets: ["ValidatorUI"]), + ], + dependencies: [], + targets: [ + .target(name: "ValidatorCore", dependencies: []), + .target(name: "ValidatorUI", dependencies: ["ValidatorCore"]), + .testTarget(name: "ValidatorCoreTests", dependencies: ["ValidatorCore"]), + .testTarget(name: "ValidatorUITests", dependencies: ["ValidatorCore", "ValidatorUI"]), + ] +) diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift new file mode 100644 index 0000000..715d497 --- /dev/null +++ b/Package@swift-5.9.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Validator", + platforms: [ + .iOS(.v16), + .macOS(.v13), + .watchOS(.v9), + .tvOS(.v16), + .visionOS(.v1), + ], + products: [ + .library(name: "ValidatorCore", targets: ["ValidatorCore"]), + .library(name: "ValidatorUI", targets: ["ValidatorUI"]), + ], + dependencies: [], + targets: [ + .target(name: "ValidatorCore", dependencies: []), + .target(name: "ValidatorUI", dependencies: ["ValidatorCore"]), + .testTarget(name: "ValidatorCoreTests", dependencies: ["ValidatorCore"]), + .testTarget(name: "ValidatorUITests", dependencies: ["ValidatorCore", "ValidatorUI"]), + ] +) diff --git a/Sources/ValidatorCore/Classes/Extensions/String+IValidationError.swift b/Sources/ValidatorCore/Classes/Extensions/String+IValidationError.swift index 74419e4..8fce1b0 100644 --- a/Sources/ValidatorCore/Classes/Extensions/String+IValidationError.swift +++ b/Sources/ValidatorCore/Classes/Extensions/String+IValidationError.swift @@ -5,6 +5,12 @@ import Foundation +#if hasFeature(RetroactiveAttribute) + extension String: @retroactive Error {} +#endif + +// MARK: - String + IValidationError + extension String: IValidationError { public var message: String { self } } diff --git a/Sources/ValidatorUI/Classes/IUIValidatable.swift b/Sources/ValidatorUI/Classes/IUIValidatable.swift index c7cb980..7728687 100644 --- a/Sources/ValidatorUI/Classes/IUIValidatable.swift +++ b/Sources/ValidatorUI/Classes/IUIValidatable.swift @@ -3,13 +3,12 @@ // Copyright © 2023 Space Code. All rights reserved. // -// swiftlint:disable prefixed_toplevel_constant - import Foundation import ValidatorCore // MARK: - IUIValidatable +@MainActor public protocol IUIValidatable: AnyObject { associatedtype Input @@ -38,27 +37,36 @@ public protocol IUIValidatable: AnyObject { func validateOnInputChange(isEnabled: Bool) } -private var kValidationRules: UInt8 = 0 -private var kValidationHandler: UInt8 = 0 +#if swift(>=5.10) + private nonisolated(unsafe) var kValidationRules: UInt8 = 0 + private nonisolated(unsafe) var kValidationHandler: UInt8 = 0 + + // swiftlint:disable:next prefixed_toplevel_constant + private nonisolated(unsafe) let validator = Validator() +#else + private var kValidationRules: UInt8 = 0 + private var kValidationHandler: UInt8 = 0 -private let validator = Validator() + // swiftlint:disable:next prefixed_toplevel_constant + private let validator = Validator() +#endif public extension IUIValidatable { @discardableResult - func validate(rule: some IValidationRule) -> ValidationResult where T == Input { + func validate(rule: some IValidationRule) -> ValidationResult { let result = validator.validate(input: inputValue, rule: rule) validationHandler?(result) return result } @discardableResult - func validate(rules: [any IValidationRule]) -> ValidationResult where T == Input { + func validate(rules: [any IValidationRule]) -> ValidationResult { let result = validator.validate(input: inputValue, rules: rules) validationHandler?(result) return result } - func add(rule: some IValidationRule) where T == Input { + func add(rule: some IValidationRule) { validationRules.append(rule) } @@ -81,7 +89,7 @@ public extension IUIValidatable { objc_getAssociatedObject(self, &kValidationHandler) as? ((ValidationResult) -> Void) } set { - if let newValue = newValue { + if let newValue { objc_setAssociatedObject(self, &kValidationHandler, newValue as AnyObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } diff --git a/Sources/ValidatorUI/Classes/SUI/Extensions/View+Validation.swift b/Sources/ValidatorUI/Classes/SUI/Extensions/View+Validation.swift index f54f2db..0682177 100644 --- a/Sources/ValidatorUI/Classes/SUI/Extensions/View+Validation.swift +++ b/Sources/ValidatorUI/Classes/SUI/Extensions/View+Validation.swift @@ -3,12 +3,16 @@ // Copyright © 2023 Space Code. All rights reserved. // -// swiftlint:disable prefixed_toplevel_constant - import SwiftUI import ValidatorCore -private let validator = Validator() +#if swift(>=5.10) + // swiftlint:disable:next prefixed_toplevel_constant + private nonisolated(unsafe) let validator = Validator() +#else + // swiftlint:disable:next prefixed_toplevel_constant + private let validator = Validator() +#endif public extension View { /// Validate a binding item using a set of validation rules and perform an action based on diff --git a/Tests/ValidatorUITests/UnitTests/UITextFieldTests.swift b/Tests/ValidatorUITests/UnitTests/UITextFieldTests.swift index 9eefe9c..8284dfb 100644 --- a/Tests/ValidatorUITests/UnitTests/UITextFieldTests.swift +++ b/Tests/ValidatorUITests/UnitTests/UITextFieldTests.swift @@ -13,26 +13,13 @@ import XCTest #if os(iOS) final class UITextFieldTests: XCTestCase { - // MARK: Properties - - private var textField: UITextField! - - // MARK: XCTestCase - - override func setUp() { - super.setUp() - textField = UITextField() - } - - override func tearDown() { - textField = nil - super.tearDown() - } - // MARK: Tests + @MainActor func test_thatTextFieldValidationReturnsValid_whenInputValueIsValid() { // given + let textField = UITextField() + textField.validateOnInputChange(isEnabled: true) textField.add(rule: LengthValidationRule(max: .max, error: String.error)) @@ -49,8 +36,11 @@ import XCTest else { XCTFail("The result must be equal to the valid value") } } + @MainActor func test_thatTextFieldValidationReturnsInvalid_whenInputValueIsInvalid() { // given + let textField = UITextField() + textField.validateOnInputChange(isEnabled: true) textField.add(rule: LengthValidationRule(max: .max, error: String.error))