Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ android/keystores/debug.keystore

# generated by bob
lib/
/azesmway-react-native-unity-1.0.11.tgz
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
5 changes: 5 additions & 0 deletions ios/RNUnityView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -14,6 +15,8 @@
"ios",
"cpp",
"unity",
"app.plugin.js",
"plugin/build",
"*.podspec",
"!ios/build",
"!android/build",
Expand All @@ -31,8 +34,10 @@
"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",
"prepare": "bob build",
"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 && yarn build:plugin",
"pack": "del-cli azesmway-react-native-unity-1.0.11.tgz && npm pack",
"release": "release-it"
},
"keywords": [
Expand Down
103 changes: 86 additions & 17 deletions plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import type { ConfigPlugin } from '@expo/config-plugins';
import {
AndroidConfig,
withDangerousMod,
withGradleProperties,
withProjectBuildGradle,
withSettingsGradle,
withStringsXml,
} 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<{}> = (config, {} = {}) => {
config = withProjectBuildGradleMod(config);
config = withSettingsGradleMod(config);
config = withGradlePropertiesMod(config);
config = withStringsXMLMod(config);
config = withIosFabricRegistration(config);
return config;
};

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,
Expand All @@ -40,27 +43,34 @@ 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;
});

// add string
const withStringsXMLMod: ConfigPlugin = (config) =>
withStringsXml(config, (config) => {
config.modResults = AndroidConfig.Strings.setStringItem(
withStringsXml(config, (modConfig) => {
modConfig.modResults = AndroidConfig.Strings.setStringItem(
[
{
_: 'Game View',
Expand All @@ -69,9 +79,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/<AppName>/
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<RCTComponentViewProtocol> 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;
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down