garrytan / garrytan/gstack

ios-qa: DebugBridge templates fail clean install on Swift 6.x + leak private SPIs into Release (5 bugs, incl. Apple-rejection class)

Open
#1,836 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

**gstack version:** v1.55.0.0
**Skill:** `/ios-qa`
**Discovered:** 2026-06-01, fresh install of DebugBridge into the Dugout Sports iOS app (after our PR #342 had to surgically remove the previous DebugBridge to pass App Store review — see #5 below, which is the same failure mode).

A clean `/ios-qa` install of the DebugBridge stack currently does not build out of the box on Swift 6.x toolchains, and — most seriously — the generated Obj-C touch-synthesis target compiles private UIKit/IOKit SPIs into **Release** builds, which Apple's static scanner rejects. Five distinct template bugs below, in increasing severity. We've applied local patches for all five in our working tree; the corrected files are quoted/referenced inline.

---

### 1. `gen-accessors-tool/Package.swift` references a non-existent test target

`ios-qa/scripts/gen-accessors-tool/Package.swift` declares:

```swift
.testTarget(name: "GenAccessorsTests", path: "Tests/GenAccessorsTests")
```

but `Tests/` does not exist in the package, so `swift build` fails:

```
error: invalid custom path 'Tests/GenAccessorsTests' for target 'GenAccessorsTests'
```

**Fix:** remove the `.testTarget(...)` block — no tests exist.

---

### 2. `Package.swift.template` puts the tools-version directive on line 21, not line 1

`ios-qa/templates/Package.swift.template` opens with a multi-line doc-comment block and only declares `// swift-tools-version:5.9` on ~line 21. The directive **must** be the first line of the manifest. Swift 6.0+ rejects the misplaced directive:

```
error: manifest is backward-incompatible with Swift < 6.0
```

**Fix:** move `// swift-tools-version:5.9` to line 1 (doc-comment block follows it). Our generated `apps/ios/DebugBridge/Package.swift` shows the correct ordering — directive on line 1, comment block lines 2–22.

---

### 3. `Package.swift.template` references a non-existent test target

Same template declares:

```swift
.testTarget(name: "DebugBridgeCoreTests", path: "Tests/DebugBridgeCoreTests")
```

`/ios-qa` never populates a `Tests/` folder, so the install fails the same way as #1 (`invalid custom path`).

**Fix:** drop the `.testTarget(...)` block.

---

### 4. `DebugBridgeWiring.swift.template` is broken and redundant — delete it

`ios-qa/templates/DebugBridgeWiring.swift.template` does not compile and duplicates wiring that `Bridges.swift.template` already provides:

- `import DebugBridge` — there is no `DebugBridge` module. The modules are `DebugBridgeCore`, `DebugBridgeUI`, `DebugBridgeTouch`.
- References `AccessibilityScanner`, `SnapshotCapture`, `MutationDispatcher` — none of these symbols exist anywhere in the templates. `Bridges.swift.template` defines `ElementsBridgeImpl`, `ScreenshotBridgeImpl`, `MutationBridgeImpl` instead.
- Calls `DebugBridgeManager.shared.start(appState:recording:)` — `DebugBridgeManager` only defines `start(appState:)`.

`Bridges.swift`'s `DebugBridgeUIWiring.installAll()` already performs the actual wiring (confirmed in our generated `Sources/DebugBridgeUI/Bridges.swift` — public `enum DebugBridgeUIWiring { static func installAll() ... }`).

**Fix:** delete `DebugBridgeWiring.swift.template` entirely.

---

### 5. ⚠️ `DebugBridgeTouch.m.template` compiles private SPIs into App Store Release builds

**This is the critical one — it is the exact failure that got Dugout Sports v1.0 rejected (App Store Guideline 2.1, private API scan) and forced the surgical DebugBridge removal in our PR #342.**

`ios-qa/templates/DebugBridgeTouch.m.template` gates its iOS implementation block with:

```objc
#if TARGET_OS_IOS
```

instead of:

```objc
#if TARGET_OS_IOS && defined(DEBUG)
```

The block references private UITouch/UIEvent/IOKit SPIs — `_setLocationInWindow:resetPrevious:`, `_addTouch:forDelayedDelivery:`, `_clearTouches`, `_setHIDEvent:`, `_setIsFirstTouchForView:`, `_touchesEvent`, `IOHIDEventCreateDigitizer*`. With only `TARGET_OS_IOS`, these compile into **iOS Release** builds and land in the App Store binary, where Apple's static scanner flags them.

Critically, the `Package.swift` Swift-side gate does **not** protect this file:

```swift
swiftSettings: [.define("DEBUG", .when(configuration: .debug))]
```

`swiftSettings` only affects Swift compile units. The Obj-C `.m` file is compiled with `cSettings`, which the template doesn't set — so `DEBUG` is never defined for this translation unit in a SwiftPM build, and the SPIs leak.

**Fix (two parts):**

1. Change the gate to `#if TARGET_OS_IOS && defined(DEBUG)`. The existing `#else` stub (iOS Release / macOS / Catalyst) already provides the no-op implementations, so the non-DEBUG branch is already handled.
2. Add a `cSettings` DEBUG define to the `DebugBridgeTouch` target in `Package.swift.template` as belt-and-suspenders:

```swift
.target(
name: "DebugBridgeTouch",
...
cSettings: [
.define("DEBUG", to: "1", .when(configuration: .debug)),
],
...
)
```

Our patched `apps/ios/DebugBridge/Sources/DebugBridgeTouch/DebugBridgeTouch.m` now gates at line 28 (`#if TARGET_OS_IOS && defined(DEBUG)`), with the `#else` stub at line 294 and `#endif` at line 307; the patched `Package.swift` carries the `cSettings` define on the `DebugBridgeTouch` target.

---

### Verification (passes with all five fixes applied)

```bash
cd "$APP/DebugBridge" && swift build -c release
nm -j .build/arm64-apple-macosx/release/DebugBridgeTouch.build/DebugBridgeTouch.m.o \
| grep -qE '_setLocationInWindow|_addTouch|_clearTouches|_setHIDEvent' \
&& exit 1 || echo "clean"
```

Expected: `swift build -c release` succeeds (fixes #1–#4) and the `nm` symbol scan prints `clean` (fix #5).

We have local patches for all five at `apps/ios/DebugBridge/` in the Dugout repo (uncommitted, branch `fix/data-accuracy-fixes`) and can open a PR against the templates if useful — happy to do so once you confirm the approach.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.