Repository navigation
Conversation
DebugBridgeTouch.m and its header both promise the code is DEBUG-only and
never shipped:
"Uses these private UIKit selectors (DEBUG-only; never shipped to App Store)"
"DEBUG-only — never link in Release."
Nothing enforced it. The only guard was `#if TARGET_OS_IOS`, so a Release build
for iOS compiled the entire implementation in, private API and all.
Measured on a real app (an iOS Release build, `nm -j` on the app binary):
DebugBridge symbols 15
IOHIDEventCreateDigitizer 2
AXSSetAutomationEnabled 1 symbol, 2 strings
IOKit.framework 4 strings
including +[DebugBridgeTouch sendTapAtPoint:inWindow:] and
_OBJC_CLASS_$_DebugBridgeTouch. That is a Guideline 2.5.1 private-API exposure
in a shippable binary, and it fails Package.swift's own stated CI invariant:
nm -j build/Release/<binary> | grep -q DebugBridge && exit 1
WHY THE EXISTING GUARD DOES NOT COVER THIS
Package.swift documents the protection as `.when(configuration: .debug)` on the
consuming target's dependency. That works for SwiftPM consumers. It cannot be
expressed by an app that integrates DebugBridge as a local package inside an
.xcodeproj: Xcode's Filters column under Frameworks, Libraries, and Embedded
Content offers platform conditions only — iOS, macOS, visionOS — never build
configuration. So for xcodeproj consumers the documented guard silently does
nothing, which is precisely the case that was measured.
The Swift targets were already safe: all four .swift files are `#if DEBUG`
guarded and Package.swift defines DEBUG for them via swiftSettings. Only the
Objective-C target, the one that actually links private API, was unguarded.
THE FIX
1. DebugBridgeTouch.m.template now branches `#if !defined(DEBUG)` first and
emits nothing at all in Release, falling through to the existing iOS and
non-iOS branches only in Debug.
2. Package.swift.template declares DEBUG explicitly for the ObjC target:
cSettings: [.define("DEBUG", .when(configuration: .debug))]
The two Swift targets already did this. Relying on SwiftPM's implicit DEBUG
for C-family targets is not worth betting a private-API exposure on.
VERIFIED, by compiling the generated file for iOS both ways:
xcrun -sdk iphoneos clang -c DebugBridgeTouch.m -arch arm64 ...
Release (no -DDEBUG) 0 DebugBridge symbols, 0 private-API symbols, 448 B
Debug (-DDEBUG=1) 7 DebugBridge symbols, 6 private-API symbols, 13104 B
The harness is unchanged in Debug. Release now emits an empty translation unit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
garrytan
added a commit
that referenced
this pull request
Aug 16, 2026
…plates The #2585 absorb updated DebugBridgeTouch.m.template and Package.swift.template but not their FixtureApp mirrors, failing the template↔fixture parity gate. DebugBridgeTouch.m syncs byte-for-byte; the fixture Package.swift takes only the template's new cSettings DEBUG define on the Touch target (the fixture's own testTarget is fixture-only content the parity normalization deliberately ignores — a naive full copy breaks the XCTest invariant). 23/23 including the real swift build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 of 9 tasks
Owner
|
Thank you — this landed in v1.67.0.0 (credited in the CHANGELOG), plus a release-guard tripwire added in v1.67.1.0. Closing since the fix is on main. 🙏 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
DebugBridgeTouch.mand its header both promise the code never ships:Nothing enforced it. The only guard was
#if TARGET_OS_IOS, so a Release build for iOS compiles the whole implementation in — private API included.Measured on a real app,
nm -jagainst an iOS Release binary:DebugBridgesymbolsIOHIDEventCreateDigitizerAXSSetAutomationEnabledIOKit.frameworkIncluding
+[DebugBridgeTouch sendTapAtPoint:inWindow:]and_OBJC_CLASS_$_DebugBridgeTouch.That is a Guideline 2.5.1 private-API exposure in a shippable binary, and it fails
Package.swift's own stated CI invariant:Why the documented guard doesn't cover it
Package.swiftdescribes the protection as.when(configuration: .debug)on the consuming target's dependency. That works for SwiftPM consumers.It cannot be expressed by an app integrating DebugBridge as a local package inside an
.xcodeproj. Xcode's Filters column under Frameworks, Libraries, and Embedded Content offers platform conditions only — iOS, macOS, visionOS — never build configuration. So for xcodeproj consumers the documented guard silently does nothing, which is exactly the case measured above.The Swift targets were already safe: all four
.swiftfiles are#if DEBUGguarded andPackage.swiftdefinesDEBUGfor them viaswiftSettings. Only the Objective-C target — the one that actually links private API — was unguarded.The fix
DebugBridgeTouch.m.templatebranches on#if !defined(DEBUG)first and emits nothing in Release, falling through to the existing iOS / non-iOS branches only in Debug.Package.swift.templatedeclaresDEBUGexplicitly for the ObjC target:DEBUGfor C-family targets isn't worth betting a private-API exposure on.Putting the guard in the source means it holds regardless of how the package is integrated, rather than depending on the consumer getting it right.
Verification
Compiled the generated file for iOS both ways:
-DDEBUG)-DDEBUG=1)The harness is unchanged in Debug. Release emits an empty translation unit.
Note for maintainers
The comments in both templates now record the measured failure, so the guard isn't removed later as redundant. Happy to adjust wording or split the two changes if you'd prefer them separately.