From 5d1cb6d40f70de8cca0d9ace8f8cc7755b9207b9 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Tue, 21 Jul 2026 17:53:54 -0300 Subject: [PATCH] feat: enable TestRunner suite and metadata availability checks on visionOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes that make the full TestRunner suite build, run, and pass on visionOS for the first time, and make API availability filtering real on that platform: - project.pbxproj: TestFixtures and TestRunnerTests now declare xros/xrsimulator in SUPPORTED_PLATFORMS, and TestRunner's dependency on TestFixtures drops its ios-only platformFilter. Previously Xcode silently skipped building libTestFixtures.a for visionOS and the TestRunner link failed. - TNSVersions.h: the two 'never available' fixtures gain an explicit visionos availability attribute. Clang's ios-to-visionos mapping turned the ios-only MAX_AVAILABILITY attribute into a hard 'unavailable on visionOS' error at their reference sites; declaring visionos explicitly keeps them warning-level (potentially unavailable), matching iOS semantics. Metadata output is unchanged. - Metadata.mm: Meta::isAvailable() no longer short-circuits to true on visionOS. getSystemVersion() now also runs there, mapping pre-unified visionOS majors onto the iOS scale used by metadata introducedIn values (visionOS 1.x ~ iOS 17.x, 2.x ~ 18.x; aligned from 26 on). Before this, every API was reported available on visionOS, so metadata for APIs that do not exist there leaked into JS. Validated on visionOS 26.2 simulator (Apple Vision Pro): full suite 849 tests, 0 failures — including VersionDiffTests, which previously failed because unavailable base classes were not skipped. iOS regression run: 849 tests, 0 failures. --- NativeScript/runtime/Metadata.mm | 459 ++++++++++++++++--------------- TestFixtures/Api/TNSVersions.h | 8 +- v8ios.xcodeproj/project.pbxproj | 9 +- 3 files changed, 257 insertions(+), 219 deletions(-) diff --git a/NativeScript/runtime/Metadata.mm b/NativeScript/runtime/Metadata.mm index 69442877..7f75c459 100644 --- a/NativeScript/runtime/Metadata.mm +++ b/NativeScript/runtime/Metadata.mm @@ -1,304 +1,331 @@ #include "Metadata.h" #include #include -#include "SymbolLoader.h" #include "Helpers.h" +#include "SymbolLoader.h" namespace tns { using namespace std; -void LogMetadataUnavailable(const char* identifierString, uint8_t majorVersion, uint8_t minorVersion, const char* baseName) { - Log(@"** \"%s\" introduced in iOS SDK %d.%d is currently unavailable, attempting to load its base: \"%s\". **", - identifierString, - majorVersion, - minorVersion, - baseName - ); +void LogMetadataUnavailable(const char* identifierString, uint8_t majorVersion, + uint8_t minorVersion, const char* baseName) { + Log(@"** \"%s\" introduced in iOS SDK %d.%d is currently unavailable, attempting to load its " + @"base: \"%s\". **", + identifierString, majorVersion, minorVersion, baseName); } -#if !TARGET_OS_VISION /** - * \brief Gets the system version of the current device. + * \brief Gets the system version of the current device, expressed on the iOS + * version scale used by metadata introducedIn values. */ static UInt8 getSystemVersion() { - static UInt8 iosVersion; - if (iosVersion != 0) { - return iosVersion; - } - - NSString* version = [[UIDevice currentDevice] systemVersion]; - NSArray* versionTokens = [version componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]; - UInt8 majorVersion = (UInt8)[versionTokens[0] intValue]; - UInt8 minorVersion = (UInt8)[versionTokens[1] intValue]; + static UInt8 iosVersion; + if (iosVersion != 0) { + return iosVersion; + } + + NSString* version = [[UIDevice currentDevice] systemVersion]; + NSArray* versionTokens = + [version componentsSeparatedByCharactersInSet:[NSCharacterSet + characterSetWithCharactersInString:@"."]]; + UInt8 majorVersion = (UInt8)[versionTokens[0] intValue]; + UInt8 minorVersion = (UInt8)[versionTokens[1] intValue]; + +#if TARGET_OS_VISION + // Metadata availability is expressed in iOS versions. visionOS majors + // before the unified 26 numbering trail iOS by 16 (visionOS 1.x ~ iOS + // 17.x, visionOS 2.x ~ iOS 18.x); from 26 onward the numbering aligns. + if (majorVersion < 26) { + majorVersion += 16; + } +#endif - return iosVersion = encodeVersion(majorVersion, minorVersion); + return iosVersion = encodeVersion(majorVersion, minorVersion); } -#endif -robin_hood::unordered_map getMetasByJSNames(MembersCollection members) { - robin_hood::unordered_map result; - for (auto member : members) { - result[member->jsName()].insert(member); - } - return result; +robin_hood::unordered_map getMetasByJSNames( + MembersCollection members) { + robin_hood::unordered_map result; + for (auto member : members) { + result[member->jsName()].insert(member); + } + return result; } // Meta bool Meta::isAvailable() const { - #if TARGET_OS_VISION - return true; - #else - UInt8 introducedIn = this->introducedIn(); - UInt8 systemVersion = getSystemVersion(); - return introducedIn == 0 || introducedIn <= systemVersion; - #endif + UInt8 introducedIn = this->introducedIn(); + UInt8 systemVersion = getSystemVersion(); + return introducedIn == 0 || introducedIn <= systemVersion; } // MethodMeta class bool MethodMeta::isImplementedInClass(Class klass, bool isStatic) const { - // class can be null for Protocol prototypes, treat all members in a protocol as implemented - if (klass == nullptr) { - return true; + // class can be null for Protocol prototypes, treat all members in a protocol as implemented + if (klass == nullptr) { + return true; + } + + // Some members are implemented by extension of classes defined in a different + // module than the class, ensure they've been initialized + SymbolLoader::instance().ensureModule(this->topLevelModule()); + + if (isStatic) { + return [klass respondsToSelector:this->selector()] || + ([klass resolveClassMethod:this->selector()]); + } else { + if ([klass instancesRespondToSelector:this->selector()] || + [klass resolveInstanceMethod:this->selector()]) { + return true; } - // Some members are implemented by extension of classes defined in a different - // module than the class, ensure they've been initialized - SymbolLoader::instance().ensureModule(this->topLevelModule()); - - if (isStatic) { - return [klass respondsToSelector:this->selector()] || ([klass resolveClassMethod:this->selector()]); - } else { - if ([klass instancesRespondToSelector:this->selector()] || [klass resolveInstanceMethod:this->selector()]) { - return true; - } - - // Last resort - allocate an object and ask it if it supports the selector. - // There are two kinds of scenarios that need this additional check: - // 1. The `alloc` method is overridden and returns an instance of another class - // E.g. [NSAttributedString alloc] returns a NSConcreteAttributedString* - // 2. The message is forwarded to another object. E.g. `UITextField` forwards - // `autocapitalizationType` to an instance of `UITextInputTraits` - static robin_hood::unordered_map sampleInstances; - - auto it = sampleInstances.find(klass); - if (it == sampleInstances.end()) { - @try { - it = sampleInstances.emplace(klass, [klass alloc]).first; - } - @catch(id err) { - return false; - } - } - id sampleInstance = it->second; - return [sampleInstance respondsToSelector:this->selector()]; + // Last resort - allocate an object and ask it if it supports the selector. + // There are two kinds of scenarios that need this additional check: + // 1. The `alloc` method is overridden and returns an instance of another class + // E.g. [NSAttributedString alloc] returns a NSConcreteAttributedString* + // 2. The message is forwarded to another object. E.g. `UITextField` forwards + // `autocapitalizationType` to an instance of `UITextInputTraits` + static robin_hood::unordered_map sampleInstances; + + auto it = sampleInstances.find(klass); + if (it == sampleInstances.end()) { + @try { + it = sampleInstances.emplace(klass, [klass alloc]).first; + } @catch (id err) { + return false; + } } + id sampleInstance = it->second; + return [sampleInstance respondsToSelector:this->selector()]; + } } // BaseClassMeta std::set BaseClassMeta::protocolsSet() const { - std::set protocols; - this->forEachProtocol([&protocols](const ProtocolMeta* protocolMeta) { - protocols.insert(protocolMeta); - }, - /*additionalProtocols*/ nullptr); + std::set protocols; + this->forEachProtocol( + [&protocols](const ProtocolMeta* protocolMeta) { protocols.insert(protocolMeta); }, + /*additionalProtocols*/ nullptr); - return protocols; + return protocols; } std::set BaseClassMeta::deepProtocolsSet() const { - std::set protocols; - this->deepProtocolsSet(protocols); - return protocols; + std::set protocols; + this->deepProtocolsSet(protocols); + return protocols; } void BaseClassMeta::deepProtocolsSet(std::set& protocols) const { - if (this->type() == Interface) { - auto interfaceMeta = static_cast(this); - if (auto baseMeta = interfaceMeta->baseMeta()) { - baseMeta->deepProtocolsSet(protocols); - } + if (this->type() == Interface) { + auto interfaceMeta = static_cast(this); + if (auto baseMeta = interfaceMeta->baseMeta()) { + baseMeta->deepProtocolsSet(protocols); } + } - this->forEachProtocol([&protocols](const ProtocolMeta* protocolMeta) { + this->forEachProtocol( + [&protocols](const ProtocolMeta* protocolMeta) { protocolMeta->deepProtocolsSet(protocols); protocols.insert(protocolMeta); - }, - /*additionalProtocols*/ nullptr); + }, + /*additionalProtocols*/ nullptr); } const MemberMeta* BaseClassMeta::member(const char* identifier, size_t length, MemberType type, bool includeProtocols, bool onlyIfAvailable, const ProtocolMetas& additionalProtocols) const { + MembersCollection members = this->members(identifier, length, type, includeProtocols, + onlyIfAvailable, additionalProtocols); - MembersCollection members = this->members(identifier, length, type, includeProtocols, onlyIfAvailable, additionalProtocols); + // It's expected to receive only one occurence when member is used. If more than one results can + // be found consider (1) using BaseClassMeta::members to process all of them; or (2) fixing + // metadata generator to disambiguate and remove the redundant one(s); or (3) modify this method + // so that it doesn't arbitrary choose one and drop the other(s) but deterministically decides + // which one has to be returned. + ASSERT(members.size() <= 1); - // It's expected to receive only one occurence when member is used. If more than one results can - // be found consider (1) using BaseClassMeta::members to process all of them; or (2) fixing metadata - // generator to disambiguate and remove the redundant one(s); or (3) modify this method so that it doesn't arbitrary - // choose one and drop the other(s) but deterministically decides which one has to be returned. - ASSERT(members.size() <= 1); - - return members.size() > 0 ? *members.begin() : nullptr; + return members.size() > 0 ? *members.begin() : nullptr; } -void collectInheritanceChainMembers(const char* identifier, size_t length, MemberType type, bool onlyIfAvailable, const BaseClassMeta* meta, std::function collectMember) { - - const ArrayOfPtrTo* members = nullptr; - // Scan method overloads (methods with different selectors and number of arguments which have the same jsName) - // in base classes. Properties cannot be overridden like that so there's no need to traverse the hierarchy. - bool shouldScanForOverrides = true; - switch (type) { +void collectInheritanceChainMembers(const char* identifier, size_t length, MemberType type, + bool onlyIfAvailable, const BaseClassMeta* meta, + std::function collectMember) { + const ArrayOfPtrTo* members = nullptr; + // Scan method overloads (methods with different selectors and number of arguments which have the + // same jsName) in base classes. Properties cannot be overridden like that so there's no need to + // traverse the hierarchy. + bool shouldScanForOverrides = true; + switch (type) { case MemberType::InstanceMethod: - members = &meta->instanceMethods->castTo>(); - break; + members = &meta->instanceMethods->castTo>(); + break; case MemberType::StaticMethod: - members = &meta->staticMethods->castTo>(); - break; + members = &meta->staticMethods->castTo>(); + break; case MemberType::InstanceProperty: - shouldScanForOverrides = false; - members = &meta->instanceProps->castTo>(); - break; + shouldScanForOverrides = false; + members = &meta->instanceProps->castTo>(); + break; case MemberType::StaticProperty: - shouldScanForOverrides = false; - members = &meta->staticProps->castTo>(); - break; + shouldScanForOverrides = false; + members = &meta->staticProps->castTo>(); + break; + } + + int resultIndex = -1; + resultIndex = members->binarySearchLeftmost([&](const PtrTo& member) { + return compareIdentifiers(member->jsName(), identifier, length); + }); + + if (resultIndex >= 0) { + for (const MemberMeta* m = (*members)[resultIndex].valuePtr(); + resultIndex < members->count && + (strncmp(m->jsName(), identifier, length) == 0 && strlen(m->jsName()) == length); + m = (*members)[++resultIndex].valuePtr()) { + if (m->isAvailable() || !onlyIfAvailable) { + collectMember(m); + } } - int resultIndex = -1; - resultIndex = members->binarySearchLeftmost([&](const PtrTo& member) { return compareIdentifiers(member->jsName(), identifier, length); }); - - if (resultIndex >= 0) { - for (const MemberMeta* m = (*members)[resultIndex].valuePtr(); - resultIndex < members->count && (strncmp(m->jsName(), identifier, length) == 0 && strlen(m->jsName()) == length); - m = (*members)[++resultIndex].valuePtr()) { - if (m->isAvailable() || !onlyIfAvailable) { - collectMember(m); - } - } - - if (shouldScanForOverrides && meta->type() == MetaType::Interface) { - const BaseClassMeta* superClass = static_cast(meta)->baseMeta(); - if (superClass) { - collectInheritanceChainMembers(identifier, length, type, onlyIfAvailable, superClass, collectMember); - } - } + if (shouldScanForOverrides && meta->type() == MetaType::Interface) { + const BaseClassMeta* superClass = static_cast(meta)->baseMeta(); + if (superClass) { + collectInheritanceChainMembers(identifier, length, type, onlyIfAvailable, superClass, + collectMember); + } } - - // Instance members of NSObject can be called as static as well (e.g. [NSString performSelector:@selector(alloc)] - static const char* nsObject = "NSObject"; - static const size_t nsObjectLen = strlen(nsObject); - if (strncmp(meta->name(), nsObject, nsObjectLen + 1) == 0) { - if (type == MemberType::StaticMethod) { - collectInheritanceChainMembers(identifier, length, MemberType::InstanceMethod, onlyIfAvailable, meta, collectMember); - } else if (type == MemberType::StaticProperty) { - collectInheritanceChainMembers(identifier, length, MemberType::InstanceProperty, onlyIfAvailable, meta, collectMember); - } + } + + // Instance members of NSObject can be called as static as well (e.g. [NSString + // performSelector:@selector(alloc)] + static const char* nsObject = "NSObject"; + static const size_t nsObjectLen = strlen(nsObject); + if (strncmp(meta->name(), nsObject, nsObjectLen + 1) == 0) { + if (type == MemberType::StaticMethod) { + collectInheritanceChainMembers(identifier, length, MemberType::InstanceMethod, + onlyIfAvailable, meta, collectMember); + } else if (type == MemberType::StaticProperty) { + collectInheritanceChainMembers(identifier, length, MemberType::InstanceProperty, + onlyIfAvailable, meta, collectMember); } + } } -const MembersCollection BaseClassMeta::members(const char* identifier, size_t length, MemberType type, - bool includeProtocols, bool onlyIfAvailable, +const MembersCollection BaseClassMeta::members(const char* identifier, size_t length, + MemberType type, bool includeProtocols, + bool onlyIfAvailable, const ProtocolMetas& additionalProtocols) const { - - MembersCollection result; - - if (type == MemberType::InstanceMethod || type == MemberType::StaticMethod) { - - // We need to return base class members as well. Otherwise, - // if an overloaded method is overriden by a derived class - // the FunctionWrapper's *functionsContainer* will contain - // overriden members metas only. - std::map membersMap; - collectInheritanceChainMembers(identifier, length, type, onlyIfAvailable, this, [&](const MemberMeta* member) { - const MethodMeta* method = static_cast(member); - ArrayCount count = method->encodings()->count; - membersMap.emplace(count, member); - }); - for (std::map::iterator it = membersMap.begin(); it != membersMap.end(); ++it) { - result.insert(it->second); - } - - } else { // member is a property - collectInheritanceChainMembers(identifier, length, type, onlyIfAvailable, this, [&](const MemberMeta* member) { - result.insert(member); + MembersCollection result; + + if (type == MemberType::InstanceMethod || type == MemberType::StaticMethod) { + // We need to return base class members as well. Otherwise, + // if an overloaded method is overriden by a derived class + // the FunctionWrapper's *functionsContainer* will contain + // overriden members metas only. + std::map membersMap; + collectInheritanceChainMembers( + identifier, length, type, onlyIfAvailable, this, [&](const MemberMeta* member) { + const MethodMeta* method = static_cast(member); + ArrayCount count = method->encodings()->count; + membersMap.emplace(count, member); }); + for (std::map::iterator it = membersMap.begin(); it != membersMap.end(); + ++it) { + result.insert(it->second); } - if (result.size() > 0) { - return result; - } + } else { // member is a property + collectInheritanceChainMembers(identifier, length, type, onlyIfAvailable, this, + [&](const MemberMeta* member) { result.insert(member); }); + } - // search in protocols - if (includeProtocols) { - this->forEachProtocol([&result, identifier, length, type, includeProtocols, onlyIfAvailable](const ProtocolMeta* protocolMeta) { - const MembersCollection members = protocolMeta->members(identifier, length, type, includeProtocols, onlyIfAvailable, ProtocolMetas()); - result.insert(members.begin(), members.end()); + if (result.size() > 0) { + return result; + } + + // search in protocols + if (includeProtocols) { + this->forEachProtocol( + [&result, identifier, length, type, includeProtocols, + onlyIfAvailable](const ProtocolMeta* protocolMeta) { + const MembersCollection members = protocolMeta->members( + identifier, length, type, includeProtocols, onlyIfAvailable, ProtocolMetas()); + result.insert(members.begin(), members.end()); }, - &additionalProtocols); - } + &additionalProtocols); + } - return result; + return result; } -std::vector BaseClassMeta::instancePropertiesWithProtocols(std::vector& container, KnownUnknownClassPair klasses, const ProtocolMetas& additionalProtocols) const { - this->instanceProperties(container, klasses); - this->forEachProtocol([&container, klasses](const ProtocolMeta* protocolMeta) { +std::vector BaseClassMeta::instancePropertiesWithProtocols( + std::vector& container, KnownUnknownClassPair klasses, + const ProtocolMetas& additionalProtocols) const { + this->instanceProperties(container, klasses); + this->forEachProtocol( + [&container, klasses](const ProtocolMeta* protocolMeta) { protocolMeta->instancePropertiesWithProtocols(container, klasses, ProtocolMetas()); - }, - &additionalProtocols); - return container; + }, + &additionalProtocols); + return container; } -std::vector BaseClassMeta::staticPropertiesWithProtocols(std::vector& container, KnownUnknownClassPair klasses, const ProtocolMetas& additionalProtocols) const { - this->staticProperties(container, klasses); - this->forEachProtocol([&container, klasses](const ProtocolMeta* protocolMeta) { +std::vector BaseClassMeta::staticPropertiesWithProtocols( + std::vector& container, KnownUnknownClassPair klasses, + const ProtocolMetas& additionalProtocols) const { + this->staticProperties(container, klasses); + this->forEachProtocol( + [&container, klasses](const ProtocolMeta* protocolMeta) { protocolMeta->staticPropertiesWithProtocols(container, klasses, ProtocolMetas()); - }, - &additionalProtocols); - return container; + }, + &additionalProtocols); + return container; } -vector BaseClassMeta::initializers(vector& container, KnownUnknownClassPair klasses) const { - // search in instance methods - int16_t firstInitIndex = this->initializersStartIndex; - if (firstInitIndex != -1) { - for (int i = firstInitIndex; i < instanceMethods->count; i++) { - const MethodMeta* method = instanceMethods.value()[i].valuePtr(); - if (!method->isInitializer()) { - break; - } - - if (method->isAvailableInClasses(klasses, /*isStatic*/ false)) { - container.push_back(method); - } - } +vector BaseClassMeta::initializers(vector& container, + KnownUnknownClassPair klasses) const { + // search in instance methods + int16_t firstInitIndex = this->initializersStartIndex; + if (firstInitIndex != -1) { + for (int i = firstInitIndex; i < instanceMethods->count; i++) { + const MethodMeta* method = instanceMethods.value()[i].valuePtr(); + if (!method->isInitializer()) { + break; + } + + if (method->isAvailableInClasses(klasses, /*isStatic*/ false)) { + container.push_back(method); + } } - return container; + } + return container; } -vector BaseClassMeta::initializersWithProtocols(vector& container, KnownUnknownClassPair klasses, const ProtocolMetas& additionalProtocols) const { - this->initializers(container, klasses); - for (Array::iterator it = this->protocols->begin(); it != this->protocols->end(); it++) { - const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTableJs()->findProtocol((*it).valuePtr()); - if (protocolMeta != nullptr) - protocolMeta->initializersWithProtocols(container, klasses, ProtocolMetas()); - } - for (const ProtocolMeta* protocolMeta : additionalProtocols) { - protocolMeta->initializersWithProtocols(container, klasses, ProtocolMetas()); - } - return container; +vector BaseClassMeta::initializersWithProtocols( + vector& container, KnownUnknownClassPair klasses, + const ProtocolMetas& additionalProtocols) const { + this->initializers(container, klasses); + for (Array::iterator it = this->protocols->begin(); it != this->protocols->end(); it++) { + const ProtocolMeta* protocolMeta = + MetaFile::instance()->globalTableJs()->findProtocol((*it).valuePtr()); + if (protocolMeta != nullptr) + protocolMeta->initializersWithProtocols(container, klasses, ProtocolMetas()); + } + for (const ProtocolMeta* protocolMeta : additionalProtocols) { + protocolMeta->initializersWithProtocols(container, klasses, ProtocolMetas()); + } + return container; } static MetaFile* metaFileInstance(nullptr); -MetaFile* MetaFile::instance() { - return metaFileInstance; -} +MetaFile* MetaFile::instance() { return metaFileInstance; } MetaFile* MetaFile::setInstance(void* metadataPtr) { - metaFileInstance = reinterpret_cast(metadataPtr); - return metaFileInstance; -} + metaFileInstance = reinterpret_cast(metadataPtr); + return metaFileInstance; } +} // namespace tns diff --git a/TestFixtures/Api/TNSVersions.h b/TestFixtures/Api/TNSVersions.h index fba2328d..e66af3fa 100644 --- a/TestFixtures/Api/TNSVersions.h +++ b/TestFixtures/Api/TNSVersions.h @@ -1,5 +1,6 @@ #define generateVersionDeclarations(V1, V2) \ - __attribute__((availability(ios, introduced = V1))) @interface TNSInterface \ + __attribute__((availability(ios, introduced = V1))) \ + @interface TNSInterface \ ##V2##Plus : NSObject @end \ \ @interface TNSInterfaceMembers \ @@ -58,6 +59,7 @@ generateMinors(15); #define MAX_AVAILABILITY 31.7 __attribute__((availability(ios, introduced = MAX_AVAILABILITY))) +__attribute__((availability(visionos, introduced = MAX_AVAILABILITY))) @protocol TNSProtocolNeverAvailable @property(class, readonly) int staticPropertyFromProtocolNeverAvailable; @@ -74,7 +76,8 @@ __attribute__((availability(ios, introduced = MAX_AVAILABILITY))) @end -__attribute__((availability(ios, introduced = 1.0))) @protocol TNSProtocolAlwaysAvailable +__attribute__((availability(ios, introduced = 1.0))) +@protocol TNSProtocolAlwaysAvailable @property(class, readonly) int staticPropertyFromProtocolAlwaysAvailable; @@ -91,6 +94,7 @@ __attribute__((availability(ios, introduced = 1.0))) @protocol TNSProtocolAlways @end __attribute__((availability(ios, introduced = MAX_AVAILABILITY))) +__attribute__((availability(visionos, introduced = MAX_AVAILABILITY))) @interface TNSInterfaceNeverAvailable : TNSInterfaceAlwaysAvailable @end diff --git a/v8ios.xcodeproj/project.pbxproj b/v8ios.xcodeproj/project.pbxproj index 19ca5ef8..4046a032 100644 --- a/v8ios.xcodeproj/project.pbxproj +++ b/v8ios.xcodeproj/project.pbxproj @@ -2339,7 +2339,10 @@ }; C293752B229FC4740075CB16 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilter = ios; + platformFilters = ( + ios, + xros, + ); target = C29374E1229FC0F60075CB16 /* TestFixtures */; targetProxy = C293752A229FC4740075CB16 /* PBXContainerItemProxy */; }; @@ -2433,6 +2436,7 @@ C23992CE236C2D6E00D2F720 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_MODULES = NO; CODE_SIGN_STYLE = Automatic; @@ -2459,6 +2463,7 @@ C23992CF236C2D6E00D2F720 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_MODULES = NO; CODE_SIGN_STYLE = Automatic; @@ -2682,6 +2687,7 @@ C29374E9229FC0F60075CB16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; CLANG_ENABLE_MODULES = NO; CLANG_WARN_STRICT_PROTOTYPES = NO; DEFINES_MODULE = YES; @@ -2699,6 +2705,7 @@ C29374EA229FC0F60075CB16 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; CLANG_ENABLE_MODULES = NO; CLANG_WARN_STRICT_PROTOTYPES = NO; DEFINES_MODULE = YES;