From 52c75267837c0ab2810a1b40e09f5817bdf0ee84 Mon Sep 17 00:00:00 2001 From: David Steinacher Date: Thu, 18 Jan 2024 00:09:37 +0100 Subject: [PATCH] Add visionOS support (#1098) * add compiler directives for visionOS support * add SPM visionOS support * add CocoaPods visionOS support * update Carthage dependencies * add visionOS support when using the Xcode project * extend test script to include visionOS * fix cocoapods support by modifying dependencies directly * do not break older Swift version support by creating a swift 5.9 explicit Package.swift file * skip tests on visionOS if it's not present * fix build issues below Xcode 15 * skip visionOS cocoapod validation if visionOS is not present * move TARGET_OS_VISION definition to umbrella header * bump CwlPreconditionTesting to 2.2.0 * link CwlPreconditionTesting on visionOS * add visionOS for to dependencies array * add CI execution for visionOS with Xcode 15.2 * remove visionOS testing on Xcode 15.2 * it looks like the visionOS simulator is not created by default on the used macOS image --- Nimble.podspec | 3 +- Nimble.xcodeproj/project.pbxproj | 12 ++-- Package.resolved | 42 ++++++------ Package@swift-5.9.swift | 65 +++++++++++++++++++ .../Nimble/Adapters/NimbleXCTestHandler.swift | 4 +- Sources/Nimble/Matchers/BeginWith.swift | 2 +- Sources/Nimble/Matchers/EndWith.swift | 2 +- Sources/Nimble/Matchers/ThrowAssertion.swift | 2 +- Sources/Nimble/Nimble.h | 9 ++- test | 46 ++++++++++++- 10 files changed, 151 insertions(+), 36 deletions(-) create mode 100644 Package@swift-5.9.swift diff --git a/Nimble.podspec b/Nimble.podspec index 2de28567f..7da3ad1c5 100644 --- a/Nimble.podspec +++ b/Nimble.podspec @@ -12,6 +12,7 @@ Pod::Spec.new do |s| s.osx.deployment_target = "10.15" s.tvos.deployment_target = "13.0" s.watchos.deployment_target = "7.0" + s.visionos.deployment_target = "1.0" s.source = { :git => "https://github.com/Quick/Nimble.git", :tag => "v#{s.version}" } @@ -34,7 +35,7 @@ Pod::Spec.new do |s| 'OTHER_SWIFT_FLAGS' => '$(inherited) -suppress-warnings', } - [s.osx, s.ios].each do |platform| + [s.osx, s.ios, s.visionos].each do |platform| platform.dependency 'CwlPreconditionTesting', '~> 2.1.0' end diff --git a/Nimble.xcodeproj/project.pbxproj b/Nimble.xcodeproj/project.pbxproj index 5fca9d4f0..4d2f55b88 100644 --- a/Nimble.xcodeproj/project.pbxproj +++ b/Nimble.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 53; + objectVersion = 55; objects = { /* Begin PBXAggregateTarget section */ @@ -143,7 +143,7 @@ 89C297CE2A92AB34002A143F /* AsyncPromiseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C297CD2A92AB34002A143F /* AsyncPromiseTest.swift */; }; 89D8AC852B3211C600410644 /* CwlCatchException in Frameworks */ = {isa = PBXBuildFile; productRef = 89D8AC842B3211C600410644 /* CwlCatchException */; }; 89D8AC872B3211EA00410644 /* CwlPosixPreconditionTesting in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, watchos, ); productRef = 89D8AC862B3211EA00410644 /* CwlPosixPreconditionTesting */; }; - 89D8AC892B3211EA00410644 /* CwlPreconditionTesting in Frameworks */ = {isa = PBXBuildFile; platformFilters = (driverkit, ios, maccatalyst, macos, ); productRef = 89D8AC882B3211EA00410644 /* CwlPreconditionTesting */; }; + 89D8AC892B3211EA00410644 /* CwlPreconditionTesting in Frameworks */ = {isa = PBXBuildFile; platformFilters = (driverkit, ios, maccatalyst, macos, xros, ); productRef = 89D8AC882B3211EA00410644 /* CwlPreconditionTesting */; }; 89EEF5A52A03293100988224 /* AsyncMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89EEF5A42A03293100988224 /* AsyncMatcher.swift */; }; 89EEF5B72A032C3200988224 /* AsyncPredicateTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89EEF5B22A032C2500988224 /* AsyncPredicateTest.swift */; }; 89EEF5C02A06211C00988224 /* AsyncHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89EEF5BB2A06210D00988224 /* AsyncHelpers.swift */; }; @@ -1183,10 +1183,10 @@ PRODUCT_NAME = Nimble; SDKROOT = macosx; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; }; name = Debug; }; @@ -1225,12 +1225,12 @@ PRODUCT_NAME = Nimble; SDKROOT = macosx; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4,7"; }; name = Release; }; diff --git a/Package.resolved b/Package.resolved index b429a9226..989bd3792 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,25 +1,23 @@ { - "object": { - "pins": [ - { - "package": "CwlCatchException", - "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", - "state": { - "branch": null, - "revision": "f809deb30dc5c9d9b78c872e553261a61177721a", - "version": "2.0.0" - } - }, - { - "package": "CwlPreconditionTesting", - "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", - "state": { - "branch": null, - "revision": "c21f7bab5ca8eee0a9998bbd17ca1d0eb45d4688", - "version": "2.1.0" - } + "pins" : [ + { + "identity" : "cwlcatchexception", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattgallagher/CwlCatchException.git", + "state" : { + "revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00", + "version" : "2.1.2" } - ] - }, - "version": 1 + }, + { + "identity" : "cwlpreconditiontesting", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", + "state" : { + "revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc", + "version" : "2.2.0" + } + } + ], + "version" : 2 } diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift new file mode 100644 index 000000000..3a52f3140 --- /dev/null +++ b/Package@swift-5.9.swift @@ -0,0 +1,65 @@ +// swift-tools-version:5.9 +import PackageDescription + +let package = Package( + name: "Nimble", + platforms: [ + .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .visionOS(.v1) + ], + products: [ + .library( + name: "Nimble", + targets: { + var targets: [String] = ["Nimble"] + #if os(macOS) + targets.append("NimbleObjectiveC") + #endif + return targets + }() + ), + ], + dependencies: [ + .package(url: "https://github.com/mattgallagher/CwlPreconditionTesting.git", .upToNextMajor(from: "2.2.0")), + ], + targets: { + var testHelperDependencies: [PackageDescription.Target.Dependency] = ["Nimble"] + #if os(macOS) + testHelperDependencies.append("NimbleObjectiveC") + #endif + var targets: [Target] = [ + .target( + name: "Nimble", + dependencies: [ + .product(name: "CwlPreconditionTesting", package: "CwlPreconditionTesting", + condition: .when(platforms: [.macOS, .iOS, .macCatalyst, .visionOS])), + .product(name: "CwlPosixPreconditionTesting", package: "CwlPreconditionTesting", + condition: .when(platforms: [.tvOS, .watchOS])) + ], + exclude: ["Info.plist"] + ), + .target( + name: "NimbleSharedTestHelpers", + dependencies: testHelperDependencies + ), + .testTarget( + name: "NimbleTests", + dependencies: ["Nimble", "NimbleSharedTestHelpers"], + exclude: ["Info.plist"] + ), + ] +#if os(macOS) + targets.append(contentsOf: [ + .target( + name: "NimbleObjectiveC", + dependencies: ["Nimble"] + ), + .testTarget( + name: "NimbleObjectiveCTests", + dependencies: ["NimbleObjectiveC", "Nimble", "NimbleSharedTestHelpers"] + ) + ]) + #endif + return targets + }(), + swiftLanguageVersions: [.v5] +) diff --git a/Sources/Nimble/Adapters/NimbleXCTestHandler.swift b/Sources/Nimble/Adapters/NimbleXCTestHandler.swift index 697dc8d80..286ae7ea6 100644 --- a/Sources/Nimble/Adapters/NimbleXCTestHandler.swift +++ b/Sources/Nimble/Adapters/NimbleXCTestHandler.swift @@ -45,7 +45,7 @@ class NimbleXCTestUnavailableHandler: AssertionHandler { private var stashed_swift_reportFatalErrorsToDebugger: Bool = false @objc public func testCaseWillStart(_ testCase: XCTestCase) { - #if (os(macOS) || os(iOS)) && !SWIFT_PACKAGE + #if (os(macOS) || os(iOS) || os(visionOS)) && !SWIFT_PACKAGE stashed_swift_reportFatalErrorsToDebugger = _swift_reportFatalErrorsToDebugger _swift_reportFatalErrorsToDebugger = false #endif @@ -56,7 +56,7 @@ class NimbleXCTestUnavailableHandler: AssertionHandler { @objc public func testCaseDidFinish(_ testCase: XCTestCase) { currentTestCase = nil - #if (os(macOS) || os(iOS)) && !SWIFT_PACKAGE + #if (os(macOS) || os(iOS) || os(visionOS)) && !SWIFT_PACKAGE _swift_reportFatalErrorsToDebugger = stashed_swift_reportFatalErrorsToDebugger #endif } diff --git a/Sources/Nimble/Matchers/BeginWith.swift b/Sources/Nimble/Matchers/BeginWith.swift index cc4a58dac..05e1b8231 100644 --- a/Sources/Nimble/Matchers/BeginWith.swift +++ b/Sources/Nimble/Matchers/BeginWith.swift @@ -17,7 +17,7 @@ public func beginWith(_ startingElement: Any) -> Matcher { return Matcher.simple("begin with <\(startingElement)>") { actualExpression in guard let collection = try actualExpression.evaluate() else { return .fail } guard collection.count > 0 else { return .doesNotMatch } - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) let collectionValue = collection.object(at: 0) as AnyObject #else guard let collectionValue = collection.object(at: 0) as? NSObject else { diff --git a/Sources/Nimble/Matchers/EndWith.swift b/Sources/Nimble/Matchers/EndWith.swift index d1fffc42e..0b61da991 100644 --- a/Sources/Nimble/Matchers/EndWith.swift +++ b/Sources/Nimble/Matchers/EndWith.swift @@ -25,7 +25,7 @@ public func endWith(_ endingElement: Any) -> Matcher { guard let collection = try actualExpression.evaluate() else { return .fail } guard collection.count > 0 else { return MatcherStatus(bool: false) } - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) let collectionValue = collection.object(at: collection.count - 1) as AnyObject #else guard let collectionValue = collection.object(at: collection.count - 1) as? NSObject else { diff --git a/Sources/Nimble/Matchers/ThrowAssertion.swift b/Sources/Nimble/Matchers/ThrowAssertion.swift index 79b2a4e96..b0c22457c 100644 --- a/Sources/Nimble/Matchers/ThrowAssertion.swift +++ b/Sources/Nimble/Matchers/ThrowAssertion.swift @@ -1,5 +1,5 @@ // swiftlint:disable all -#if canImport(CwlPreconditionTesting) && (os(macOS) || os(iOS)) +#if canImport(CwlPreconditionTesting) && (os(macOS) || os(iOS) || os(visionOS)) import CwlPreconditionTesting #elseif canImport(CwlPosixPreconditionTesting) import CwlPosixPreconditionTesting diff --git a/Sources/Nimble/Nimble.h b/Sources/Nimble/Nimble.h index 8f99fbb58..9fb6ca315 100644 --- a/Sources/Nimble/Nimble.h +++ b/Sources/Nimble/Nimble.h @@ -1,9 +1,16 @@ #import + +// When running below Xcode 15, TARGET_OS_VISION is not defined. Since the project has TREAT_WARNINGS_AS_ERROS enabled +// we need to workaround this warning. +#ifndef TARGET_OS_VISION + #define TARGET_OS_VISION 0 +#endif /* TARGET_OS_VISION */ + #import #import #import -#if TARGET_OS_OSX || TARGET_OS_IOS +#if TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_VISION #if COCOAPODS #import #import diff --git a/test b/test index 97779c98a..f48d98a3d 100755 --- a/test +++ b/test @@ -13,6 +13,8 @@ if which xcodebuild > /dev/null; then LATEST_WATCHOS_SDK_VERSION=`xcodebuild -showsdks | grep -e '-sdk watchos' | cut -d ' ' -f 2 | ruby -e 'puts STDIN.read.chomp.split("\n").last'` LATEST_WATCHOS_VERSION=`xcrun simctl list | grep ^watchOS | ruby -e 'puts /\(([0-9.]+).*\)/.match(STDIN.read.chomp.split("\n").last).to_a[1]'` LATEST_MACOS_SDK_VERSION=`xcodebuild -showsdks | grep -e '-sdk macosx' | cut -d ' ' -f 2 | ruby -e 'puts STDIN.read.chomp.split("\n").last'` + LATEST_VISIONOS_SDK_VERSION=`xcodebuild -showsdks | grep -e '-sdk xros' | cut -d ' ' -f 2 | ruby -e 'puts STDIN.read.chomp.split("\n").last'` + LATEST_VISIONOS_VERSION=`xcrun simctl list | grep ^visionOS | ruby -e 'puts /\(([0-9.]+).*\)/.match(STDIN.read.chomp.split("\n").last).to_a[1]'` BUILD_IOS_SDK_VERSION=${NIMBLE_BUILD_IOS_SDK_VERSION:-$LATEST_IOS_SDK_VERSION} RUNTIME_IOS_VERSION=${NIMBLE_RUNTIME_IOS_VERSION:-$LATEST_IOS_VERSION} BUILD_TVOS_SDK_VERSION=${NIMBLE_BUILD_TVOS_SDK_VERSION:-$LATEST_TVOS_SDK_VERSION} @@ -20,6 +22,8 @@ if which xcodebuild > /dev/null; then BUILD_WATCHOS_SDK_VERSION=${NIMBLE_BUILD_WATCHOS_SDK_VERSION:-$LATEST_WATCHOS_SDK_VERSION} RUNTIME_WATCHOS_VERSION=${NIMBLE_RUNTIME_WATCHOS_VERSION:-$LATEST_WATCHOS_VERSION} BUILD_MACOS_SDK_VERSION=${NIMBLE_BUILD_MACOS_SDK_VERSION:-$LATEST_MACOS_SDK_VERSION} + BUILD_VISIONOS_SDK_VERSION=${NIMBLE_BUILD_VISIONOS_SDK_VERSION:-$LATEST_VISIONOS_SDK_VERSION} + RUNTIME_VISIONOS_VERSION=${NIMBLE_RUNTIME_VISIONOS_VERSION:-$LATEST_VISIONOS_VERSION} fi set -e @@ -51,6 +55,11 @@ function print_env { echo " Building with watchOS SDK: `color_if_overridden $BUILD_WATCHOS_SDK_VERSION $NIMBLE_BUILD_WATCHOS_SDK_VERSION`" echo " Running with watchOS: `color_if_overridden $RUNTIME_WATCHOS_VERSION $NIMBLE_RUNTIME_WATCHOS_VERSION`" echo + echo " visionOS:" + echo " Latest visionOS SDK: $LATEST_VISIONOS_SDK_VERSION" + echo " Building with visionOS SDK: `color_if_overridden $BUILD_VISIONOS_SDK_VERSION $NIMBLE_BUILD_VISIONOS_SDK_VERSION`" + echo " Running with visionOS: `color_if_overridden $RUNTIME_VISIONOS_VERSION $NIMBLE_RUNTIME_VISIONOS_VERSION`" + echo echo " macOS:" echo " Latest macOS SDK: $LATEST_MACOS_SDK_VERSION" echo " Building with macOS SDK: `color_if_overridden $BUILD_MACOS_SDK_VERSION $NIMBLE_BUILD_MACOS_SDK_VERSION`" @@ -85,6 +94,18 @@ function test_watchos { run set -o pipefail && xcodebuild -project Nimble.xcodeproj -scheme "Nimble" -configuration "Debug" -sdk "watchsimulator$BUILD_WATCHOS_SDK_VERSION" -destination "name=Apple Watch Series 6 (40mm),OS=$RUNTIME_WATCHOS_VERSION" OTHER_SWIFT_FLAGS='$(inherited) -suppress-warnings' build-for-testing test-without-building | xcpretty } +function test_visionos { + if [[ -z $LATEST_VISIONOS_SDK_VERSION ]]; then + echo "visionOS is not supported on this machine" + return + fi + + run set -o pipefail && xcodebuild -project Nimble.xcodeproj -scheme "Nimble" -configuration "Debug" -destination "generic/platform=visionOS" OTHER_SWIFT_FLAGS='$(inherited) -suppress-warnings' build | xcpretty + + run osascript -e 'tell app "Simulator" to quit' + run set -o pipefail && xcodebuild -project Nimble.xcodeproj -scheme "Nimble" -configuration "Debug" -sdk "xrsimulator$BUILD_VISIONOS_SDK_VERSION" -destination "name=Apple Vision Pro),OS=$RUNTIME_VISIONOS_VERSION" OTHER_SWIFT_FLAGS='$(inherited) -suppress-warnings' build-for-testing test-without-building | xcpretty +} + function test_macos { run set -o pipefail && xcodebuild -project Nimble.xcodeproj -scheme "Nimble" -configuration "Debug" -sdk "macosx$BUILD_MACOS_SDK_VERSION" OTHER_SWIFT_FLAGS='$(inherited) -suppress-warnings' build-for-testing test-without-building | xcpretty } @@ -116,11 +137,28 @@ function test_xcode_spm_watchos { run set -o pipefail && xcodebuild -scheme "Nimble" -configuration "Debug" -sdk "watchsimulator$BUILD_WATCHOS_SDK_VERSION" -destination "name=Apple Watch Series 6 (40mm),OS=$RUNTIME_WATCHOS_VERSION" OTHER_SWIFT_FLAGS='$(inherited) -suppress-warnings' build-for-testing test-without-building | xcpretty } +function test_xcode_spm_visionos { + if [[ -z $LATEST_VISIONOS_SDK_VERSION ]]; then + echo "visionOS is not supported on this machine" + return + fi + + run osascript -e 'tell app "Simulator" to quit' + mv Nimble.xcodeproj Nimble.xcodeproj.bak + trap 'mv Nimble.xcodeproj.bak Nimble.xcodeproj' EXIT + run set -o pipefail && xcodebuild -scheme "Nimble" -configuration "Debug" -sdk "xrsimulator$BUILD_WATCHOS_SDK_VERSION" -destination "name=Apple Vision Pro,OS=$RUNTIME_WATCHOS_VERSION" OTHER_SWIFT_FLAGS='$(inherited) -suppress-warnings' build-for-testing test-without-building | xcpretty +} + function test_podspec { + PLATFORMS="ios,tvos,watchos,macos" + if [[ ! -z $LATEST_VISIONOS_SDK_VERSION ]]; then + PLATFORMS="$PLATFORMS,visionos" + fi + echo "Gathering CocoaPods installation information..." run bundle exec pod --version echo "Linting podspec..." - run bundle exec pod lib lint Nimble.podspec --skip-import-validation --verbose + run bundle exec pod lib lint Nimble.podspec --skip-import-validation --verbose --platforms=$PLATFORMS } function test_carthage { @@ -147,12 +185,14 @@ function test() { test_ios test_tvos test_watchos + test_visionos if xcodebuild --help 2>&1 | grep xcframework > /dev/null; then test_xcode_spm_macos test_xcode_spm_ios test_xcode_spm_tvos test_xcode_spm_watchos + test_xcode_spm_visionos else echo "Not testing with Swift Package Manager version of Xcode because it requires at least Xcode 11" fi @@ -189,6 +229,8 @@ function help { echo " tvos_xcodespm - Runs the tests as an tvOS device using the Swift Package Manager version of Xcode" echo " watchos - Runs the tests as an watchOS device" echo " watchos_xcodespm - Runs the tests as an watchOS device using the Swift Package Manager version of Xcode" + echo " visionos - Runs the tests as an visionOS device" + echo " visionos_xcodespm - Runs the tests as an visionOS device using the Swift Package Manager version of Xcode" echo " podspec - Runs pod lib lint against the podspec to detect breaking changes" echo " carthage - Runs verifyies that the carthage artifacts build" echo " swiftpm - Runs the tests built by the Swift Package Manager" @@ -209,6 +251,8 @@ function main { tvos_xcodespm) test_xcode_spm_tvos ;; watchos) test_watchos ;; watchos_xcodespm) test_xcode_spm_watchos ;; + visionos) test_visionos ;; + visionos_xcodespm) test_xcode_spm_visionos ;; macos) test_macos ;; macos_xcodespm) test_xcode_spm_macos ;; podspec) test_podspec ;;