[PR #913] [MERGED] fix(artifacts): project UIKit runtime outlet and event connections #2340

Closed
opened 2026-10-16 21:17:32 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/913
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/7/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: fix/uikit-runtime-connections


📝 Commits (6)

  • b08228d fix(artifacts): project compiled AppKit actions from the control to its target
  • c60b737 test(artifacts): report observed action routes when a compiled route is missing
  • d421d3f test(artifacts): report compiled connector records when a storyboard route is missing
  • 53bfa7e test(artifacts): write the storyboard fixture's action in Cocoa syntax
  • 8b24387 fix(artifacts): project UIKit runtime outlet and event connections
  • 1d769cd chore: merge main and preserve UIKit and AppKit connections

📊 Changes

3 files changed (+127 additions, -24 deletions)

View changed files

📝 src/application/InterfaceBuilderAnalysis.ts (+33 -23)
📝 src/domain/interfaceBuilderKeyedArchive.ts (+5 -1)
📝 tests/boundary/filesystem/interfaceBuilderBundle.test.ts (+89 -0)

📄 Description

Summary

Builds on #905 (same function). Until #905 merges, this diff includes its commits. The UIKit change is the last commit.

Compiled UIKit NIBs and storyboards (iOS apps, and iOS apps installed on a Mac) archive connections as UIRuntimeOutletConnection and UIRuntimeEventConnection, with UISource, UIDestination, UILabel, and, for events, UIEventMask. The NIBArchive projection in decode_interface_builder recognized only AppKit *Connector classes with NS-prefixed keys. An iOS app therefore produced no outlet or action edges at all. The connection records also became unknown graph nodes, UIProxyObject (File's Owner, First Responder) was not a placeholder, and UIClassSwapper custom classes were not reported.

The archived roles match AppKit's. In Stash 3.4.1 (/Applications/Stash.app/Wrapper/Stash.app), every event connection is control → target, for example UIRuntimeEventConnection src=UISwitch dst=UIClassSwapper(_TtC5Stash10SwitchCell) label=switchAction: mask=4096.

Changes in projectNibArchive:

  • UIRuntime*Connection records are read like AppKit connectors, with the source (outlet owner or sending control) → the destination (outlet value or action target), a nil target being the first responder. Event connections are actions, and their raw UIEventMask is kept as the ui_event_mask attribute on the action.
  • Connection records are no longer emitted as object nodes. They were already excluded for NSNib*Connector.
  • UIClassSwapper reports its UIClassName, as NSClassSwapper reports NSClassName. Swift classes appear mangled, as archived, e.g. _TtC5Stash10SwitchCell.
  • UIProxyObject is classified as a placeholder. Its name comes from UIProxiedObjectIdentifier, e.g. IBFilesOwner.

Stash, first 64 documents (the default limit):

main / #905 this PR
edges 0 372 (174 outlet_to, 174 contains, 24 target_action)
outlet / action nodes 0 / 0 174 / 8
placeholder nodes 13 178
connection records as unknown nodes 458 0

No schema or catalog change.

Verification

  • New case in tests/boundary/filesystem/interfaceBuilderBundle.test.ts (in its own describe block, under the 150-line function limit): a compiled UIKit NIB with a UIClassSwapper cell, UIButton, UIProxyObject, an event connection, and an outlet connection. It expects UIButton → buildTapped: → BuildCell with ui_event_mask: 64, the buildButton outlet to UIButton, IBFilesOwner as a placeholder, and no connection record nodes. Fails without this change.
  • Real app: Stash as in the table above.
  • npm run check:changed passes. The ibtool storyboard test fails locally only because this host lacks Xcode; it passes on CI's macOS job on #905.

No Hopper/Ghidra/IDA verification is needed.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/913 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ✅ Merged **Merged:** 10/7/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/uikit-runtime-connections` --- ### 📝 Commits (6) - [`b08228d`](https://github.com/morluto/rea/commit/b08228d19f8a0f6dbc940e8d322ef2bb42d2ce1a) fix(artifacts): project compiled AppKit actions from the control to its target - [`c60b737`](https://github.com/morluto/rea/commit/c60b737f7db8e2c9a1982cdb4357fcda1f47ff08) test(artifacts): report observed action routes when a compiled route is missing - [`d421d3f`](https://github.com/morluto/rea/commit/d421d3f286ce515b5126859a0f9cac60667a168e) test(artifacts): report compiled connector records when a storyboard route is missing - [`53bfa7e`](https://github.com/morluto/rea/commit/53bfa7e3f792cdb9f2e95fc954f3616426414bcc) test(artifacts): write the storyboard fixture's action in Cocoa syntax - [`8b24387`](https://github.com/morluto/rea/commit/8b243877c22a5550d005e7b8abf93791ce8566b9) fix(artifacts): project UIKit runtime outlet and event connections - [`1d769cd`](https://github.com/morluto/rea/commit/1d769cd261cec5ab04809b36d48b59183553e4d9) chore: merge main and preserve UIKit and AppKit connections ### 📊 Changes **3 files changed** (+127 additions, -24 deletions) <details> <summary>View changed files</summary> 📝 `src/application/InterfaceBuilderAnalysis.ts` (+33 -23) 📝 `src/domain/interfaceBuilderKeyedArchive.ts` (+5 -1) 📝 `tests/boundary/filesystem/interfaceBuilderBundle.test.ts` (+89 -0) </details> ### 📄 Description ## Summary **Builds on #905** (same function). Until #905 merges, this diff includes its commits. The UIKit change is the last commit. Compiled UIKit NIBs and storyboards (iOS apps, and iOS apps installed on a Mac) archive connections as `UIRuntimeOutletConnection` and `UIRuntimeEventConnection`, with `UISource`, `UIDestination`, `UILabel`, and, for events, `UIEventMask`. The NIBArchive projection in `decode_interface_builder` recognized only AppKit `*Connector` classes with `NS`-prefixed keys. An iOS app therefore produced **no outlet or action edges at all**. The connection records also became `unknown` graph nodes, `UIProxyObject` (File's Owner, First Responder) was not a placeholder, and `UIClassSwapper` custom classes were not reported. The archived roles match AppKit's. In Stash 3.4.1 (`/Applications/Stash.app/Wrapper/Stash.app`), every event connection is control → target, for example `UIRuntimeEventConnection src=UISwitch dst=UIClassSwapper(_TtC5Stash10SwitchCell) label=switchAction: mask=4096`. Changes in `projectNibArchive`: - `UIRuntime*Connection` records are read like AppKit connectors, with the source (outlet owner or sending control) → the destination (outlet value or action target), a nil target being the first responder. Event connections are actions, and their raw `UIEventMask` is kept as the `ui_event_mask` attribute on the action. - Connection records are no longer emitted as object nodes. They were already excluded for `NSNib*Connector`. - `UIClassSwapper` reports its `UIClassName`, as `NSClassSwapper` reports `NSClassName`. Swift classes appear mangled, as archived, e.g. `_TtC5Stash10SwitchCell`. - `UIProxyObject` is classified as a placeholder. Its name comes from `UIProxiedObjectIdentifier`, e.g. `IBFilesOwner`. Stash, first 64 documents (the default limit): | | `main` / #905 | this PR | | --- | --- | --- | | edges | 0 | 372 (174 `outlet_to`, 174 `contains`, 24 `target_action`) | | outlet / action nodes | 0 / 0 | 174 / 8 | | `placeholder` nodes | 13 | 178 | | connection records as `unknown` nodes | 458 | 0 | No schema or catalog change. ## Verification - New case in `tests/boundary/filesystem/interfaceBuilderBundle.test.ts` (in its own describe block, under the 150-line function limit): a compiled UIKit NIB with a `UIClassSwapper` cell, `UIButton`, `UIProxyObject`, an event connection, and an outlet connection. It expects `UIButton → buildTapped: → BuildCell` with `ui_event_mask: 64`, the `buildButton` outlet to `UIButton`, `IBFilesOwner` as a placeholder, and no connection record nodes. Fails without this change. - Real app: Stash as in the table above. - `npm run check:changed` passes. The ibtool storyboard test fails locally only because this host lacks Xcode; it passes on CI's macOS job on #905. No Hopper/Ghidra/IDA verification is needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-16 21:17:32 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2340
No description provided.