Skip to content

Add an iOS withDangerousMod to the Expo config plugin that patches RCTThirdPartyComponentsProvider.mm#176

Open
Th0mYT wants to merge 43 commits intoazesmway:mainfrom
Th0mYT:main
Open

Add an iOS withDangerousMod to the Expo config plugin that patches RCTThirdPartyComponentsProvider.mm#176
Th0mYT wants to merge 43 commits intoazesmway:mainfrom
Th0mYT:main

Conversation

@Th0mYT
Copy link

@Th0mYT Th0mYT commented Feb 20, 2026

What was changed: plugin/src/index.ts (and its compiled output plugin/build/index.js)

What was broken: Expo's prebuild ran the C++ codegen for unityview but never generated the Fabric registration step. RNUnityViewCls() was defined in RNUnityView.mm but never
called, so Fabric couldn't find the component by name, fell back to the legacy interop layer, and updateProps was never dispatched.

The fix — withIosFabricRegistration: A new iOS withDangerousMod that runs at the end of every npx expo prebuild and patches RCTThirdPartyComponentsProvider.mm with two insertions:

// 1. Forward declaration (before @implementation)
Class RNUnityViewCls(void);

// 2. Dictionary entry (inside thirdPartyFabricComponents)
@"RNUnityView" : RNUnityViewCls(),

Call chain after the fix (matches what should happen):
codegenNativeComponent('RNUnityView')
→ Fabric finds RNUnityView via RCTThirdPartyComponentsProvider
→ initWithFrame: (lambda set, initUnityModule called as belt-and-suspenders)
→ updateEventEmitter:
→ updateProps: ← initUnityModule() ← Unity starts ✓

The initUnityModule call in initWithFrame: from the previous commit remains — it acts as a safety net for any edge case where updateProps might still be delayed.

- Detect Expo projects automatically via app.json
- Use correct framework search paths for both Expo and RN CLI
- Add Expo config plugin for automatic setup
- Update README with Expo-specific instructions

Fixes: Unity framework not found in Expo projects
@azesmway
Copy link
Owner

azesmway commented Mar 2, 2026

@Th0mYT Thanks for the improvements!
Can I take a look at everything and publish it?

@Th0mYT
Copy link
Author

Th0mYT commented Mar 2, 2026

Can I take a look at everything and publish it?

@azesmway of course. If you want, I'm working on the fork to further optimize the package for both Android and iOS. I'm also updating the plugin for expo. If you want, after finishing these changes and testing everything I can create a new PR with these changes.

@azesmway
Copy link
Owner

azesmway commented Mar 3, 2026

Can I take a look at everything and publish it?

@azesmway of course. If you want, I'm working on the fork to further optimize the package for both Android and iOS. I'm also updating the plugin for expo. If you want, after finishing these changes and testing everything I can create a new PR with these changes.

ok

@Th0mYT Th0mYT marked this pull request as draft March 3, 2026 11:02
@Th0mYT
Copy link
Author

Th0mYT commented Mar 3, 2026

Can I take a look at everything and publish it?

@azesmway of course. If you want, I'm working on the fork to further optimize the package for both Android and iOS. I'm also updating the plugin for expo. If you want, after finishing these changes and testing everything I can create a new PR with these changes.

ok

I updated the PR.

Summary of changes

ios/RNUnityView.mm — New Architecture fix

  • Unity is now initialized immediately inside initWithFrame: without waiting for updateProps to be called.
    This fixes a regression on New Arch where the Unity view would remain blank if no props were passed on mount.

android/.../UPlayer.java — Type cast fix

  • Fixed an unsafe cast that caused a compilation warning/error on some setups:
    return unityPlayer → return (FrameLayout)(Object) unityPlayer

plugin/src/index.ts — Expo config plugin refactor

Several issues fixed to make the plugin safe to run multiple times (e.g. after repeated npx expo prebuild):

  • withProjectBuildGradleMod: now strips existing flatDir / unityLibrary entries before re-inserting, preventing duplicate lines on every prebuild.
  • withSettingsGradleMod: same approach — filters out any existing unityLibrary lines before appending, so the include is always present exactly once.
  • withGradlePropertiesMod: checks if unityStreamingAssets property already exists before pushing it, avoiding duplicate gradle properties.
  • withStringsXMLMod: renamed internal variable from config to modConfig to avoid shadowing the outer config parameter.
  • withAndroidManifestMod (new): adds xmlns:tools to the manifest root and sets tools:replace="android:enableOnBackInvokedCallback" on the tag. This resolves a manifest merger conflict that occurs because unityLibrary declares this attribute with a different value.
  • withIosFabricRegistration (new): patches RCTThirdPartyComponentsProvider.mm (generated by expo prebuild) to register RNUnityView with Fabric's component registry. Without this, updateProps is never dispatched on New Arch iOS. The patch is idempotent and skips gracefully if the file doesn't exist yet.
  • Removed the unused name option from the plugin config signature.

tsconfig.json — Plugin path alias

  • Added @azesmway/react-native-unity/plugin → ./plugin/src/index to the path aliases so the plugin source can be resolved correctly during development.

package.json — Build setup for the plugin

  • Added app.plugin.js and plugin/build to the files array so the compiled plugin is included when the package is published.
  • Added build:plugin script: tsc --project plugin/tsconfig.json
  • Updated prepare to also run build:plugin after bob build.
  • Updated clean to also wipe plugin/build.

@Th0mYT Th0mYT marked this pull request as ready for review March 3, 2026 11:16
@Th0mYT Th0mYT marked this pull request as draft March 3, 2026 11:18
@Th0mYT Th0mYT marked this pull request as draft March 3, 2026 11:18
@Th0mYT Th0mYT marked this pull request as ready for review March 3, 2026 11:19
Th0mYT added 23 commits March 3, 2026 17:57
- Replace RCTEventEmitter with EventDispatcher for new arch compat
- Add dispatchEvent() helper with old/new arch routing + null checks
- Guard sendMessageToMobileApp against null view
- Fix pauseUnity to use static pause()/resume() to track _isUnityPaused
- Replace hardcoded "MyMessage" in callbacks with dispatchEvent()
- Log exceptions in createViewInstance instead of swallowing
- Clear stale view ref in onDropViewInstance if instance matches
- Add TAG constant for logging
…oid devices

- Set `android:enableOnBackInvokedCallback` to `false` in the manifest.
- Use `tools:replace` to override conflicting value set by `unityLibrary`.
- Avoid crashes caused by `OnBackInvokedCallback` on pre-API 33 devices.
- Post `onReady()` calls to the main thread to prevent view hierarchy updates off the main thread.
- Wrap `sendMessageToMobileApp` event dispatch in a main thread handler.
…ensions

- Post a forced layout runnable to address issues with parent views
…chitecture

- Add fallback using OnGlobalLayoutListener to handle delayed group dimensions.
- Use ThemedReactContext in view creation for proper surface association.
- Update dispatchEvent logic to retrieve surfaceId directly from ThemedReactContext if parent chain fails.
- Bump package version to 1.0.17.
- Reset z-elevation to make Unity visible after re-parenting.
- Add delayed resume to prevent black screen on remount by allowing surface creation.
- Bump package version to 1.0.18.
…arenting

- Introduce direct SurfaceView surfaceCreated hook for precise Unity resume timing.
- Add fallback for cases without SurfaceView by implementing a delayed resume.
- Refactor surface detection into a new helper method for cleaner logic.
- Adjust z-elevation for proper Unity visibility after re-parenting.
- Add precise surface resume logic using SurfaceView and OnPreDrawListener.
- Refactor resume timing to account for Choreographer and compositor updates.
- Add pause logic in `onDetachedFromWindow` to stop rendering frames before backgrounding.
- Prevents "BufferQueueProducer disconnect: not connected" error.
…ompatibility

- Handle FLAG_FULLSCREEN deprecation with conditional logic.
- Add detailed logging for UnityPlayer constructor resolution to improve debugging.
- Catch and log alignment errors on Unity native library loading.
- Enhance robustness of UnityPlayer initialization.
…ment adjustments

- Add a 2-second timeout fallback to resume Unity if surfaceCreated never fires.
- Ensure proper frame measurement before layout to prevent missed surfaceCreated events.
- Improve logging for better debugging of surface and group layout issues.
@Th0mYT Th0mYT marked this pull request as draft March 23, 2026 09:18
@Th0mYT
Copy link
Author

Th0mYT commented Mar 23, 2026

Hi @azesmway!

I've updated the PR with several improvements focused on stability and Android compatibility.
Here's a summary of what was addressed:

Android surface & rendering fixes

  • Fixed black screens after re-parenting the Unity view by improving surface handling
  • Added a timeout fallback and measurement adjustments to surface initialization
  • Unity rendering is now paused before surface destruction to prevent crashes
  • Improved resume timing to ensure the rendering surface is ready before Unity restarts

New Architecture (Fabric) compatibility

  • Fixed layout handling and event dispatch for the new React Native architecture
  • Force layout pass to ensure valid dimensions in Fabric mode
  • Thread safety improvements for Unity callbacks and event dispatch

Android API 30+ compatibility

  • Updated UnityPlayer instantiation and flag handling for API 30+
  • Disabled predictive back gesture to prevent crashes on older devices
  • Wrapped getSurfaceId in try-catch for safer handling

Other

  • Removed jcenter() (shut down) from repositories block
  • Expo compatibility improvements for iOS Unity framework integration

Happy to discuss any of the changes or provide more context if needed!

@Th0mYT Th0mYT marked this pull request as ready for review March 23, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants