…needs
ByteCodeTranslator treated UniformTypeIdentifiers.framework as the macOS
spelling of MobileCoreServices and linked it on that branch alone. It is not:
MobileCoreServices carries only the deprecated C functions, while the UTType
class lives in UniformTypeIdentifiers on every platform from iOS 14 / macOS 11 /
watchOS 7 / tvOS 14.
CN1DragAndDrop.m compiles that class into every slice -- its MIME-to-identifier
mapping sits outside the UIKit guard because the clipboard natives need it too
-- so an iOS application referenced _OBJC_CLASS_$_UTType with nothing defining
it, and every device archive failed at the link.
Link it on all slices, weakly: ios.deployment_target reaches back before the
framework existed and a hard link against one the device lacks kills the process
at launch, while the uses are already @available fenced. Classified in
WatchNativeBuilder as linkable, verified present in both watch SDKs.
Why no CI job caught it
-----------------------
Every iOS job was green for five days, and structurally had to be. Nothing
declared the framework there either -- the sample application supplies it by
accident, three ways a plain application does not have:
- a Swift autolink hint, since the sample has Swift in its app target and
`import SwiftUI` stamps -framework UniformTypeIdentifiers into the object
- CLANG_ENABLE_MODULES, which IPhoneBuilder turns on for the Metal build, so
the same source links there and fails on the GL one
- CocoaPods dependencies on the link line
scripts/check-ios-framework-links.py closes that. It runs over the project
build-ios-app.sh has already generated, compiles the port's natives with the
project's own settings, and resolves their undefined symbols against the
frameworks the project DECLARES -- never against an autolink hint, which is a
load command rather than a definition and so cannot hide a symbol from nm. A
native that fails to compile is fatal rather than skipped, and the SDK symbol
index self-tests, because both of those failure modes report success.
Verified: with the post-#5662 source injected into a project that does not
declare the framework the gate fails naming UniformTypeIdentifiers.framework and
_OBJC_CLASS_$_UTType; declaring it on the app target makes the gate pass. 79 of
79 port natives compile, 623 SDK symbols examined. The link itself was A/B'd
with the real CN1DragAndDrop.m for arm64-apple-ios15.0: undefined without the
framework, resolved with -weak_framework UniformTypeIdentifiers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bug
Every iOS device archive fails at the link:
ByteCodeTranslatortreatedUniformTypeIdentifiers.frameworkas the macOS spelling ofMobileCoreServicesand linked it on that branch alone:It is not the same framework.
MobileCoreServicescarries only the deprecated C functions (UTTypeCreatePreferredIdentifierForTag); theUTTypeclass lives inUniformTypeIdentifierson every platform from iOS 14 / macOS 11 / watchOS 7 / tvOS 14.CN1DragAndDrop.mcompiles that class into every slice — the MIME-to-identifier mapping sits outside its UIKit guard because the clipboard natives need it too.Two commits collided: #5601 wrote the either/or split, #5662 later introduced the
UTTypeuse. Neither is wrong alone.The fix
Link it on all slices, weakly —
ios.deployment_targetreaches back before the framework existed, and a hard link against one the device lacks kills the process at launch; the uses are already@availablefenced. Classified inWatchNativeBuilderas linkable, verified present in both watch SDKs.Why no CI job caught it
Every iOS job was green for five days, and structurally had to be. Nothing declares the framework there either — I pulled the link line out of the last green master nightly and it has no
-framework UniformTypeIdentifiers. The sample supplies it by accident, three ways a plain application does not have:import SwiftUImakes swiftc stamp-framework UniformTypeIdentifiersinto the object as anLC_LINKER_OPTIONCLANG_ENABLE_MODULES—IPhoneBuilderturns it on for the Metal build, so the same source links there and fails on the GL oneThe gate
scripts/check-ios-framework-links.pyruns over the projectbuild-ios-app.shhas already generated (seconds, no extra build) and answers one question: every symbol our natives reference that an SDK framework defines — is it defined by a framework this project declares?It compiles the port's natives with the project's own settings (prefix header, defines, per-file
-fobjc-arc, modules where the project asks) and resolves the undefined symbols against the declared set alone. Autolink cannot hide anything from it — a hint is a load command, not a definition, sonmstill reports the symbol undefined, and the probe it links to confirm a finding carries no hints at all.Two deliberate properties: a native that fails to compile is fatal rather than skipped (a partial set reporting success is the failure mode this replaces), and the SDK symbol index self-tests against a floor plus two known symbols, because a text index that parses to nothing reports every project clean.
Wired into
build-iosandbuild-ios-metal.Verification
UniformTypeIdentifiers.framework/_OBJC_CLASS_$_UTType; declaring it on the app target makes it pass. 79 of 79 port natives compile, 623 SDK symbols examined, 556k symbols indexed.CN1DragAndDrop.matarm64-apple-ios15.0, only CN1's own callbacks stubbed so every system symbol had to resolve for real: undefined without the framework, resolved with-weak_framework UniformTypeIdentifiers.PBXFrameworksBuildPhasewithATTRIBUTES = (Weak).WatchNativeBuilderTest49/49, SpotBugs 0 findings onByteCodeTranslator.Ruled out on the way: device-vs-simulator, Debug-vs-Release, LTO, and deployment target — the simulator link fails identically.