From d2e7fc10ab50eaee1bc95dba987b7794de9c5fe3 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Fri, 20 Feb 2026 09:09:12 +0100 Subject: [PATCH 01/10] New Arch initWithFrame:: call initUnityModule so Unity starts regardless of updateProps --- ios/RNUnityView.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ios/RNUnityView.mm b/ios/RNUnityView.mm index a4caa74..d089f52 100644 --- a/ios/RNUnityView.mm +++ b/ios/RNUnityView.mm @@ -187,6 +187,11 @@ - (instancetype)initWithFrame:(CGRect)frame { gridViewEventEmitter->onUnityMessage(event); } }; + + // Start Unity immediately, don't wait for updateProps + if (![self unityIsInitialized]) { + [self initUnityModule]; + } } return self; From 094ca48f13c124f0400de947011dc31e79b095a9 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Fri, 20 Feb 2026 11:14:51 +0100 Subject: [PATCH 02/10] fix: add iOS Fabric registration for RNUnityView and fix Android strings mod configuration --- plugin/src/index.ts | 71 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 9dd56df..89e88b9 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -4,8 +4,11 @@ import { withProjectBuildGradle, withSettingsGradle, withStringsXml, + withDangerousMod, } from '@expo/config-plugins'; import type { ConfigPlugin } from '@expo/config-plugins'; +import * as fs from 'fs'; +import * as path from 'path'; const withUnity: ConfigPlugin<{ name?: string }> = ( config, @@ -16,6 +19,7 @@ const withUnity: ConfigPlugin<{ name?: string }> = ( config = withSettingsGradleMod(config); config = withGradlePropertiesMod(config); config = withStringsXMLMod(config); + config = withIosFabricRegistration(config); return config; }; @@ -59,8 +63,8 @@ const withGradlePropertiesMod: ConfigPlugin = (config) => // add string const withStringsXMLMod: ConfigPlugin = (config) => - withStringsXml(config, (config) => { - config.modResults = AndroidConfig.Strings.setStringItem( + withStringsXml(config, (modConfig) => { + modConfig.modResults = AndroidConfig.Strings.setStringItem( [ { _: 'Game View', @@ -69,9 +73,68 @@ const withStringsXMLMod: ConfigPlugin = (config) => }, }, ], - config.modResults + modConfig.modResults ); - return config; + return modConfig; }); +// Patches RCTThirdPartyComponentsProvider.mm (generated by expo prebuild) to register +// RNUnityView with Fabric's component registry so that updateProps is dispatched correctly. +const withIosFabricRegistration: ConfigPlugin = (config) => + withDangerousMod(config, [ + 'ios', + (modConfig) => { + const iosRoot = modConfig.modRequest.platformProjectRoot; + const projectName = modConfig.modRequest.projectName ?? ''; + + // The file may be at the ios/ root or inside ios// + const candidates = [ + path.join(iosRoot, 'RCTThirdPartyComponentsProvider.mm'), + path.join(iosRoot, projectName, 'RCTThirdPartyComponentsProvider.mm'), + ]; + + const providerPath = candidates.find((p) => fs.existsSync(p)); + if (!providerPath) { + console.warn( + '[react-native-unity] RCTThirdPartyComponentsProvider.mm not found. ' + + 'RNUnityView may not be registered with Fabric. ' + + 'Run `npx expo prebuild` again after the initial build.' + ); + return modConfig; + } + + let contents = fs.readFileSync(providerPath, 'utf-8'); + + const dictEntry = `@"RNUnityView" : RNUnityViewCls(),`; + + // Nothing to do if already patched + if (contents.includes(dictEntry)) { + return modConfig; + } + + const forwardDecl = + 'Class RNUnityViewCls(void);'; + + // Insert forward declaration before @implementation + if (!contents.includes(forwardDecl)) { + contents = contents.replace( + /(@implementation RCTThirdPartyComponentsProvider)/, + `${forwardDecl}\n\n$1` + ); + } + + // Insert dictionary entry before the closing }; of the components dict. + // The dict sits inside a dispatch_once block so the unique pattern is: + // (indent)};(whitespace)}); + contents = contents.replace( + /([ \t]*)(};)([ \t]*\n[ \t]*\}\);)/, + `$1 ${dictEntry}\n$1$2$3` + ); + + fs.writeFileSync(providerPath, contents, 'utf-8'); + + return modConfig; + }, + ]); + export default withUnity; From 977ef4623ec2cdf6f34ed34520c5411f33f7b134 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Fri, 20 Feb 2026 11:33:55 +0100 Subject: [PATCH 03/10] Add private field to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 063bf5e..3c6dd57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "@azesmway/react-native-unity", "version": "1.0.11", + "private": true, "description": "React Native Unity", "main": "lib/commonjs/index", "module": "lib/module/index", From f70723676bac256db71d17a72b253a9e83158897 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Tue, 24 Feb 2026 16:28:57 +0100 Subject: [PATCH 04/10] fix: type casting in UPlayer and update .gitignore --- .gitignore | 1 + android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d3b53df..b513993 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ android/keystores/debug.keystore # generated by bob lib/ +/azesmway-react-native-unity-1.0.11.tgz diff --git a/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java b/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java index b944779..4c3228e 100644 --- a/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java +++ b/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java @@ -97,7 +97,7 @@ public FrameLayout requestFrame() throws NoSuchMethodException { return (FrameLayout) getFrameLayout.invoke(unityPlayer); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - return unityPlayer; + return (FrameLayout)(object) unityPlayer; } } From 29fce81cfb75a2c1de56f073712b8a31927be77c Mon Sep 17 00:00:00 2001 From: Tommaso Date: Tue, 24 Feb 2026 16:37:26 +0100 Subject: [PATCH 05/10] fix: correct type casting in UPlayer and add pack script to package.json --- android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java b/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java index 4c3228e..bf44133 100644 --- a/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java +++ b/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java @@ -97,7 +97,7 @@ public FrameLayout requestFrame() throws NoSuchMethodException { return (FrameLayout) getFrameLayout.invoke(unityPlayer); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - return (FrameLayout)(object) unityPlayer; + return (FrameLayout)(Object) unityPlayer; } } diff --git a/package.json b/package.json index 3c6dd57..b227c97 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "lint": "eslint \"**/*.{js,ts,tsx}\"", "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", "prepare": "bob build", + "pack": "del-cli azesmway-react-native-unity-1.0.11.tgz && npm pack", "release": "release-it" }, "keywords": [ From e9aed188b403da06ac5246cdc5821b8af9a23302 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Tue, 24 Feb 2026 18:07:00 +0100 Subject: [PATCH 06/10] fix: update plugin mods to prevent duplicate entries and extend .gitignore --- package.json | 2 ++ plugin/src/index.ts | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b227c97..d824e41 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "ios", "cpp", "unity", + "app.plugin.js", + "plugin/build", "*.podspec", "!ios/build", "!android/build", diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 89e88b9..317db03 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -44,20 +44,27 @@ const withProjectBuildGradleMod: ConfigPlugin = (config) => const withSettingsGradleMod: ConfigPlugin = (config) => withSettingsGradle(config, (modConfig) => { - modConfig.modResults.contents += ` + if (!modConfig.modResults.contents.includes(':unityLibrary')) { + modConfig.modResults.contents += ` include ':unityLibrary' project(':unityLibrary').projectDir=new File('../unity/builds/android/unityLibrary') `; + } return modConfig; }); const withGradlePropertiesMod: ConfigPlugin = (config) => withGradleProperties(config, (modConfig) => { - modConfig.modResults.push({ - type: 'property', - key: 'unityStreamingAssets', - value: '.unity3d', - }); + const alreadySet = modConfig.modResults.some( + (item) => item.type === 'property' && item.key === 'unityStreamingAssets' + ); + if (!alreadySet) { + modConfig.modResults.push({ + type: 'property', + key: 'unityStreamingAssets', + value: '.unity3d', + }); + } return modConfig; }); From d36051046b8778d754de48c59f18f4a7a9454dff Mon Sep 17 00:00:00 2001 From: Tommaso Date: Wed, 25 Feb 2026 17:33:21 +0100 Subject: [PATCH 07/10] fix: update clean script and simplify plugin configuration --- package.json | 2 +- plugin/src/index.ts | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index d824e41..05905b4 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "test": "jest", "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", - "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", + "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib plugin/build", "prepare": "bob build", "pack": "del-cli azesmway-react-native-unity-1.0.11.tgz && npm pack", "release": "release-it" diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 317db03..60b751e 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -1,20 +1,16 @@ +import type { ConfigPlugin } from '@expo/config-plugins'; import { AndroidConfig, + withDangerousMod, withGradleProperties, withProjectBuildGradle, withSettingsGradle, withStringsXml, - withDangerousMod, } from '@expo/config-plugins'; -import type { ConfigPlugin } from '@expo/config-plugins'; import * as fs from 'fs'; import * as path from 'path'; -const withUnity: ConfigPlugin<{ name?: string }> = ( - config, - { name = 'react-native-unity' } = {} -) => { - config.name = name; +const withUnity: ConfigPlugin<{ name?: string }> = (config, {} = {}) => { config = withProjectBuildGradleMod(config); config = withSettingsGradleMod(config); config = withGradlePropertiesMod(config); From 76a4a1d27dd9303336ceda71aa5c336a632e6688 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Wed, 25 Feb 2026 17:46:00 +0100 Subject: [PATCH 08/10] fix: extend tsconfig paths and update build process for plugin --- package.json | 3 ++- tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 05905b4..b515f01 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,9 @@ "test": "jest", "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", + "build:plugin": "tsc --project plugin/tsconfig.json", "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib plugin/build", - "prepare": "bob build", + "prepare": "bob build && yarn build:plugin", "pack": "del-cli azesmway-react-native-unity-1.0.11.tgz && npm pack", "release": "release-it" }, diff --git a/tsconfig.json b/tsconfig.json index 05362c2..37e43d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@azesmway/react-native-unity": ["./src/index"] + "@azesmway/react-native-unity": ["./src/index"], + "@azesmway/react-native-unity/plugin": ["./plugin/src/index"], }, "allowUnreachableCode": false, "allowUnusedLabels": false, From c94c4dd4208fbe658a74d449ad99bee3abe20867 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Fri, 27 Feb 2026 11:30:20 +0100 Subject: [PATCH 09/10] fix: update plugin mod checks to prevent redundant unityLibrary entries and simplify config type --- plugin/src/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 60b751e..b51ec85 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -10,7 +10,7 @@ import { import * as fs from 'fs'; import * as path from 'path'; -const withUnity: ConfigPlugin<{ name?: string }> = (config, {} = {}) => { +const withUnity: ConfigPlugin<{}> = (config, {} = {}) => { config = withProjectBuildGradleMod(config); config = withSettingsGradleMod(config); config = withGradlePropertiesMod(config); @@ -23,7 +23,10 @@ const REPOSITORIES_END_LINE = `maven { url 'https://www.jitpack.io' }`; const withProjectBuildGradleMod: ConfigPlugin = (config) => withProjectBuildGradle(config, (modConfig) => { - if (modConfig.modResults.contents.includes(REPOSITORIES_END_LINE)) { + if ( + modConfig.modResults.contents.includes(REPOSITORIES_END_LINE) && + !modConfig.modResults.contents.includes(':unityLibrary') + ) { // use the last known line in expo's build.gradle file to append the newline after modConfig.modResults.contents = modConfig.modResults.contents.replace( REPOSITORIES_END_LINE, From 947e74c6ee24c4e98f578b5c5e760ff69673fac4 Mon Sep 17 00:00:00 2001 From: Tommaso Date: Fri, 27 Feb 2026 17:24:01 +0100 Subject: [PATCH 10/10] fix: remove unnecessary error throw in plugin mod to simplify config handling --- plugin/src/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugin/src/index.ts b/plugin/src/index.ts index b51ec85..5ac08b5 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -33,10 +33,6 @@ const withProjectBuildGradleMod: ConfigPlugin = (config) => REPOSITORIES_END_LINE + '\nflatDir { dirs "${project(\':unityLibrary\').projectDir}/libs" }\n' ); - } else { - throw new Error( - 'Failed to find the end of repositories in the android/build.gradle file`' - ); } return modConfig; });