From e1a1e5c9f9102dda44f2c4e43d99299a4ce059fc Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 22:53:01 +0800 Subject: [PATCH 1/8] feat: improve types, docs, and podspec for RN 0.85+ compatibility - Fix UnityView props to extend ViewProps (enables style typing without manual .d.ts workarounds) - Add troubleshooting section for common iOS/Android integration issues - Improve podspec prepare_command with warning when Unity framework missing - Include pre-built lib/ for GitHub-based installs - Update package metadata for fork Based on upstream v1.0.11 (azesmway/react-native-unity) --- README.md | 39 ++++++++++++ lib/commonjs/UnityView.js | 59 +++++++++++++++++++ lib/commonjs/UnityView.js.map | 1 + lib/commonjs/index.js | 10 ++++ lib/commonjs/index.js.map | 1 + .../specs/UnityViewNativeComponent.js | 14 +++++ .../specs/UnityViewNativeComponent.js.map | 1 + lib/module/UnityView.js | 49 +++++++++++++++ lib/module/UnityView.js.map | 1 + lib/module/index.js | 3 + lib/module/index.js.map | 1 + lib/module/specs/UnityViewNativeComponent.js | 7 +++ .../specs/UnityViewNativeComponent.js.map | 1 + lib/typescript/plugin/src/index.d.ts | 6 ++ lib/typescript/plugin/src/index.d.ts.map | 1 + lib/typescript/src/UnityView.d.ts | 28 +++++++++ lib/typescript/src/UnityView.d.ts.map | 1 + lib/typescript/src/__tests__/index.test.d.ts | 1 + .../src/__tests__/index.test.d.ts.map | 1 + lib/typescript/src/index.d.ts | 3 + lib/typescript/src/index.d.ts.map | 1 + .../src/specs/UnityViewNativeComponent.d.ts | 26 ++++++++ .../specs/UnityViewNativeComponent.d.ts.map | 1 + package.json | 8 +-- react-native-unity.podspec | 12 +++- src/UnityView.tsx | 3 +- 26 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 lib/commonjs/UnityView.js create mode 100644 lib/commonjs/UnityView.js.map create mode 100644 lib/commonjs/index.js create mode 100644 lib/commonjs/index.js.map create mode 100644 lib/commonjs/specs/UnityViewNativeComponent.js create mode 100644 lib/commonjs/specs/UnityViewNativeComponent.js.map create mode 100644 lib/module/UnityView.js create mode 100644 lib/module/UnityView.js.map create mode 100644 lib/module/index.js create mode 100644 lib/module/index.js.map create mode 100644 lib/module/specs/UnityViewNativeComponent.js create mode 100644 lib/module/specs/UnityViewNativeComponent.js.map create mode 100644 lib/typescript/plugin/src/index.d.ts create mode 100644 lib/typescript/plugin/src/index.d.ts.map create mode 100644 lib/typescript/src/UnityView.d.ts create mode 100644 lib/typescript/src/UnityView.d.ts.map create mode 100644 lib/typescript/src/__tests__/index.test.d.ts create mode 100644 lib/typescript/src/__tests__/index.test.d.ts.map create mode 100644 lib/typescript/src/index.d.ts create mode 100644 lib/typescript/src/index.d.ts.map create mode 100644 lib/typescript/src/specs/UnityViewNativeComponent.d.ts create mode 100644 lib/typescript/src/specs/UnityViewNativeComponent.d.ts.map diff --git a/README.md b/README.md index 7b747d4..2b8dbff 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,45 @@ export default Unity; - `pauseUnity?: (pause: boolean)` - pause the Unity - `windowFocusChanged(hasFocus: boolean = false)` - simulate focus change (intended to be used to recover from black screen (not rendering) after remounting Unity view when `resumeUnity` does not work) **ANDROID ONLY** +# Troubleshooting + +## iOS: Unity view not rendering / blank screen + +The `UnityView` component **must** have non-zero dimensions. Always use `style={{ flex: 1 }}` on both the parent container and the `UnityView` itself. The native `layoutSubviews` method checks for positive width and height before attaching the Unity root view. + +## iOS: `MTLTextureDescriptor has width of zero` crash + +This is the same root cause as above. The Unity Metal renderer crashes if it receives a zero-sized texture. Ensure the view has layout dimensions before the component mounts. + +## Android: Build fails with Java-related errors + +Unity's Android export requires a specific JDK version. Add the following to `android/gradle.properties`, pointing to the JDK that matches your Unity version: + +```gradle +org.gradle.java.home=/path/to/your/jdk +``` + +For Unity 6000.x, JDK 17 is typically required (e.g., Zulu JDK 17). + +## React Native 0.85+ / New Architecture (Fabric) + +This package supports New Architecture out of the box via codegen. On RN 0.85+, New Architecture is the default. Ensure: + +- `newArchEnabled=true` in `android/gradle.properties` +- Your Podfile uses `install_modules_dependencies` (standard in RN 0.71+) + +The podspec automatically detects and configures Fabric dependencies when available. + +## iOS: UnityFramework not found during `pod install` + +Ensure you have built the `UnityFramework.framework` from your Unity project and placed it at: + +``` +/unity/builds/ios/UnityFramework.framework +``` + +The `pod install` step copies this framework into the package. If the path is wrong, you will see a warning during installation. + # Contributing See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. diff --git a/lib/commonjs/UnityView.js b/lib/commonjs/UnityView.js new file mode 100644 index 0000000..be97bae --- /dev/null +++ b/lib/commonjs/UnityView.js @@ -0,0 +1,59 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _UnityViewNativeComponent = _interopRequireWildcard(require("./specs/UnityViewNativeComponent")); +var _reactNative = require("react-native"); +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } +class UnityView extends _react.default.Component { + ref = /*#__PURE__*/_react.default.createRef(); + postMessage = (gameObject, methodName, message) => { + if (this.ref.current) { + _UnityViewNativeComponent.Commands.postMessage(this.ref.current, gameObject, methodName, message); + } + }; + unloadUnity = () => { + if (this.ref.current) { + _UnityViewNativeComponent.Commands.unloadUnity(this.ref.current); + } + }; + pauseUnity(pause) { + if (this.ref.current) { + _UnityViewNativeComponent.Commands.pauseUnity(this.ref.current, pause); + } + } + resumeUnity() { + if (this.ref.current) { + _UnityViewNativeComponent.Commands.resumeUnity(this.ref.current); + } + } + windowFocusChanged(hasFocus = true) { + if (_reactNative.Platform.OS !== 'android') return; + if (this.ref.current) { + _UnityViewNativeComponent.Commands.windowFocusChanged(this.ref.current, hasFocus); + } + } + getProps() { + return { + ...this.props + }; + } + componentWillUnmount() { + if (this.ref.current) { + _UnityViewNativeComponent.Commands.unloadUnity(this.ref.current); + } + } + render() { + return /*#__PURE__*/_react.default.createElement(_UnityViewNativeComponent.default, _extends({ + ref: this.ref + }, this.getProps())); + } +} +exports.default = UnityView; +//# sourceMappingURL=UnityView.js.map \ No newline at end of file diff --git a/lib/commonjs/UnityView.js.map b/lib/commonjs/UnityView.js.map new file mode 100644 index 0000000..a968e08 --- /dev/null +++ b/lib/commonjs/UnityView.js.map @@ -0,0 +1 @@ +{"version":3,"names":["_react","_interopRequireDefault","require","_UnityViewNativeComponent","_interopRequireWildcard","_reactNative","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","prototype","hasOwnProperty","call","i","set","obj","_extends","assign","bind","target","arguments","length","source","key","apply","UnityView","React","Component","ref","createRef","postMessage","gameObject","methodName","message","current","Commands","unloadUnity","pauseUnity","pause","resumeUnity","windowFocusChanged","hasFocus","Platform","OS","getProps","props","componentWillUnmount","render","createElement","exports"],"sourceRoot":"../../src","sources":["UnityView.tsx"],"mappings":"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"} \ No newline at end of file diff --git a/lib/commonjs/index.js b/lib/commonjs/index.js new file mode 100644 index 0000000..aeee223 --- /dev/null +++ b/lib/commonjs/index.js @@ -0,0 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _UnityView = _interopRequireDefault(require("./UnityView")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +var _default = exports.default = _UnityView.default; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/lib/commonjs/index.js.map b/lib/commonjs/index.js.map new file mode 100644 index 0000000..3eb8053 --- /dev/null +++ b/lib/commonjs/index.js.map @@ -0,0 +1 @@ +{"version":3,"names":["_UnityView","_interopRequireDefault","require","obj","__esModule","default","_default","exports","UnityView"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;AAAA,IAAAA,UAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAoC,SAAAD,uBAAAE,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAErBG,kBAAS"} \ No newline at end of file diff --git a/lib/commonjs/specs/UnityViewNativeComponent.js b/lib/commonjs/specs/UnityViewNativeComponent.js new file mode 100644 index 0000000..18c3269 --- /dev/null +++ b/lib/commonjs/specs/UnityViewNativeComponent.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Commands = void 0; +var _codegenNativeComponent = _interopRequireDefault(require("react-native/Libraries/Utilities/codegenNativeComponent")); +var _codegenNativeCommands = _interopRequireDefault(require("react-native/Libraries/Utilities/codegenNativeCommands")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const Commands = exports.Commands = (0, _codegenNativeCommands.default)({ + supportedCommands: ['postMessage', 'unloadUnity', 'pauseUnity', 'resumeUnity', 'windowFocusChanged'] +}); +var _default = exports.default = (0, _codegenNativeComponent.default)('RNUnityView'); +//# sourceMappingURL=UnityViewNativeComponent.js.map \ No newline at end of file diff --git a/lib/commonjs/specs/UnityViewNativeComponent.js.map b/lib/commonjs/specs/UnityViewNativeComponent.js.map new file mode 100644 index 0000000..683d65b --- /dev/null +++ b/lib/commonjs/specs/UnityViewNativeComponent.js.map @@ -0,0 +1 @@ +{"version":3,"names":["_codegenNativeComponent","_interopRequireDefault","require","_codegenNativeCommands","obj","__esModule","default","Commands","exports","codegenNativeCommands","supportedCommands","_default","codegenNativeComponent"],"sourceRoot":"../../../src","sources":["specs/UnityViewNativeComponent.ts"],"mappings":";;;;;;AAAA,IAAAA,uBAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,sBAAA,GAAAF,sBAAA,CAAAC,OAAA;AAA2F,SAAAD,uBAAAG,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAmCpF,MAAMG,QAAwB,GAAAC,OAAA,CAAAD,QAAA,GAAG,IAAAE,8BAAqB,EAAiB;EAC5EC,iBAAiB,EAAE,CACjB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,oBAAoB;AAExB,CAAC,CAAC;AAAC,IAAAC,QAAA,GAAAH,OAAA,CAAAF,OAAA,GAEY,IAAAM,+BAAsB,EACnC,aACF,CAAC"} \ No newline at end of file diff --git a/lib/module/UnityView.js b/lib/module/UnityView.js new file mode 100644 index 0000000..70a0ffd --- /dev/null +++ b/lib/module/UnityView.js @@ -0,0 +1,49 @@ +function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } +import React from 'react'; +import NativeUnityView, { Commands } from './specs/UnityViewNativeComponent'; +import { Platform } from 'react-native'; +export default class UnityView extends React.Component { + ref = /*#__PURE__*/React.createRef(); + postMessage = (gameObject, methodName, message) => { + if (this.ref.current) { + Commands.postMessage(this.ref.current, gameObject, methodName, message); + } + }; + unloadUnity = () => { + if (this.ref.current) { + Commands.unloadUnity(this.ref.current); + } + }; + pauseUnity(pause) { + if (this.ref.current) { + Commands.pauseUnity(this.ref.current, pause); + } + } + resumeUnity() { + if (this.ref.current) { + Commands.resumeUnity(this.ref.current); + } + } + windowFocusChanged(hasFocus = true) { + if (Platform.OS !== 'android') return; + if (this.ref.current) { + Commands.windowFocusChanged(this.ref.current, hasFocus); + } + } + getProps() { + return { + ...this.props + }; + } + componentWillUnmount() { + if (this.ref.current) { + Commands.unloadUnity(this.ref.current); + } + } + render() { + return /*#__PURE__*/React.createElement(NativeUnityView, _extends({ + ref: this.ref + }, this.getProps())); + } +} +//# sourceMappingURL=UnityView.js.map \ No newline at end of file diff --git a/lib/module/UnityView.js.map b/lib/module/UnityView.js.map new file mode 100644 index 0000000..c0bf50f --- /dev/null +++ b/lib/module/UnityView.js.map @@ -0,0 +1 @@ +{"version":3,"names":["React","NativeUnityView","Commands","Platform","UnityView","Component","ref","createRef","postMessage","gameObject","methodName","message","current","unloadUnity","pauseUnity","pause","resumeUnity","windowFocusChanged","hasFocus","OS","getProps","props","componentWillUnmount","render","createElement","_extends"],"sourceRoot":"../../src","sources":["UnityView.tsx"],"mappings":";AAAA,OAAOA,KAAK,MAAM,OAAO;AAEzB,OAAOC,eAAe,IAAIC,QAAQ,QAAQ,kCAAkC;AAE5E,SAASC,QAAQ,QAAQ,cAAc;AAiBvC,eAAe,MAAMC,SAAS,SAASJ,KAAK,CAACK,SAAS,CAAmB;EACvEC,GAAG,gBAAGN,KAAK,CAACO,SAAS,CAAe,CAAC;EAE9BC,WAAW,GAAGA,CACnBC,UAAkB,EAClBC,UAAkB,EAClBC,OAAe,KACZ;IACH,IAAI,IAAI,CAACL,GAAG,CAACM,OAAO,EAAE;MACpBV,QAAQ,CAACM,WAAW,CAAC,IAAI,CAACF,GAAG,CAACM,OAAO,EAAEH,UAAU,EAAEC,UAAU,EAAEC,OAAO,CAAC;IACzE;EACF,CAAC;EAEME,WAAW,GAAGA,CAAA,KAAM;IACzB,IAAI,IAAI,CAACP,GAAG,CAACM,OAAO,EAAE;MACpBV,QAAQ,CAACW,WAAW,CAAC,IAAI,CAACP,GAAG,CAACM,OAAO,CAAC;IACxC;EACF,CAAC;EAEME,UAAUA,CAACC,KAAc,EAAE;IAChC,IAAI,IAAI,CAACT,GAAG,CAACM,OAAO,EAAE;MACpBV,QAAQ,CAACY,UAAU,CAAC,IAAI,CAACR,GAAG,CAACM,OAAO,EAAEG,KAAK,CAAC;IAC9C;EACF;EAEOC,WAAWA,CAAA,EAAG;IACnB,IAAI,IAAI,CAACV,GAAG,CAACM,OAAO,EAAE;MACpBV,QAAQ,CAACc,WAAW,CAAC,IAAI,CAACV,GAAG,CAACM,OAAO,CAAC;IACxC;EACF;EAEOK,kBAAkBA,CAACC,QAAQ,GAAG,IAAI,EAAE;IACzC,IAAIf,QAAQ,CAACgB,EAAE,KAAK,SAAS,EAAE;IAE/B,IAAI,IAAI,CAACb,GAAG,CAACM,OAAO,EAAE;MACpBV,QAAQ,CAACe,kBAAkB,CAAC,IAAI,CAACX,GAAG,CAACM,OAAO,EAAEM,QAAQ,CAAC;IACzD;EACF;EAEQE,QAAQA,CAAA,EAAG;IACjB,OAAO;MACL,GAAG,IAAI,CAACC;IACV,CAAC;EACH;EAEAC,oBAAoBA,CAAA,EAAG;IACrB,IAAI,IAAI,CAAChB,GAAG,CAACM,OAAO,EAAE;MACpBV,QAAQ,CAACW,WAAW,CAAC,IAAI,CAACP,GAAG,CAACM,OAAO,CAAC;IACxC;EACF;EAEAW,MAAMA,CAAA,EAAG;IACP,oBAAOvB,KAAA,CAAAwB,aAAA,CAACvB,eAAe,EAAAwB,QAAA;MAACnB,GAAG,EAAE,IAAI,CAACA;IAAI,GAAK,IAAI,CAACc,QAAQ,CAAC,CAAC,CAAG,CAAC;EAChE;AACF"} \ No newline at end of file diff --git a/lib/module/index.js b/lib/module/index.js new file mode 100644 index 0000000..646526b --- /dev/null +++ b/lib/module/index.js @@ -0,0 +1,3 @@ +import UnityView from './UnityView'; +export default UnityView; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/lib/module/index.js.map b/lib/module/index.js.map new file mode 100644 index 0000000..e414676 --- /dev/null +++ b/lib/module/index.js.map @@ -0,0 +1 @@ +{"version":3,"names":["UnityView"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"AAAA,OAAOA,SAAS,MAAM,aAAa;AAEnC,eAAeA,SAAS"} \ No newline at end of file diff --git a/lib/module/specs/UnityViewNativeComponent.js b/lib/module/specs/UnityViewNativeComponent.js new file mode 100644 index 0000000..dc24569 --- /dev/null +++ b/lib/module/specs/UnityViewNativeComponent.js @@ -0,0 +1,7 @@ +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +export const Commands = codegenNativeCommands({ + supportedCommands: ['postMessage', 'unloadUnity', 'pauseUnity', 'resumeUnity', 'windowFocusChanged'] +}); +export default codegenNativeComponent('RNUnityView'); +//# sourceMappingURL=UnityViewNativeComponent.js.map \ No newline at end of file diff --git a/lib/module/specs/UnityViewNativeComponent.js.map b/lib/module/specs/UnityViewNativeComponent.js.map new file mode 100644 index 0000000..6a9d536 --- /dev/null +++ b/lib/module/specs/UnityViewNativeComponent.js.map @@ -0,0 +1 @@ +{"version":3,"names":["codegenNativeComponent","codegenNativeCommands","Commands","supportedCommands"],"sourceRoot":"../../../src","sources":["specs/UnityViewNativeComponent.ts"],"mappings":"AAAA,OAAOA,sBAAsB,MAAM,yDAAyD;AAC5F,OAAOC,qBAAqB,MAAM,wDAAwD;AAmC1F,OAAO,MAAMC,QAAwB,GAAGD,qBAAqB,CAAiB;EAC5EE,iBAAiB,EAAE,CACjB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,oBAAoB;AAExB,CAAC,CAAC;AAEF,eAAeH,sBAAsB,CACnC,aACF,CAAC"} \ No newline at end of file diff --git a/lib/typescript/plugin/src/index.d.ts b/lib/typescript/plugin/src/index.d.ts new file mode 100644 index 0000000..50afa16 --- /dev/null +++ b/lib/typescript/plugin/src/index.d.ts @@ -0,0 +1,6 @@ +import type { ConfigPlugin } from '@expo/config-plugins'; +declare const withUnity: ConfigPlugin<{ + name?: string; +}>; +export default withUnity; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/lib/typescript/plugin/src/index.d.ts.map b/lib/typescript/plugin/src/index.d.ts.map new file mode 100644 index 0000000..43476ac --- /dev/null +++ b/lib/typescript/plugin/src/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../plugin/src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,QAAA,MAAM,SAAS,EAAE,YAAY,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAU9C,CAAC;AAyDF,eAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/lib/typescript/src/UnityView.d.ts b/lib/typescript/src/UnityView.d.ts new file mode 100644 index 0000000..4064610 --- /dev/null +++ b/lib/typescript/src/UnityView.d.ts @@ -0,0 +1,28 @@ +/// +/// +import React from 'react'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; +import type { ViewProps } from 'react-native'; +type UnityViewContentUpdateEvent = Readonly<{ + message: string; +}>; +type RNUnityViewProps = ViewProps & { + androidKeepPlayerMounted?: boolean; + fullScreen?: boolean; + onUnityMessage?: DirectEventHandler; + onPlayerUnload?: DirectEventHandler; + onPlayerQuit?: DirectEventHandler; +}; +export default class UnityView extends React.Component { + ref: React.RefObject & Readonly>; + postMessage: (gameObject: string, methodName: string, message: string) => void; + unloadUnity: () => void; + pauseUnity(pause: boolean): void; + resumeUnity(): void; + windowFocusChanged(hasFocus?: boolean): void; + private getProps; + componentWillUnmount(): void; + render(): JSX.Element; +} +export {}; +//# sourceMappingURL=UnityView.d.ts.map \ No newline at end of file diff --git a/lib/typescript/src/UnityView.d.ts.map b/lib/typescript/src/UnityView.d.ts.map new file mode 100644 index 0000000..0e610c4 --- /dev/null +++ b/lib/typescript/src/UnityView.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UnityView.d.ts","sourceRoot":"","sources":["../../../src/UnityView.tsx"],"names":[],"mappings":";;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAEpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,KAAK,2BAA2B,GAAG,QAAQ,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,KAAK,gBAAgB,GAAG,SAAS,GAAG;IAClC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;IACjE,cAAc,CAAC,EAAE,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;IACjE,YAAY,CAAC,EAAE,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;CAChE,CAAC;AAIF,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC;IACtE,GAAG,qJAAmC;IAE/B,WAAW,eACJ,MAAM,cACN,MAAM,WACT,MAAM,UAKf;IAEK,WAAW,aAIhB;IAEK,UAAU,CAAC,KAAK,EAAE,OAAO;IAMzB,WAAW;IAMX,kBAAkB,CAAC,QAAQ,UAAO;IAQzC,OAAO,CAAC,QAAQ;IAMhB,oBAAoB;IAMpB,MAAM;CAGP"} \ No newline at end of file diff --git a/lib/typescript/src/__tests__/index.test.d.ts b/lib/typescript/src/__tests__/index.test.d.ts new file mode 100644 index 0000000..cbbd668 --- /dev/null +++ b/lib/typescript/src/__tests__/index.test.d.ts @@ -0,0 +1 @@ +//# sourceMappingURL=index.test.d.ts.map \ No newline at end of file diff --git a/lib/typescript/src/__tests__/index.test.d.ts.map b/lib/typescript/src/__tests__/index.test.d.ts.map new file mode 100644 index 0000000..7b9421a --- /dev/null +++ b/lib/typescript/src/__tests__/index.test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/index.test.tsx"],"names":[],"mappings":""} \ No newline at end of file diff --git a/lib/typescript/src/index.d.ts b/lib/typescript/src/index.d.ts new file mode 100644 index 0000000..d13ac04 --- /dev/null +++ b/lib/typescript/src/index.d.ts @@ -0,0 +1,3 @@ +import UnityView from './UnityView'; +export default UnityView; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/lib/typescript/src/index.d.ts.map b/lib/typescript/src/index.d.ts.map new file mode 100644 index 0000000..b5dacd5 --- /dev/null +++ b/lib/typescript/src/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,aAAa,CAAC;AAEpC,eAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/lib/typescript/src/specs/UnityViewNativeComponent.d.ts b/lib/typescript/src/specs/UnityViewNativeComponent.d.ts new file mode 100644 index 0000000..e5a1a06 --- /dev/null +++ b/lib/typescript/src/specs/UnityViewNativeComponent.d.ts @@ -0,0 +1,26 @@ +/// +/// +/// +import type { HostComponent, ViewProps } from 'react-native'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; +export type UnityViewContentUpdateEvent = Readonly<{ + message: string; +}>; +export interface NativeProps extends ViewProps { + androidKeepPlayerMounted?: boolean; + fullScreen?: boolean; + onUnityMessage?: DirectEventHandler; + onPlayerUnload?: DirectEventHandler; + onPlayerQuit?: DirectEventHandler; +} +export interface NativeCommands { + postMessage: (viewRef: React.ElementRef>, gameObject: string, methodName: string, message: string) => void; + unloadUnity: (viewRef: React.ElementRef>) => void; + pauseUnity: (viewRef: React.ElementRef>, pause: boolean) => void; + resumeUnity: (viewRef: React.ElementRef>) => void; + windowFocusChanged: (viewRef: React.ElementRef>, hasFocus: boolean) => void; +} +export declare const Commands: NativeCommands; +declare const _default: HostComponent; +export default _default; +//# sourceMappingURL=UnityViewNativeComponent.d.ts.map \ No newline at end of file diff --git a/lib/typescript/src/specs/UnityViewNativeComponent.d.ts.map b/lib/typescript/src/specs/UnityViewNativeComponent.d.ts.map new file mode 100644 index 0000000..73e15f1 --- /dev/null +++ b/lib/typescript/src/specs/UnityViewNativeComponent.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"UnityViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../../src/specs/UnityViewNativeComponent.ts"],"names":[],"mappings":";;;AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAEpF,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;IACjE,cAAc,CAAC,EAAE,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;IACjE,YAAY,CAAC,EAAE,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,CACX,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,EACrD,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,KACZ,IAAI,CAAC;IACV,WAAW,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;IAC7E,UAAU,EAAE,CACV,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,EACrD,KAAK,EAAE,OAAO,KACX,IAAI,CAAC;IACV,WAAW,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,CAAC;IAC7E,kBAAkB,EAAE,CAClB,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,EACrD,QAAQ,EAAE,OAAO,KACd,IAAI,CAAC;CACX;AAED,eAAO,MAAM,QAAQ,EAAE,cAQrB,CAAC;;AAEH,wBAEgC"} \ No newline at end of file diff --git a/package.json b/package.json index 063bf5e..b3dfb20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@azesmway/react-native-unity", - "version": "1.0.11", + "version": "1.0.11-fork.1", "description": "React Native Unity", "main": "lib/commonjs/index", "module": "lib/module/index", @@ -43,14 +43,14 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/azesmway/react-native-unity.git" + "url": "git+https://github.com/leaguefun/react-native-unity.git" }, "author": "Alexey Zolotaryov (https://github.com/azesmway)", "license": "MIT", "bugs": { - "url": "https://github.com/azesmway/react-native-unity/issues" + "url": "https://github.com/leaguefun/react-native-unity/issues" }, - "homepage": "https://github.com/azesmway/react-native-unity#readme", + "homepage": "https://github.com/leaguefun/react-native-unity#readme", "publishConfig": { "registry": "https://registry.npmjs.org/", "access": "public" diff --git a/react-native-unity.podspec b/react-native-unity.podspec index af0ea2c..99efe4a 100644 --- a/react-native-unity.podspec +++ b/react-native-unity.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.authors = package["author"] s.platforms = { :ios => "12.4" } - s.source = { :git => "https://github.com/azesmway/react-native-unity.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/leaguefun/react-native-unity.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m,mm}" @@ -45,7 +45,15 @@ Pod::Spec.new do |s| # The framework should be placed in the /unity/builds/ios folder. s.prepare_command = <<-CMD - cp -R ../../../unity/builds/ios/ ios/ + UNITY_BUILD_SRC="../../../unity/builds/ios/" + if [ -d "$UNITY_BUILD_SRC" ]; then + cp -R "$UNITY_BUILD_SRC" ios/ + else + echo "" + echo "WARNING: react-native-unity: Unity iOS framework not found at $UNITY_BUILD_SRC" + echo " Place UnityFramework.framework in /unity/builds/ios/" + echo "" + fi CMD s.vendored_frameworks = ["ios/UnityFramework.framework"] diff --git a/src/UnityView.tsx b/src/UnityView.tsx index d9f1b9c..becba62 100644 --- a/src/UnityView.tsx +++ b/src/UnityView.tsx @@ -3,12 +3,13 @@ import React from 'react'; import NativeUnityView, { Commands } from './specs/UnityViewNativeComponent'; import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; import { Platform } from 'react-native'; +import type { ViewProps } from 'react-native'; type UnityViewContentUpdateEvent = Readonly<{ message: string; }>; -type RNUnityViewProps = { +type RNUnityViewProps = ViewProps & { androidKeepPlayerMounted?: boolean; fullScreen?: boolean; onUnityMessage?: DirectEventHandler; From 37a96e35f8e4c3e3af2a8511168b3e2a2da1b681 Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 22:54:35 +0800 Subject: [PATCH 2/8] fix: correct types entry path in package.json The types field pointed to lib/typescript/index.d.ts but the actual generated file is at lib/typescript/src/index.d.ts. This caused TypeScript to not find declarations when installed from GitHub. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3dfb20..2826dfd 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "React Native Unity", "main": "lib/commonjs/index", "module": "lib/module/index", - "types": "lib/typescript/index.d.ts", + "types": "lib/typescript/src/index.d.ts", "react-native": "src/index", "source": "src/index", "files": [ From 27829135731f37b9468754fd9798909741494c6c Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 23:02:49 +0800 Subject: [PATCH 3/8] fix: remove jcenter and hardcoded buildscript for Gradle 9.x compat - Remove buildscript block with hardcoded AGP 7.4.2 (conflicts with host project's AGP version) - Remove jcenter() repository (removed in Gradle 9.x) - Replace deprecated lintOptions with lint block --- android/build.gradle | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a10543a..1d29b9e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath "com.android.tools.build:gradle:7.4.2" - } -} - def isNewArchitectureEnabled() { return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" } @@ -66,7 +55,7 @@ android { } } - lintOptions { + lint { disable "GradleCompatible" } @@ -98,7 +87,6 @@ repositories { } google() mavenCentral() - jcenter() } From 4426f494521c09b48dfc09c8a1abb1a30bb03de8 Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 23:06:07 +0800 Subject: [PATCH 4/8] fix: handle Unity 6 UnityPlayer no longer extending FrameLayout In Unity 6+, UnityPlayer no longer extends FrameLayout directly. The requestFrame() fallback tried to return unityPlayer as FrameLayout which caused a compilation error. Now tries getView() via reflection as an intermediate fallback, then instanceof check for legacy versions. --- .../com/azesmwayreactnativeunity/UPlayer.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java b/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java index b944779..52dff70 100644 --- a/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java +++ b/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java @@ -92,13 +92,30 @@ public void requestFocusPlayer() throws NoSuchMethodException, InvocationTargetE } public FrameLayout requestFrame() throws NoSuchMethodException { + // Unity 6+: UnityPlayer no longer extends FrameLayout, use getFrameLayout() try { Method getFrameLayout = unityPlayer.getClass().getMethod("getFrameLayout"); - return (FrameLayout) getFrameLayout.invoke(unityPlayer); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - return unityPlayer; + // Fall through to getView() + } + // Unity 6+ alternate: try getView() + try { + Method getView = unityPlayer.getClass().getMethod("getView"); + Object view = getView.invoke(unityPlayer); + if (view instanceof FrameLayout) { + return (FrameLayout) view; + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // Fall through + } + // Legacy Unity (pre-6): UnityPlayer itself extends FrameLayout + // Cast through Object to bypass compile-time type check + Object player = unityPlayer; + if (player instanceof FrameLayout) { + return (FrameLayout) player; } + return null; } public void setZ(float v) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { From 7e32966882916c98ccb01272359b35f2a480b407 Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 23:26:41 +0800 Subject: [PATCH 5/8] chore: add diagnostic logging to Unity initialization --- ios/RNUnityView.mm | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ios/RNUnityView.mm b/ios/RNUnityView.mm index a4caa74..d9cdecf 100644 --- a/ios/RNUnityView.mm +++ b/ios/RNUnityView.mm @@ -45,10 +45,19 @@ - (bool)unityIsInitialized { - (void)initUnityModule { @try { if([self unityIsInitialized]) { + NSLog(@"[RNUnity] Already initialized, skipping"); return; } + NSLog(@"[RNUnity] Loading UnityFramework..."); [self setUfw: UnityFrameworkLoad()]; + + if (![self ufw]) { + NSLog(@"[RNUnity] ERROR: UnityFrameworkLoad returned nil"); + return; + } + NSLog(@"[RNUnity] Framework loaded successfully"); + [[self ufw] registerFrameworkListener: self]; unsigned count = (int) [[[NSProcessInfo processInfo] arguments] count]; @@ -60,7 +69,15 @@ - (void)initUnityModule { } array[count] = NULL; + NSLog(@"[RNUnity] Calling runEmbeddedWithArgc..."); [[self ufw] runEmbeddedWithArgc: gArgc argv: array appLaunchOpts: appLaunchOpts]; + + if (![[self ufw] appController]) { + NSLog(@"[RNUnity] ERROR: appController is nil after runEmbedded"); + return; + } + NSLog(@"[RNUnity] appController ready, rootView=%@", self.ufw.appController.rootView); + [[self ufw] appController].quitHandler = ^(){ NSLog(@"AppController.quitHandler called"); }; [self.ufw.appController.rootView removeFromSuperview]; @@ -75,9 +92,10 @@ - (void)initUnityModule { [[[[[[self ufw] appController] window] rootViewController] view] setNeedsLayout]; [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self]; + NSLog(@"[RNUnity] Init complete. bounds=%@", NSStringFromCGRect(self.bounds)); } @catch (NSException *e) { - NSLog(@"%@",e); + NSLog(@"[RNUnity] EXCEPTION: %@", e); } } From ca0484ccd8fe1e1f1c44c3eab884de83d1b8027a Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 23:39:28 +0800 Subject: [PATCH 6/8] fix: clean up window management and add visible diagnostics - Remove contradictory windowScene:nil + makeKeyAndVisible sequence - Hide Unity's window instead, restore RN window as key - Add rootView directly to self instead of back to Unity's window - Add visible debug label to show init status on-screen - Support iOS 13+ scene-based window discovery Co-Authored-By: Claude Opus 4.6 --- ios/RNUnityView.mm | 88 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/ios/RNUnityView.mm b/ios/RNUnityView.mm index d9cdecf..3e5e942 100644 --- a/ios/RNUnityView.mm +++ b/ios/RNUnityView.mm @@ -42,21 +42,38 @@ - (bool)unityIsInitialized { return [self ufw] && [[self ufw] appController]; } +- (void)showDebugStatus:(NSString *)status { + // Temporary visible diagnostic — remove once Unity init is confirmed working + UILabel *label = [self viewWithTag:9999]; + if (!label) { + label = [[UILabel alloc] initWithFrame:CGRectMake(10, 40, 350, 80)]; + label.tag = 9999; + label.numberOfLines = 0; + label.font = [UIFont systemFontOfSize:11]; + label.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7]; + label.textColor = [UIColor greenColor]; + label.layer.zPosition = 9999; + [self addSubview:label]; + } + label.text = status; + NSLog(@"[RNUnity] %@", status); +} + - (void)initUnityModule { @try { if([self unityIsInitialized]) { - NSLog(@"[RNUnity] Already initialized, skipping"); + [self showDebugStatus:@"Already initialized"]; return; } - NSLog(@"[RNUnity] Loading UnityFramework..."); + [self showDebugStatus:@"Loading framework..."]; [self setUfw: UnityFrameworkLoad()]; if (![self ufw]) { - NSLog(@"[RNUnity] ERROR: UnityFrameworkLoad returned nil"); + [self showDebugStatus:@"ERROR: UnityFrameworkLoad returned nil"]; return; } - NSLog(@"[RNUnity] Framework loaded successfully"); + [self showDebugStatus:@"Framework loaded, registering listener..."]; [[self ufw] registerFrameworkListener: self]; @@ -69,33 +86,64 @@ - (void)initUnityModule { } array[count] = NULL; - NSLog(@"[RNUnity] Calling runEmbeddedWithArgc..."); + [self showDebugStatus:@"Calling runEmbeddedWithArgc..."]; [[self ufw] runEmbeddedWithArgc: gArgc argv: array appLaunchOpts: appLaunchOpts]; if (![[self ufw] appController]) { - NSLog(@"[RNUnity] ERROR: appController is nil after runEmbedded"); + [self showDebugStatus:@"ERROR: appController nil after runEmbedded"]; + return; + } + + UIView *unityRootView = self.ufw.appController.rootView; + if (!unityRootView) { + [self showDebugStatus:@"ERROR: rootView is nil"]; return; } - NSLog(@"[RNUnity] appController ready, rootView=%@", self.ufw.appController.rootView); + + [self showDebugStatus:[NSString stringWithFormat:@"appController OK, rootView=%@", unityRootView]]; [[self ufw] appController].quitHandler = ^(){ NSLog(@"AppController.quitHandler called"); }; - [self.ufw.appController.rootView removeFromSuperview]; - if (@available(iOS 13.0, *)) { - [[[[self ufw] appController] window] setWindowScene: nil]; - } else { - [[[[self ufw] appController] window] setScreen: nil]; + // Remove Unity's rootView from Unity's own view hierarchy + [unityRootView removeFromSuperview]; + + // Hide Unity's window so it doesn't cover React Native's UI + UIWindow *unityWindow = [[[self ufw] appController] window]; + unityWindow.hidden = YES; + + // Restore React Native's window as key window + UIWindow *rnWindow = [[[UIApplication sharedApplication] delegate] window]; + if (!rnWindow) { + // iOS 13+ scene-based: find the first connected scene's window + if (@available(iOS 13.0, *)) { + for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) { + if ([scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + for (UIWindow *w in windowScene.windows) { + if (w != unityWindow) { + rnWindow = w; + break; + } + } + if (rnWindow) break; + } + } + } + } + if (rnWindow) { + [rnWindow makeKeyAndVisible]; } - [[[[self ufw] appController] window] addSubview: self.ufw.appController.rootView]; - [[[[self ufw] appController] window] makeKeyAndVisible]; - [[[[[[self ufw] appController] window] rootViewController] view] setNeedsLayout]; + // Add Unity's rendering view to self (the RN component view) + unityRootView.frame = self.bounds; + [self addSubview:unityRootView]; [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self]; - NSLog(@"[RNUnity] Init complete. bounds=%@", NSStringFromCGRect(self.bounds)); + [self showDebugStatus:[NSString stringWithFormat:@"INIT OK bounds=%@ rootView.frame=%@", + NSStringFromCGRect(self.bounds), NSStringFromCGRect(unityRootView.frame)]]; } @catch (NSException *e) { - NSLog(@"[RNUnity] EXCEPTION: %@", e); + [self showDebugStatus:[NSString stringWithFormat:@"EXCEPTION: %@", e.reason]]; } } @@ -106,6 +154,12 @@ - (void)layoutSubviews { self.ufw.appController.rootView.frame = self.bounds; [self addSubview:self.ufw.appController.rootView]; } + + // Keep debug label on top + UILabel *label = [self viewWithTag:9999]; + if (label) { + [self bringSubviewToFront:label]; + } } - (void)pauseUnity:(BOOL * _Nonnull)pause { From f7d6313793f38210784140d0af438fd519f56762 Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 23:49:17 +0800 Subject: [PATCH 7/8] fix: add iOS componentProvider to codegenConfig for Fabric registration Without componentProvider mapping, RNUnityView is not registered in RCTThirdPartyComponentsProvider and the native view is never created. Co-Authored-By: Claude Opus 4.6 --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2826dfd..3d1f096 100644 --- a/package.json +++ b/package.json @@ -172,6 +172,11 @@ "codegenConfig": { "name": "unityview", "type": "components", - "jsSrcsDir": "src" + "jsSrcsDir": "src", + "ios": { + "componentProvider": { + "RNUnityView": "RNUnityView" + } + } } } From 861ca3fede33e143792e9e69020ddd09ccc3dae7 Mon Sep 17 00:00:00 2001 From: Leaguefun Date: Wed, 3 Jun 2026 23:52:42 +0800 Subject: [PATCH 8/8] chore: remove debug diagnostics, keep clean window management Co-Authored-By: Claude Opus 4.6 --- ios/RNUnityView.mm | 44 +++++--------------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/ios/RNUnityView.mm b/ios/RNUnityView.mm index 3e5e942..12c52c3 100644 --- a/ios/RNUnityView.mm +++ b/ios/RNUnityView.mm @@ -42,38 +42,18 @@ - (bool)unityIsInitialized { return [self ufw] && [[self ufw] appController]; } -- (void)showDebugStatus:(NSString *)status { - // Temporary visible diagnostic — remove once Unity init is confirmed working - UILabel *label = [self viewWithTag:9999]; - if (!label) { - label = [[UILabel alloc] initWithFrame:CGRectMake(10, 40, 350, 80)]; - label.tag = 9999; - label.numberOfLines = 0; - label.font = [UIFont systemFontOfSize:11]; - label.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7]; - label.textColor = [UIColor greenColor]; - label.layer.zPosition = 9999; - [self addSubview:label]; - } - label.text = status; - NSLog(@"[RNUnity] %@", status); -} - - (void)initUnityModule { @try { if([self unityIsInitialized]) { - [self showDebugStatus:@"Already initialized"]; return; } - [self showDebugStatus:@"Loading framework..."]; [self setUfw: UnityFrameworkLoad()]; if (![self ufw]) { - [self showDebugStatus:@"ERROR: UnityFrameworkLoad returned nil"]; + NSLog(@"[RNUnity] ERROR: UnityFrameworkLoad returned nil"); return; } - [self showDebugStatus:@"Framework loaded, registering listener..."]; [[self ufw] registerFrameworkListener: self]; @@ -86,25 +66,17 @@ - (void)initUnityModule { } array[count] = NULL; - [self showDebugStatus:@"Calling runEmbeddedWithArgc..."]; [[self ufw] runEmbeddedWithArgc: gArgc argv: array appLaunchOpts: appLaunchOpts]; if (![[self ufw] appController]) { - [self showDebugStatus:@"ERROR: appController nil after runEmbedded"]; + NSLog(@"[RNUnity] ERROR: appController nil after runEmbedded"); return; } - UIView *unityRootView = self.ufw.appController.rootView; - if (!unityRootView) { - [self showDebugStatus:@"ERROR: rootView is nil"]; - return; - } - - [self showDebugStatus:[NSString stringWithFormat:@"appController OK, rootView=%@", unityRootView]]; - [[self ufw] appController].quitHandler = ^(){ NSLog(@"AppController.quitHandler called"); }; // Remove Unity's rootView from Unity's own view hierarchy + UIView *unityRootView = self.ufw.appController.rootView; [unityRootView removeFromSuperview]; // Hide Unity's window so it doesn't cover React Native's UI @@ -139,11 +111,9 @@ - (void)initUnityModule { [self addSubview:unityRootView]; [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self]; - [self showDebugStatus:[NSString stringWithFormat:@"INIT OK bounds=%@ rootView.frame=%@", - NSStringFromCGRect(self.bounds), NSStringFromCGRect(unityRootView.frame)]]; } @catch (NSException *e) { - [self showDebugStatus:[NSString stringWithFormat:@"EXCEPTION: %@", e.reason]]; + NSLog(@"[RNUnity] EXCEPTION: %@", e.reason); } } @@ -155,11 +125,7 @@ - (void)layoutSubviews { [self addSubview:self.ufw.appController.rootView]; } - // Keep debug label on top - UILabel *label = [self viewWithTag:9999]; - if (label) { - [self bringSubviewToFront:label]; - } + } - (void)pauseUnity:(BOOL * _Nonnull)pause {