diff --git a/CHANGELOG.md b/CHANGELOG.md index 03a1488a..5a829a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +- Added: advanced range-based platform version predicates (#285) +- Documentation: generate docs for extensions (#282) +- Infrastructure: set up `tea` for CI and local environments (#276) + ## [0.7.0] ### SwiftUIIntrospect diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 19008a0e..4c1be9a6 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -21,27 +21,27 @@ extension View { /// /// - Parameters: /// - viewType: The type of view to be introspected. - /// - platforms: A list of `PlatformViewVersions` that specify platform-specific entities associated with the view, with one or more corresponding version numbers. - /// - scope: An optional `IntrospectionScope` that specifies the scope of introspection. + /// - platforms: A list of version predicates that specify platform-specific entities associated with the view. + /// - scope: Optionally overrides the view's default scope of introspection. /// - customize: A closure that hands over the underlying UIKit/AppKit instance ready for customization. /// /// Here's an example usage: /// /// ```swift /// struct ContentView: View { - /// @State var date = Date() + /// @State var text = "" /// /// var body: some View { - /// DatePicker("Pick a date", selection: $date) - /// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { - /// print(type(of: $0)) // UIDatePicker + /// TextField("Placeholder", text: $text) + /// .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { + /// print(type(of: $0)) // UITextField /// } /// } /// } /// ``` public func introspect( _ viewType: SwiftUIViewType, - on platforms: (PlatformViewVersions)..., + on platforms: (PlatformViewVersionPredicate)..., scope: IntrospectionScope? = nil, customize: @escaping (PlatformSpecificEntity) -> Void ) -> some View { @@ -57,16 +57,12 @@ struct IntrospectModifier], + platforms: [PlatformViewVersionPredicate], scope: IntrospectionScope?, customize: @escaping (PlatformSpecificEntity) -> Void ) { self.scope = scope ?? viewType.scope - if let platform = platforms.first(where: \.isCurrent) { - self.selector = platform.selector ?? .default - } else { - self.selector = nil - } + self.selector = platforms.lazy.compactMap(\.selector).first self.customize = customize } diff --git a/Sources/PlatformVersion.swift b/Sources/PlatformVersion.swift index 194b5974..ad085d1c 100644 --- a/Sources/PlatformVersion.swift +++ b/Sources/PlatformVersion.swift @@ -1,195 +1,275 @@ import Foundation +public enum PlatformVersionCondition { + case past + case current + case future +} + public protocol PlatformVersion { - var isCurrent: Bool { get } + var condition: PlatformVersionCondition? { get } +} + +extension PlatformVersion { + public var isCurrent: Bool { + condition == .current + } + + public var isCurrentOrPast: Bool { + condition == .current || condition == .past + } } public struct iOSVersion: PlatformVersion { - public let isCurrent: Bool + public let condition: PlatformVersionCondition? - public init(isCurrent: () -> Bool) { - self.isCurrent = isCurrent() + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() } } extension iOSVersion { public static let v13 = iOSVersion { + #if os(iOS) if #available(iOS 14, *) { - return false + return .past } if #available(iOS 13, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v14 = iOSVersion { + #if os(iOS) if #available(iOS 15, *) { - return false + return .past } if #available(iOS 14, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v15 = iOSVersion { + #if os(iOS) if #available(iOS 16, *) { - return false + return .past } if #available(iOS 15, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v16 = iOSVersion { + #if os(iOS) if #available(iOS 17, *) { - return false + return .past } if #available(iOS 16, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v17 = iOSVersion { + #if os(iOS) if #available(iOS 18, *) { - return false + return .past } if #available(iOS 17, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } } public struct tvOSVersion: PlatformVersion { - public let isCurrent: Bool + public let condition: PlatformVersionCondition? - public init(isCurrent: () -> Bool) { - self.isCurrent = isCurrent() + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() } } extension tvOSVersion { public static let v13 = tvOSVersion { + #if os(tvOS) if #available(tvOS 14, *) { - return false + return .past } if #available(tvOS 13, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v14 = tvOSVersion { + #if os(tvOS) if #available(tvOS 15, *) { - return false + return .past } if #available(tvOS 14, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v15 = tvOSVersion { + #if os(tvOS) if #available(tvOS 16, *) { - return false + return .past } if #available(tvOS 15, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v16 = tvOSVersion { + #if os(tvOS) if #available(tvOS 17, *) { - return false + return .past } if #available(tvOS 16, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v17 = tvOSVersion { + #if os(tvOS) if #available(tvOS 18, *) { - return false + return .past } if #available(tvOS 17, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } } public struct macOSVersion: PlatformVersion { - public let isCurrent: Bool + public let condition: PlatformVersionCondition? - public init(isCurrent: () -> Bool) { - self.isCurrent = isCurrent() + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() } } extension macOSVersion { public static let v10_15 = macOSVersion { + #if os(macOS) if #available(macOS 11, *) { - return false + return .past } if #available(macOS 10.15, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v10_15_4 = macOSVersion { + #if os(macOS) if #available(macOS 11, *) { - return false + return .past } if #available(macOS 10.15.4, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v11 = macOSVersion { + #if os(macOS) if #available(macOS 12, *) { - return false + return .past } if #available(macOS 11, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v12 = macOSVersion { + #if os(macOS) if #available(macOS 13, *) { - return false + return .past } if #available(macOS 12, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v13 = macOSVersion { + #if os(macOS) if #available(macOS 14, *) { - return false + return .past } if #available(macOS 13, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v14 = macOSVersion { + #if os(macOS) if #available(macOS 15, *) { - return false + return .past } if #available(macOS 14, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } } diff --git a/Sources/PlatformViewVersion.swift b/Sources/PlatformViewVersion.swift index 9684a552..5190340a 100644 --- a/Sources/PlatformViewVersion.swift +++ b/Sources/PlatformViewVersion.swift @@ -1,31 +1,44 @@ import SwiftUI -public struct PlatformViewVersions { - let isCurrent: Bool +public struct PlatformViewVersionPredicate { let selector: IntrospectionSelector? private init( - _ versions: [PlatformViewVersion] + _ versions: [PlatformViewVersion], + matches: (PlatformViewVersion) -> Bool ) { - if let currentVersion = versions.first(where: \.isCurrent) { - self.isCurrent = true - self.selector = currentVersion.selector + if let matchingVersion = versions.first(where: matches) { + self.selector = matchingVersion.selector ?? .default } else { - self.isCurrent = false self.selector = nil } } public static func iOS(_ versions: (iOSViewVersion)...) -> Self { - Self(versions) + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func iOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) } public static func tvOS(_ versions: (tvOSViewVersion)...) -> Self { - Self(versions) + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func tvOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) } public static func macOS(_ versions: (macOSViewVersion)...) -> Self { - Self(versions) + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func macOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) } } @@ -36,14 +49,12 @@ public typealias tvOSViewVersion = PlatformViewVersion -public struct PlatformViewVersion { - let isCurrent: Bool - let selector: IntrospectionSelector? -} +public enum PlatformViewVersion { + @_spi(Private) case available(Version, IntrospectionSelector?) + @_spi(Private) case unavailable -extension PlatformViewVersion { @_spi(Internals) public init(for version: Version, selector: IntrospectionSelector? = nil) { - self.init(isCurrent: version.isCurrent, selector: selector) + self = .available(version, selector) } @_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self { @@ -60,6 +71,42 @@ extension PlatformViewVersion { https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable """ ) - return Self(isCurrent: false, selector: nil) + return .unavailable + } + + private var version: Version? { + if case .available(let version, _) = self { + return version + } else { + return nil + } + } + + fileprivate var selector: IntrospectionSelector? { + if case .available(_, let selector) = self { + return selector + } else { + return nil + } + } + + fileprivate var isCurrent: Bool { + version?.isCurrent ?? false + } + + fileprivate var isCurrentOrPast: Bool { + version?.isCurrentOrPast ?? false + } +} + +// This conformance isn't meant to be used directly by the user, +// it's only to satisfy requirements for forming ranges (e.g. `.v15...`). +extension PlatformViewVersion: Comparable { + public static func == (lhs: Self, rhs: Self) -> Bool { + true + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + true } } diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 0c191f36..1c1de178 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -38,7 +38,7 @@ D50E2F782A2B9F6600BAFB03 /* StepperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D12A23A77C0081F853 /* StepperTests.swift */; }; D50E2F792A2B9F6600BAFB03 /* ColorPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D92A23B7700081F853 /* ColorPickerTests.swift */; }; D50E2F7A2A2B9F6600BAFB03 /* ToggleWithButtonStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */; }; - D50E2F7B2A2B9F6600BAFB03 /* PlatformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */; }; + D50E2F7B2A2B9F6600BAFB03 /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */; }; D50E2F7C2A2B9F6600BAFB03 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58CE15729C621DD0081BFB0 /* TestUtils.swift */; }; D50E2F7D2A2B9F6600BAFB03 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; }; D50E2F7E2A2B9F6600BAFB03 /* TabViewWithPageStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */; }; @@ -109,7 +109,7 @@ D5ADFAD72A4A4653009494FD /* PopoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACF2A4A3E54009494FD /* PopoverTests.swift */; }; D5B67B842A0D318F007D5D9B /* TextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */; }; D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */; }; - D5F0BE6A29C0DC4900AD95AB /* PlatformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */; }; + D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */; }; D5F8D5ED2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */; }; D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */; }; /* End PBXBuildFile section */ @@ -193,7 +193,7 @@ D5F0BE4929C0DBE800AD95AB /* TestsHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestsHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestsHostApp.swift; sourceTree = ""; }; D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformTests.swift; sourceTree = ""; }; + D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformVersionTests.swift; sourceTree = ""; }; D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewWithStackStyleTests.swift; sourceTree = ""; }; D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewWithColumnsStyleTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -330,7 +330,7 @@ isa = PBXGroup; children = ( D5B67B852A0D3193007D5D9B /* ViewTypes */, - D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */, + D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */, D58CE15729C621DD0081BFB0 /* TestUtils.swift */, ); path = Tests; @@ -545,7 +545,7 @@ D50E2F782A2B9F6600BAFB03 /* StepperTests.swift in Sources */, D50E2F792A2B9F6600BAFB03 /* ColorPickerTests.swift in Sources */, D50E2F7A2A2B9F6600BAFB03 /* ToggleWithButtonStyleTests.swift in Sources */, - D50E2F7B2A2B9F6600BAFB03 /* PlatformTests.swift in Sources */, + D50E2F7B2A2B9F6600BAFB03 /* PlatformVersionTests.swift in Sources */, D50E2F7C2A2B9F6600BAFB03 /* TestUtils.swift in Sources */, D50E2F7D2A2B9F6600BAFB03 /* PickerWithSegmentedStyleTests.swift in Sources */, D50E2F7E2A2B9F6600BAFB03 /* TabViewWithPageStyleTests.swift in Sources */, @@ -610,7 +610,7 @@ D5ADFAD22A4A41CB009494FD /* SignInWithAppleButtonTests.swift in Sources */, D58119DA2A23B7700081F853 /* ColorPickerTests.swift in Sources */, D575068E2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift in Sources */, - D5F0BE6A29C0DC4900AD95AB /* PlatformTests.swift in Sources */, + D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */, D58CE15829C621DD0081BFB0 /* TestUtils.swift in Sources */, D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */, D58119CE2A23A4A70081F853 /* TabViewWithPageStyleTests.swift in Sources */, diff --git a/Tests/Tests/PlatformTests.swift b/Tests/Tests/PlatformTests.swift deleted file mode 100644 index 0c736fa3..00000000 --- a/Tests/Tests/PlatformTests.swift +++ /dev/null @@ -1,130 +0,0 @@ -import SwiftUIIntrospect -import XCTest - -final class PlatformTests: XCTestCase { - func test_iOS() { - #if os(iOS) - if #available(iOS 17, *) { - XCTAssertEqual(iOSVersion.v17.isCurrent, true) - XCTAssertEqual(iOSVersion.v16.isCurrent, false) - XCTAssertEqual(iOSVersion.v15.isCurrent, false) - XCTAssertEqual(iOSVersion.v14.isCurrent, false) - XCTAssertEqual(iOSVersion.v13.isCurrent, false) - } else if #available(iOS 16, *) { - XCTAssertEqual(iOSVersion.v17.isCurrent, false) - XCTAssertEqual(iOSVersion.v16.isCurrent, true) - XCTAssertEqual(iOSVersion.v15.isCurrent, false) - XCTAssertEqual(iOSVersion.v14.isCurrent, false) - XCTAssertEqual(iOSVersion.v13.isCurrent, false) - } else if #available(iOS 15, *) { - XCTAssertEqual(iOSVersion.v17.isCurrent, false) - XCTAssertEqual(iOSVersion.v16.isCurrent, false) - XCTAssertEqual(iOSVersion.v15.isCurrent, true) - XCTAssertEqual(iOSVersion.v14.isCurrent, false) - XCTAssertEqual(iOSVersion.v13.isCurrent, false) - } else if #available(iOS 14, *) { - XCTAssertEqual(iOSVersion.v17.isCurrent, false) - XCTAssertEqual(iOSVersion.v16.isCurrent, false) - XCTAssertEqual(iOSVersion.v15.isCurrent, false) - XCTAssertEqual(iOSVersion.v14.isCurrent, true) - XCTAssertEqual(iOSVersion.v13.isCurrent, false) - } else if #available(iOS 13, *) { - XCTAssertEqual(iOSVersion.v17.isCurrent, false) - XCTAssertEqual(iOSVersion.v16.isCurrent, false) - XCTAssertEqual(iOSVersion.v15.isCurrent, false) - XCTAssertEqual(iOSVersion.v14.isCurrent, false) - XCTAssertEqual(iOSVersion.v13.isCurrent, true) - } - #else - XCTAssertEqual(iOSVersion.v17.isCurrent, false) - XCTAssertEqual(iOSVersion.v16.isCurrent, false) - XCTAssertEqual(iOSVersion.v15.isCurrent, false) - XCTAssertEqual(iOSVersion.v14.isCurrent, false) - XCTAssertEqual(iOSVersion.v13.isCurrent, false) - #endif - } - - func test_macOS() { - #if os(macOS) - if #available(macOS 14, *) { - XCTAssertEqual(macOSVersion.v14.isCurrent, true) - XCTAssertEqual(macOSVersion.v13.isCurrent, false) - XCTAssertEqual(macOSVersion.v12.isCurrent, false) - XCTAssertEqual(macOSVersion.v11.isCurrent, false) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) - } else if #available(macOS 13, *) { - XCTAssertEqual(macOSVersion.v14.isCurrent, false) - XCTAssertEqual(macOSVersion.v13.isCurrent, true) - XCTAssertEqual(macOSVersion.v12.isCurrent, false) - XCTAssertEqual(macOSVersion.v11.isCurrent, false) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) - } else if #available(macOS 12, *) { - XCTAssertEqual(macOSVersion.v14.isCurrent, false) - XCTAssertEqual(macOSVersion.v13.isCurrent, false) - XCTAssertEqual(macOSVersion.v12.isCurrent, true) - XCTAssertEqual(macOSVersion.v11.isCurrent, false) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) - } else if #available(macOS 11, *) { - XCTAssertEqual(macOSVersion.v14.isCurrent, false) - XCTAssertEqual(macOSVersion.v13.isCurrent, false) - XCTAssertEqual(macOSVersion.v12.isCurrent, false) - XCTAssertEqual(macOSVersion.v11.isCurrent, true) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) - } else if #available(macOS 10.15, *) { - XCTAssertEqual(macOSVersion.v14.isCurrent, false) - XCTAssertEqual(macOSVersion.v13.isCurrent, false) - XCTAssertEqual(macOSVersion.v12.isCurrent, false) - XCTAssertEqual(macOSVersion.v11.isCurrent, false) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, true) - } - #else - XCTAssertEqual(macOSVersion.v14.isCurrent, false) - XCTAssertEqual(macOSVersion.v13.isCurrent, false) - XCTAssertEqual(macOSVersion.v12.isCurrent, false) - XCTAssertEqual(macOSVersion.v11.isCurrent, false) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) - #endif - } - - func test_tvOS() { - #if os(tvOS) - if #available(tvOS 17, *) { - XCTAssertEqual(tvOSVersion.v17.isCurrent, true) - XCTAssertEqual(tvOSVersion.v16.isCurrent, false) - XCTAssertEqual(tvOSVersion.v15.isCurrent, false) - XCTAssertEqual(tvOSVersion.v14.isCurrent, false) - XCTAssertEqual(tvOSVersion.v13.isCurrent, false) - } else if #available(tvOS 16, *) { - XCTAssertEqual(tvOSVersion.v17.isCurrent, false) - XCTAssertEqual(tvOSVersion.v16.isCurrent, true) - XCTAssertEqual(tvOSVersion.v15.isCurrent, false) - XCTAssertEqual(tvOSVersion.v14.isCurrent, false) - XCTAssertEqual(tvOSVersion.v13.isCurrent, false) - } else if #available(tvOS 15, *) { - XCTAssertEqual(tvOSVersion.v17.isCurrent, false) - XCTAssertEqual(tvOSVersion.v16.isCurrent, false) - XCTAssertEqual(tvOSVersion.v15.isCurrent, true) - XCTAssertEqual(tvOSVersion.v14.isCurrent, false) - XCTAssertEqual(tvOSVersion.v13.isCurrent, false) - } else if #available(tvOS 14, *) { - XCTAssertEqual(tvOSVersion.v17.isCurrent, false) - XCTAssertEqual(tvOSVersion.v16.isCurrent, false) - XCTAssertEqual(tvOSVersion.v15.isCurrent, false) - XCTAssertEqual(tvOSVersion.v14.isCurrent, true) - XCTAssertEqual(tvOSVersion.v13.isCurrent, false) - } else if #available(tvOS 13, *) { - XCTAssertEqual(tvOSVersion.v17.isCurrent, false) - XCTAssertEqual(tvOSVersion.v16.isCurrent, false) - XCTAssertEqual(tvOSVersion.v15.isCurrent, false) - XCTAssertEqual(tvOSVersion.v14.isCurrent, false) - XCTAssertEqual(tvOSVersion.v13.isCurrent, true) - } - #else - XCTAssertEqual(tvOSVersion.v17.isCurrent, false) - XCTAssertEqual(tvOSVersion.v16.isCurrent, false) - XCTAssertEqual(tvOSVersion.v15.isCurrent, false) - XCTAssertEqual(tvOSVersion.v14.isCurrent, false) - XCTAssertEqual(tvOSVersion.v13.isCurrent, false) - #endif - } -} diff --git a/Tests/Tests/PlatformVersionTests.swift b/Tests/Tests/PlatformVersionTests.swift new file mode 100644 index 00000000..7a0bb953 --- /dev/null +++ b/Tests/Tests/PlatformVersionTests.swift @@ -0,0 +1,256 @@ +import SwiftUIIntrospect +import XCTest + +final class PlatformVersionTests: XCTestCase { + func test_iOS_isCurrent() { + #if os(iOS) + if #available(iOS 17, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, true) + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 16, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) + XCTAssertEqual(iOSVersion.v16.isCurrent, true) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 15, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, true) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 14, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, true) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 13, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, true) + } + #else + XCTAssertEqual(iOSVersion.v17.isCurrent, false) + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + #endif + } + + func test_iOS_isCurrentOrPast() { + #if os(iOS) + if #available(iOS 17, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 16, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 15, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 14, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 13, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } + #else + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, false) + #endif + } + + func test_macOS_isCurrent() { + #if os(macOS) + if #available(macOS 14, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, true) + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 13, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) + XCTAssertEqual(macOSVersion.v13.isCurrent, true) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 12, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, true) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 11, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 10.15, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, true) + } + #else + XCTAssertEqual(macOSVersion.v14.isCurrent, false) + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + #endif + } + + func test_macOS_isCurrentOrPast() { + #if os(macOS) + if #available(macOS 14, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 13, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 12, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 11, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 10.15, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } + #else + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, false) + #endif + } + + func test_tvOS_isCurrent() { + #if os(tvOS) + if #available(tvOS 17, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, true) + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 16, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) + XCTAssertEqual(tvOSVersion.v16.isCurrent, true) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 15, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, true) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 14, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, true) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 13, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, true) + } + #else + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + #endif + } + + func test_tvOS_isCurrentOrPast() { + #if os(tvOS) + if #available(tvOS 17, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 16, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 15, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 14, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 13, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } + #else + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, false) + #endif + } +}