Skip to content

Commit 58e5522

Browse files
committed
fix: limit appearance proxy accessors to UIKit
1 parent ac94954 commit 58e5522

4 files changed

Lines changed: 79 additions & 1 deletion

File tree

HANDOFF.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,39 @@ During this thread `agent-device` was version `0.18.0`; the npm package is
111111

112112
## Current Verified State
113113

114+
Update from 2026-06-29 13:47 EDT:
115+
116+
- Simulator-only rule remains active. Do not use physical iPhone/iPad devices.
117+
- GitHub Actions run `28390374047` on `ac94954e` compiled the edited V8 bridge
118+
in CI, then failed macOS `ApiTests.js Appearance` before iOS ran.
119+
- The failure was AppKit-only:
120+
`NSAppearance.appearanceNamed(NSAppearanceNameAqua).name` read back as
121+
`undefined`, so the safe UIAppearance accessor change had shadowed a real
122+
AppKit object's native `name` property.
123+
- Current runtime fix:
124+
- Static `appearance*` result tagging now requires
125+
`appearanceProxyCustomizableClassFromExactDescription(native)` to identify
126+
an exact UIKit customizable proxy target.
127+
- If the exact UIKit proxy description is not present, tagging/accessor
128+
installation is skipped. This avoids applying cache-backed UIAppearance
129+
accessors to AppKit `NSAppearance` instances.
130+
- Source coverage now blocks the receiver-class fallback that caused the
131+
shadowing regression.
132+
- Local verification after this correction:
133+
- `node packages/react-native/test/runtime-objc-property-setter.test.js`
134+
passed.
135+
- Runtime RN JS tests passed:
136+
`for f in packages/react-native/test/*.test.js; do node "$f" || exit 1; done`.
137+
- `npm run check:ffi-boundaries` passed.
138+
- `git diff --check` passed.
139+
- `npm run build:macos-cli` passed.
140+
- Focused local macOS `ApiTests`/`Appearance` launch attempt was blocked
141+
before app launch by the known local metadata-generator x86_64 link
142+
mismatch against arm64-only Xcode `libclang.dylib`.
143+
- Not done:
144+
- Commit/push the UIKit-proxy-only tagging correction and watch fresh PR CI.
145+
- If CI turns green, resume the dedicated simulator-only RNS parity sweep.
146+
114147
Update from 2026-06-29 13:21 EDT:
115148

116149
- Simulator-only rule remains active. Do not use physical iPhone/iPad devices.

NativeScript/ffi/shared/bridge/HostObjects.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,7 @@ Class tagStaticAppearanceNativeResult(
19081908
Class customizableClass =
19091909
appearanceProxyCustomizableClassFromExactDescription(native);
19101910
if (customizableClass == Nil) {
1911-
customizableClass = appearanceClass;
1911+
return Nil;
19121912
}
19131913
const char* className = class_getName(customizableClass);
19141914
if (className == nullptr || className[0] == '\0') {

PROGRESS.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,45 @@ TypeScript/UI worklets.
1717

1818
## Latest Update - 2026-06-29
1919

20+
### 2026-06-29 13:47 EDT - limit UIAppearance tagging to UIKit proxies
21+
22+
- Goal:
23+
- Keep PR #46 scoped to generic runtime primitives for the RN module branch
24+
and stay simulator-only. No physical devices are part of this fix.
25+
- CI finding:
26+
- GitHub Actions run `28390374047` on `ac94954e` compiled the edited V8
27+
bridge in CI, then failed macOS `ApiTests.js Appearance` before iOS ran.
28+
- The failure was AppKit-only: `NSAppearance.appearanceNamed(...)` returned
29+
an object, but `appearance.name` read back as `undefined` instead of
30+
containing `Aqua`.
31+
- Root cause: static selector tagging matched every class selector starting
32+
with `appearance`, then fell back to the receiver class when the native
33+
object was not an exact UIKit `UIAppearance` proxy. That mislabeled the
34+
real AppKit `NSAppearance` instance and installed cache-backed own
35+
accessors that shadowed native properties such as `name`.
36+
- Changes:
37+
- `tagStaticAppearanceNativeResult` now requires
38+
`appearanceProxyCustomizableClassFromExactDescription(native)` to identify
39+
an exact UIKit customizable proxy target. If it cannot, tagging/accessor
40+
installation is skipped.
41+
- Source coverage now blocks the receiver-class fallback so AppKit
42+
appearance objects keep their native property access.
43+
- Verification:
44+
- `node packages/react-native/test/runtime-objc-property-setter.test.js`
45+
passed.
46+
- Runtime RN JS tests passed:
47+
`for f in packages/react-native/test/*.test.js; do node "$f" || exit 1; done`.
48+
- `npm run check:ffi-boundaries` passed.
49+
- `git diff --check` passed.
50+
- `npm run build:macos-cli` passed and compiled/linked the edited V8 bridge.
51+
- Focused local macOS `ApiTests`/`Appearance` launch attempt was blocked
52+
before app launch by the known local metadata-generator x86_64 link
53+
mismatch against arm64-only Xcode `libclang.dylib`.
54+
- Still next:
55+
- Commit/push the correction and watch fresh PR CI for macOS recovery plus
56+
the authoritative iOS simulator result.
57+
- If CI turns green, resume the dedicated simulator-only RNS parity sweep.
58+
2059
### 2026-06-29 13:21 EDT - safe UIAppearance proxy accessors
2160

2261
- Goal:

packages/react-native/test/runtime-objc-property-setter.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ assert(
4646
runtimeHostObjects.includes("return customizableClass;"),
4747
"runtime UIAppearance result tagging should return the customizable target class",
4848
);
49+
assert(
50+
runtimeHostObjects.includes("appearanceProxyCustomizableClassFromExactDescription(native)") &&
51+
runtimeHostObjects.includes("if (customizableClass == Nil) {\n return Nil;\n }") &&
52+
!runtimeHostObjects.includes("customizableClass = appearanceClass;"),
53+
"runtime UIAppearance result tagging should require an exact UIKit proxy target and avoid shadowing AppKit appearance objects",
54+
);
4955
assert(
5056
runtimeHostObjects.includes("installAppearanceProxyPropertyAccessors") &&
5157
runtimeHostObjects.includes("makeAppearanceProxyPropertySetter") &&

0 commit comments

Comments
 (0)