) aApi
8 | {
9 | api = aApi;
10 | }
11 |
12 | @end
13 |
14 | extern "C"
15 | {
16 | void sendMessageToMobileApp(const char* message)
17 | {
18 | return [api sendMessageToMobileApp:[NSString stringWithUTF8String:message]];
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native'
2 | import { createNativeStackNavigator } from '@react-navigation/native-stack'
3 | import React from 'react'
4 |
5 | import Main from './Main'
6 | import Unity from './Unity'
7 |
8 | const Stack = createNativeStackNavigator()
9 |
10 | export default function App() {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset', '@babel/preset-typescript'],
3 | env: {
4 | production: {}
5 | },
6 | plugins: [
7 | 'module:react-native-dotenv',
8 | '@babel/plugin-proposal-export-namespace-from',
9 | [
10 | 'module-resolver',
11 | {
12 | root: ['./src'],
13 | alias: { '^~(.+)': './src/\\1' },
14 | extensions: ['.ios.js', '.android.js', '.ios.ts', '.android.ts', '.ios.tsx', '.android.tsx', '.js', '.ts', '.tsx', '.json']
15 | }
16 | ],
17 | 'react-native-reanimated/plugin'
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/example/src/Main.tsx:
--------------------------------------------------------------------------------
1 | import { StackActions, useNavigation } from '@react-navigation/native'
2 | import * as React from 'react'
3 | import { Button, Text, View } from 'react-native'
4 |
5 | const Main = () => {
6 | const navigation = useNavigation()
7 |
8 | return (
9 |
10 | Unity Screen
11 |
20 | )
21 | }
22 |
23 | export default Main
24 |
--------------------------------------------------------------------------------
/example/android/app/src/release/java/com/example/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.example;
8 |
9 | import android.content.Context;
10 | import com.facebook.react.ReactInstanceManager;
11 |
12 | /**
13 | * Class responsible of loading Flipper inside your React Native application. This is the release
14 | * flavor of it so it's empty as we don't want to load Flipper.
15 | */
16 | public class ReactNativeFlipper {
17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
18 | // Do nothing as we don't want to initialize Flipper on Release.
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/example/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | clearMocks: true,
3 |
4 | globals: {
5 | 'ts-jest': {
6 | tsconfig: 'tsconfig.spec.json', // as specified by ts-jest
7 | babelConfig: true
8 | }
9 | },
10 |
11 | moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'],
12 |
13 | moduleNameMapper: {
14 | '^.+.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2|webp)$': 'jest-transform-stub'
15 | },
16 |
17 | preset: 'react-native',
18 |
19 | testEnvironment: 'jsdom',
20 |
21 | transform: {
22 | '^.+\\.jsx$': 'babel-jest',
23 | '^.+\\.tsx?$': 'ts-jest',
24 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub'
25 | },
26 |
27 | transformIgnorePatterns: ['/node_modules/(?!(@react-native|react-native)).*/'],
28 |
29 | verbose: true
30 | }
31 |
--------------------------------------------------------------------------------
/example/ios/exampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "pipeline": {
4 | "build:android": {
5 | "inputs": [
6 | "package.json",
7 | "android",
8 | "!android/build",
9 | "src/*.ts",
10 | "src/*.tsx",
11 | "example/package.json",
12 | "example/android",
13 | "!example/android/.gradle",
14 | "!example/android/build",
15 | "!example/android/app/build"
16 | ],
17 | "outputs": []
18 | },
19 | "build:ios": {
20 | "inputs": [
21 | "package.json",
22 | "*.podspec",
23 | "ios",
24 | "src/*.ts",
25 | "src/*.tsx",
26 | "example/package.json",
27 | "example/ios",
28 | "!example/ios/build",
29 | "!example/ios/Pods"
30 | ],
31 | "outputs": []
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "@azesmway/react-native-unity": ["./src/index"]
6 | },
7 | "allowUnreachableCode": false,
8 | "allowUnusedLabels": false,
9 | "esModuleInterop": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "jsx": "react",
12 | "lib": ["esnext"],
13 | "module": "esnext",
14 | "moduleResolution": "node",
15 | "noFallthroughCasesInSwitch": true,
16 | "noImplicitReturns": true,
17 | "noImplicitUseStrict": false,
18 | "noStrictGenericChecks": false,
19 | "noUncheckedIndexedAccess": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "resolveJsonModule": true,
23 | "skipLibCheck": true,
24 | "strict": true,
25 | "target": "esnext",
26 | "verbatimModuleSyntax": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup
2 | description: Setup Node.js and install dependencies
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - name: Setup Node.js
8 | uses: actions/setup-node@v3
9 | with:
10 | node-version-file: .nvmrc
11 |
12 | - name: Cache dependencies
13 | id: yarn-cache
14 | uses: actions/cache@v3
15 | with:
16 | path: |
17 | **/node_modules
18 | .yarn/install-state.gz
19 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
20 | restore-keys: |
21 | ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
22 | ${{ runner.os }}-yarn-
23 |
24 | - name: Install dependencies
25 | if: steps.yarn-cache.outputs.cache-hit != 'true'
26 | run: yarn install --immutable
27 | shell: bash
28 |
--------------------------------------------------------------------------------
/example/ios/example/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 |
5 | @implementation AppDelegate
6 |
7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
8 | {
9 | self.moduleName = @"example";
10 | // You can add your custom initial props in the dictionary below.
11 | // They will be passed down to the ViewController used by React Native.
12 | self.initialProps = @{};
13 |
14 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
15 | }
16 |
17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
18 | {
19 | #if DEBUG
20 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
21 | #else
22 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
23 | #endif
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "33.0.0"
6 | minSdkVersion = 26
7 | compileSdkVersion = 33
8 | targetSdkVersion = 33
9 |
10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
11 | ndkVersion = "23.1.7779620"
12 |
13 | kotlinVersion = "1.8.0"
14 | }
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | dependencies {
20 | classpath("com.android.tools.build:gradle")
21 | classpath("com.facebook.react:react-native-gradle-plugin")
22 | }
23 | }
24 |
25 | allprojects {
26 | repositories {
27 | flatDir {
28 | dirs "${project(':unityLibrary').projectDir}/libs"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/android/src/oldarch/com/azesmwayreactnativeunity/ReactNativeUnityViewManagerSpec.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import android.view.View;
4 |
5 | import androidx.annotation.Nullable;
6 |
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.SimpleViewManager;
9 |
10 | public abstract class ReactNativeUnityViewManagerSpec extends SimpleViewManager {
11 | public abstract void postMessage(T view, String gameObject, String methodName, String message);
12 | public abstract void setAndroidKeepPlayerMounted(T view, boolean value);
13 | public abstract void setFullScreen(T view, boolean value);
14 | public abstract void unloadUnity(T view);
15 | public abstract void pauseUnity(T view, boolean pause);
16 | public abstract void resumeUnity(T view);
17 | public abstract void windowFocusChanged(T view, boolean hasFocus);
18 | }
19 |
--------------------------------------------------------------------------------
/android/src/newarch/com/azesmwayreactnativeunity/ReactNativeUnityViewManagerSpec.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import android.view.View;
4 |
5 | import androidx.annotation.Nullable;
6 |
7 | import com.facebook.react.uimanager.SimpleViewManager;
8 | import com.facebook.react.uimanager.ViewManagerDelegate;
9 | import com.facebook.react.viewmanagers.RNUnityViewManagerDelegate;
10 | import com.facebook.react.viewmanagers.RNUnityViewManagerInterface;
11 |
12 | public abstract class ReactNativeUnityViewManagerSpec extends SimpleViewManager implements RNUnityViewManagerInterface {
13 | private final ViewManagerDelegate mDelegate;
14 |
15 | public ReactNativeUnityViewManagerSpec() {
16 | mDelegate = new RNUnityViewManagerDelegate<>(this);
17 | }
18 |
19 | @Nullable
20 | @Override
21 | protected ViewManagerDelegate getDelegate() {
22 | return mDelegate;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/azesmwayreactnativeunity/ReactNativeUnityViewPackage.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.NativeModule;
5 | import com.facebook.react.bridge.ReactApplicationContext;
6 | import com.facebook.react.uimanager.ViewManager;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | public class ReactNativeUnityViewPackage implements ReactPackage {
13 | @Override
14 | public List createViewManagers(ReactApplicationContext reactContext) {
15 | List viewManagers = new ArrayList<>();
16 | viewManagers.add(new ReactNativeUnityViewManager(reactContext));
17 | return viewManagers;
18 | }
19 |
20 | @Override
21 | public List createNativeModules(ReactApplicationContext reactContext) {
22 | return Collections.emptyList();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/scripts/pod-install.cjs:
--------------------------------------------------------------------------------
1 | const child_process = require('child_process');
2 |
3 | module.exports = {
4 | name: 'pod-install',
5 | factory() {
6 | return {
7 | hooks: {
8 | afterAllInstalled(project, options) {
9 | if (process.env.POD_INSTALL === '0') {
10 | return;
11 | }
12 |
13 | if (
14 | options &&
15 | (options.mode === 'update-lockfile' ||
16 | options.mode === 'skip-build')
17 | ) {
18 | return;
19 | }
20 |
21 | const result = child_process.spawnSync(
22 | 'yarn',
23 | ['pod-install', 'example/ios'],
24 | {
25 | cwd: project.cwd,
26 | env: process.env,
27 | stdio: 'inherit',
28 | encoding: 'utf-8',
29 | shell: true,
30 | }
31 | );
32 |
33 | if (result.status !== 0) {
34 | throw new Error('Failed to run pod-install');
35 | }
36 | },
37 | },
38 | };
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/unity/Assets/ButtonBehavior.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Runtime.InteropServices;
5 | using UnityEngine.UI;
6 | using UnityEngine;
7 |
8 | public class NativeAPI {
9 | #if UNITY_IOS && !UNITY_EDITOR
10 | [DllImport("__Internal")]
11 | public static extern void sendMessageToMobileApp(string message);
12 | #endif
13 | }
14 |
15 | public class ButtonBehavior : MonoBehaviour
16 | {
17 | public void ButtonPressed()
18 | {
19 | if (Application.platform == RuntimePlatform.Android)
20 | {
21 | using (AndroidJavaClass jc = new AndroidJavaClass("com.azesmwayreactnativeunity.ReactNativeUnityViewManager"))
22 | {
23 | jc.CallStatic("sendMessageToMobileApp", "The button has been tapped!");
24 | }
25 | }
26 | else if (Application.platform == RuntimePlatform.IPhonePlayer)
27 | {
28 | #if UNITY_IOS && !UNITY_EDITOR
29 | NativeAPI.sendMessageToMobileApp("The button has been tapped!");
30 | #endif
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/unity/Assets/ButtonBehavior.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Runtime.InteropServices;
5 | using UnityEngine.UI;
6 | using UnityEngine;
7 |
8 | public class NativeAPI {
9 | #if UNITY_IOS && !UNITY_EDITOR
10 | [DllImport("__Internal")]
11 | public static extern void sendMessageToMobileApp(string message);
12 | #endif
13 | }
14 |
15 | public class ButtonBehavior : MonoBehaviour
16 | {
17 | public void ButtonPressed()
18 | {
19 | if (Application.platform == RuntimePlatform.Android)
20 | {
21 | using (AndroidJavaClass jc = new AndroidJavaClass("com.azesmwayreactnativeunity.ReactNativeUnityViewManager"))
22 | {
23 | jc.CallStatic("sendMessageToMobileApp", "The button has been tapped!");
24 | }
25 | }
26 | else if (Application.platform == RuntimePlatform.IPhonePlayer)
27 | {
28 | #if UNITY_IOS && !UNITY_EDITOR
29 | NativeAPI.sendMessageToMobileApp("The button has been tapped!");
30 | #endif
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Xcode
13 | #
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.hmap
28 | *.ipa
29 | *.xcuserstate
30 | project.xcworkspace
31 |
32 | # Android/IJ
33 | #
34 | .classpath
35 | .cxx
36 | .gradle
37 | .idea
38 | .project
39 | .settings
40 | local.properties
41 | android.iml
42 |
43 | # Cocoapods
44 | #
45 | example/ios/Pods
46 |
47 | # Ruby
48 | example/vendor/
49 |
50 | # node.js
51 | #
52 | node_modules/
53 | npm-debug.log
54 | yarn-debug.log
55 | yarn-error.log
56 |
57 | # BUCK
58 | buck-out/
59 | \.buckd/
60 | android/app/libs
61 | android/keystores/debug.keystore
62 |
63 | # Yarn
64 | .yarn/*
65 | !.yarn/patches
66 | !.yarn/plugins
67 | !.yarn/releases
68 | !.yarn/sdks
69 | !.yarn/versions
70 |
71 | # Expo
72 | .expo/
73 |
74 | # Turborepo
75 | .turbo/
76 |
77 | # generated by bob
78 | lib/
79 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 azesm
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "scale" : "1x",
46 | "size" : "1024x1024"
47 | }
48 | ],
49 | "info" : {
50 | "author" : "xcode",
51 | "version" : 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate;
7 |
8 | public class MainActivity extends ReactActivity {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | @Override
15 | protected String getMainComponentName() {
16 | return "example";
17 | }
18 |
19 | /**
20 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
21 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
22 | * (aka React 18) with two boolean flags.
23 | */
24 | @Override
25 | protected ReactActivityDelegate createReactActivityDelegate() {
26 | return new DefaultReactActivityDelegate(
27 | this,
28 | getMainComponentName(),
29 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
30 | DefaultNewArchitectureEntryPoint.getFabricEnabled());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | ios/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 | yarn-error.log
42 |
43 | # fastlane
44 | #
45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
46 | # screenshots whenever they are needed.
47 | # For more information about the recommended setup visit:
48 | # https://docs.fastlane.tools/best-practices/source-control/
49 |
50 | **/fastlane/report.xml
51 | **/fastlane/Preview.html
52 | **/fastlane/screenshots
53 | **/fastlane/test_output
54 |
55 | # Bundle artifact
56 | *.jsbundle
57 |
58 | # Ruby / CocoaPods
59 | /ios/Pods/
60 | /vendor/bundle/
61 |
62 | # Temporary files created by Metro to check the health of the file watcher
63 | .metro-health-check*
64 |
65 | # testing
66 | /coverage
67 |
--------------------------------------------------------------------------------
/example/src/Unity.tsx:
--------------------------------------------------------------------------------
1 | import UnityView from '@azesmway/react-native-unity'
2 | import React, { useEffect, useRef } from 'react'
3 | import { View } from 'react-native'
4 |
5 | interface IMessage {
6 | gameObject: string
7 | methodName: string
8 | message: string
9 | }
10 |
11 | const Unity = () => {
12 | const unityRef = useRef()
13 | const message: IMessage = {
14 | gameObject: '[Scripts]',
15 | methodName: 'InitModule',
16 | message: '{"scene": "GeoPoints"}'
17 | }
18 |
19 | useEffect(() => {
20 | setTimeout(() => {
21 | if (unityRef && unityRef.current) {
22 | // @ts-ignore
23 | unityRef.current.postMessage(message.gameObject, message.methodName, message.message)
24 | }
25 | }, 6000)
26 |
27 | return () => console.log('unmount')
28 | }, [])
29 |
30 | return (
31 | // If you wrap your UnityView inside a parent, please take care to set dimensions to it (with `flex:1` for example).
32 | // See the `Know issues` part in the README.
33 |
34 | console.log('onUnityMessage ===> ', result.nativeEvent.message)}
39 | />
40 |
41 | )
42 | }
43 |
44 | export default Unity
45 |
--------------------------------------------------------------------------------
/example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | ignorePatterns: ['**/*.d.ts'],
4 | extends: ['@react-native', 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:ft-flow/recommended'],
5 | parser: '@typescript-eslint/parser',
6 | plugins: ['@typescript-eslint', 'simple-import-sort', 'jest', 'ft-flow'],
7 | overrides: [
8 | {
9 | files: ['*.ts', '*.tsx'],
10 | rules: {
11 | '@typescript-eslint/no-shadow': ['error'],
12 | 'no-shadow': 'off',
13 | 'no-undef': 'off'
14 | }
15 | }
16 | ],
17 | rules: {
18 | eqeqeq: 'off',
19 | 'prettier/prettier': 'error',
20 | 'comma-dangle': ['error', 'never'],
21 | semi: ['error', 'never'],
22 | 'max-len': [
23 | 'error',
24 | {
25 | code: 200
26 | }
27 | ],
28 | 'react-hooks/exhaustive-deps': 'off',
29 | 'simple-import-sort/exports': 'error',
30 | 'simple-import-sort/imports': 'error',
31 | 'no-unused-vars': 'off',
32 | '@typescript-eslint/no-unused-vars': 'off',
33 | '@typescript-eslint/ban-ts-ignore': 'off',
34 | 'no-unsafe-optional-chaining': 'off',
35 | 'ft-flow/boolean-style': [2, 'boolean']
36 | },
37 | env: {
38 | node: true,
39 | 'jest/globals': true
40 | },
41 | settings: {
42 | 'ft-flow': {
43 | onlyFilesWithFlowAnnotation: false
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/specs/UnityViewNativeComponent.ts:
--------------------------------------------------------------------------------
1 | import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
2 | import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
3 | import type { HostComponent, ViewProps } from 'react-native';
4 | import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
5 |
6 | export type UnityViewContentUpdateEvent = Readonly<{
7 | message: string;
8 | }>;
9 |
10 | export interface NativeProps extends ViewProps {
11 | androidKeepPlayerMounted?: boolean;
12 | fullScreen?: boolean;
13 | onUnityMessage?: DirectEventHandler;
14 | onPlayerUnload?: DirectEventHandler;
15 | onPlayerQuit?: DirectEventHandler;
16 | }
17 |
18 | export interface NativeCommands {
19 | postMessage: (
20 | viewRef: React.ElementRef>,
21 | gameObject: string,
22 | methodName: string,
23 | message: string
24 | ) => void;
25 | unloadUnity: (viewRef: React.ElementRef>) => void;
26 | pauseUnity: (
27 | viewRef: React.ElementRef>,
28 | pause: boolean
29 | ) => void;
30 | resumeUnity: (viewRef: React.ElementRef>) => void;
31 | windowFocusChanged: (
32 | viewRef: React.ElementRef>,
33 | hasFocus: boolean
34 | ) => void;
35 | }
36 |
37 | export const Commands: NativeCommands = codegenNativeCommands({
38 | supportedCommands: [
39 | 'postMessage',
40 | 'unloadUnity',
41 | 'pauseUnity',
42 | 'resumeUnity',
43 | 'windowFocusChanged',
44 | ],
45 | });
46 |
47 | export default codegenNativeComponent(
48 | 'RNUnityView'
49 | ) as HostComponent;
50 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.182.0
29 |
30 | # Use this property to specify which architecture you want to build.
31 | # You can also override it from the CLI using
32 | # ./gradlew -PreactNativeArchitectures=x86_64
33 | reactNativeArchitectures=arm64-v8a
34 |
35 | # Use this property to enable support to the new architecture.
36 | # This will allow you to use TurboModules and the Fabric render in
37 | # your application. You should enable this flag either if you want
38 | # to write custom TurboModules/Fabric components OR use libraries that
39 | # are providing them.
40 | newArchEnabled=true
41 |
42 | # Use this property to enable or disable the Hermes JS engine.
43 | # If set to false, you will be using JSC instead.
44 | hermesEnabled=true
45 |
46 | unityStreamingAssets=.unity3d
47 |
--------------------------------------------------------------------------------
/android/src/main/java/com/azesmwayreactnativeunity/ReactNativeUnityView.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import static com.azesmwayreactnativeunity.ReactNativeUnity.*;
4 |
5 | import android.content.Context;
6 |
7 | import android.annotation.SuppressLint;
8 | import android.content.res.Configuration;
9 | import android.widget.FrameLayout;
10 |
11 | import java.lang.reflect.InvocationTargetException;
12 |
13 | @SuppressLint("ViewConstructor")
14 | public class ReactNativeUnityView extends FrameLayout {
15 | private UPlayer view;
16 | public boolean keepPlayerMounted = false;
17 |
18 | public ReactNativeUnityView(Context context) {
19 | super(context);
20 | }
21 |
22 | public void setUnityPlayer(UPlayer player) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
23 | this.view = player;
24 | addUnityViewToGroup(this);
25 | }
26 |
27 | @Override
28 | public void onWindowFocusChanged(boolean hasWindowFocus) {
29 | super.onWindowFocusChanged(hasWindowFocus);
30 |
31 | if (view == null) {
32 | return;
33 | }
34 |
35 | view.windowFocusChanged(hasWindowFocus);
36 |
37 | if (!keepPlayerMounted || !_isUnityReady) {
38 | return;
39 | }
40 |
41 | // pause Unity on blur, resume on focus
42 | if (hasWindowFocus && _isUnityPaused) {
43 | // view.requestFocus();
44 | view.resume();
45 | } else if (!hasWindowFocus && !_isUnityPaused) {
46 | view.pause();
47 | }
48 | }
49 |
50 | @Override
51 | protected void onConfigurationChanged(Configuration newConfig) {
52 | super.onConfigurationChanged(newConfig);
53 |
54 | if (view != null) {
55 | view.configurationChanged(newConfig);
56 | }
57 | }
58 |
59 | @Override
60 | protected void onDetachedFromWindow() {
61 | if (!this.keepPlayerMounted) {
62 | try {
63 | addUnityViewToBackground();
64 | } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
65 | throw new RuntimeException(e);
66 | }
67 | }
68 |
69 | super.onDetachedFromWindow();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/UnityView.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import NativeUnityView, { Commands } from './specs/UnityViewNativeComponent';
4 | import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
5 | import { Platform } from 'react-native';
6 |
7 | type UnityViewContentUpdateEvent = Readonly<{
8 | message: string;
9 | }>;
10 |
11 | type RNUnityViewProps = {
12 | androidKeepPlayerMounted?: boolean;
13 | fullScreen?: boolean;
14 | onUnityMessage?: DirectEventHandler;
15 | onPlayerUnload?: DirectEventHandler;
16 | onPlayerQuit?: DirectEventHandler;
17 | };
18 |
19 | type ComponentRef = InstanceType;
20 |
21 | export default class UnityView extends React.Component {
22 | ref = React.createRef();
23 |
24 | public postMessage = (
25 | gameObject: string,
26 | methodName: string,
27 | message: string
28 | ) => {
29 | if (this.ref.current) {
30 | Commands.postMessage(this.ref.current, gameObject, methodName, message);
31 | }
32 | };
33 |
34 | public unloadUnity = () => {
35 | if (this.ref.current) {
36 | Commands.unloadUnity(this.ref.current);
37 | }
38 | };
39 |
40 | public pauseUnity(pause: boolean) {
41 | if (this.ref.current) {
42 | Commands.pauseUnity(this.ref.current, pause);
43 | }
44 | }
45 |
46 | public resumeUnity() {
47 | if (this.ref.current) {
48 | Commands.resumeUnity(this.ref.current);
49 | }
50 | }
51 |
52 | public windowFocusChanged(hasFocus = true) {
53 | if (Platform.OS !== 'android') return;
54 |
55 | if (this.ref.current) {
56 | Commands.windowFocusChanged(this.ref.current, hasFocus);
57 | }
58 | }
59 |
60 | private getProps() {
61 | return {
62 | ...this.props,
63 | };
64 | }
65 |
66 | componentWillUnmount() {
67 | if (this.ref.current) {
68 | Commands.unloadUnity(this.ref.current);
69 | }
70 | }
71 |
72 | render() {
73 | return ;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/react-native-unity.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 | folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
5 |
6 | Pod::Spec.new do |s|
7 | s.name = "react-native-unity"
8 | s.version = package["version"]
9 | s.summary = package["description"]
10 | s.homepage = package["homepage"]
11 | s.license = package["license"]
12 | s.authors = package["author"]
13 |
14 | s.platforms = { :ios => "12.4" }
15 | s.source = { :git => "https://github.com/azesmway/react-native-unity.git", :tag => "#{s.version}" }
16 |
17 | s.source_files = "ios/**/*.{h,m,mm}"
18 |
19 | # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
20 | # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
21 | if respond_to?(:install_modules_dependencies, true)
22 | install_modules_dependencies(s)
23 | else
24 | s.dependency "React-Core"
25 |
26 | # Don't install the dependencies when we run `pod install` in the old architecture.
27 | if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
28 | s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
29 | s.pod_target_xcconfig = {
30 | "DEFINES_MODULE" => "YES",
31 | "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
32 | "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
33 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
34 | }
35 | s.dependency "React-RCTFabric"
36 | s.dependency "React-Codegen"
37 | s.dependency "RCT-Folly"
38 | s.dependency "RCTRequired"
39 | s.dependency "RCTTypeSafety"
40 | s.dependency "ReactCommon/turbomodule/core"
41 | end
42 | end
43 |
44 | # Copy the framework to the plugin folder so that xcode can install it
45 | # The framework should be placed in the /unity/builds/ios folder.
46 | s.prepare_command =
47 | <<-CMD
48 | cp -R ../../../unity/builds/ios/ ios/
49 | CMD
50 |
51 | s.vendored_frameworks = ["ios/UnityFramework.framework"]
52 | end
53 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import android.app.Application;
4 | import com.facebook.react.PackageList;
5 | import com.facebook.react.ReactApplication;
6 | import com.facebook.react.ReactNativeHost;
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
9 | import com.facebook.react.defaults.DefaultReactNativeHost;
10 | import com.facebook.soloader.SoLoader;
11 | import java.util.List;
12 |
13 | public class MainApplication extends Application implements ReactApplication {
14 |
15 | private final ReactNativeHost mReactNativeHost =
16 | new DefaultReactNativeHost(this) {
17 | @Override
18 | public boolean getUseDeveloperSupport() {
19 | return BuildConfig.DEBUG;
20 | }
21 |
22 | @Override
23 | protected List getPackages() {
24 | @SuppressWarnings("UnnecessaryLocalVariable")
25 | List packages = new PackageList(this).getPackages();
26 | // Packages that cannot be autolinked yet can be added manually here, for example:
27 | // packages.add(new MyReactNativePackage());
28 | return packages;
29 | }
30 |
31 | @Override
32 | protected String getJSMainModuleName() {
33 | return "index";
34 | }
35 |
36 | @Override
37 | protected boolean isNewArchEnabled() {
38 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
39 | }
40 |
41 | @Override
42 | protected Boolean isHermesEnabled() {
43 | return BuildConfig.IS_HERMES_ENABLED;
44 | }
45 | };
46 |
47 | @Override
48 | public ReactNativeHost getReactNativeHost() {
49 | return mReactNativeHost;
50 | }
51 |
52 | @Override
53 | public void onCreate() {
54 | super.onCreate();
55 | SoLoader.init(this, /* native exopackage */ false);
56 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
57 | // If you opted-in for the New Architecture, we load the native entry point for this app.
58 | DefaultNewArchitectureEntryPoint.load();
59 | }
60 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/example/ios/exampleTests/exampleTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface exampleTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation exampleTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
12 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
13 | #
14 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
15 | # ```js
16 | # module.exports = {
17 | # dependencies: {
18 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
19 | # ```
20 | flipper_config = FlipperConfiguration.disabled
21 |
22 | linkage = ENV['USE_FRAMEWORKS']
23 | if linkage != nil
24 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
25 | use_frameworks! :linkage => linkage.to_sym
26 | end
27 |
28 | target 'example' do
29 | config = use_native_modules!
30 |
31 | # Flags change depending on the env values.
32 | flags = get_default_flags()
33 |
34 | use_react_native!(
35 | :path => config[:reactNativePath],
36 | # Hermes is now enabled by default. Disable by setting this flag to false.
37 | :hermes_enabled => flags[:hermes_enabled],
38 | :fabric_enabled => flags[:fabric_enabled],
39 | # Enables Flipper.
40 | #
41 | # Note that if you have use_frameworks! enabled, Flipper will not work and
42 | # you should disable the next line.
43 | :flipper_configuration => flipper_config,
44 | # An absolute path to your application root.
45 | :app_path => "#{Pod::Config.instance.installation_root}/.."
46 | )
47 |
48 | target 'exampleTests' do
49 | inherit! :complete
50 | # Pods for testing
51 | end
52 |
53 | post_install do |installer|
54 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
55 | react_native_post_install(
56 | installer,
57 | config[:reactNativePath],
58 | :mac_catalyst_enabled => false
59 | )
60 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/ios/RNUnityView.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #include
4 | #include
5 |
6 | // This guard prevent this file to be compiled in the old architecture.
7 | #ifdef RCT_NEW_ARCH_ENABLED
8 | #import
9 | #import
10 |
11 | #import
12 | #import
13 | #import
14 | #import
15 |
16 | #import "RCTFabricComponentsPlugins.h"
17 |
18 | #ifndef UnityViewNativeComponent_h
19 | #define UnityViewNativeComponent_h
20 |
21 | NS_ASSUME_NONNULL_BEGIN
22 |
23 | @interface RNUnityView : RCTViewComponentView
24 |
25 | @property UnityFramework* ufw;
26 |
27 | @property (nonatomic, strong) UIView* _Nullable uView;
28 |
29 | @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onUnityMessage;
30 | @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onPlayerUnload;
31 | @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onPlayerQuit;
32 |
33 | - (void)unloadUnity;
34 | - (void)pauseUnity:(BOOL * _Nonnull)pause;
35 | - (void)postMessage:(NSString* _Nonnull )gameObject methodName:(NSString* _Nonnull)methodName message:(NSString* _Nonnull) message;
36 |
37 | @end
38 |
39 | NS_ASSUME_NONNULL_END
40 |
41 | #endif /* UnityViewNativeComponent_h */
42 | #else
43 |
44 | #import
45 |
46 | @interface RNUnityView : RCTView
47 |
48 | @property UnityFramework* ufw;
49 |
50 | @property (nonatomic, strong) UIView* _Nullable uView;
51 |
52 | @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onUnityMessage;
53 | @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onPlayerUnload;
54 | @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onPlayerQuit;
55 |
56 | - (void)unloadUnity;
57 | - (void)pauseUnity:(BOOL * _Nonnull)pause;
58 | - (void)postMessage:(NSString* _Nonnull )gameObject methodName:(NSString* _Nonnull)methodName message:(NSString* _Nonnull) message;
59 |
60 |
61 | @end
62 |
63 | #endif /* RCT_NEW_ARCH_ENABLED */
64 |
--------------------------------------------------------------------------------
/plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AndroidConfig,
3 | withGradleProperties,
4 | withProjectBuildGradle,
5 | withSettingsGradle,
6 | withStringsXml,
7 | } from '@expo/config-plugins';
8 | import type { ConfigPlugin } from '@expo/config-plugins';
9 |
10 | const withUnity: ConfigPlugin<{ name?: string }> = (
11 | config,
12 | { name = 'react-native-unity' } = {}
13 | ) => {
14 | config.name = name;
15 | config = withProjectBuildGradleMod(config);
16 | config = withSettingsGradleMod(config);
17 | config = withGradlePropertiesMod(config);
18 | config = withStringsXMLMod(config);
19 | return config;
20 | };
21 |
22 | const REPOSITORIES_END_LINE = `maven { url 'https://www.jitpack.io' }`;
23 |
24 | const withProjectBuildGradleMod: ConfigPlugin = (config) =>
25 | withProjectBuildGradle(config, (modConfig) => {
26 | if (modConfig.modResults.contents.includes(REPOSITORIES_END_LINE)) {
27 | // use the last known line in expo's build.gradle file to append the newline after
28 | modConfig.modResults.contents = modConfig.modResults.contents.replace(
29 | REPOSITORIES_END_LINE,
30 | REPOSITORIES_END_LINE +
31 | '\nflatDir { dirs "${project(\':unityLibrary\').projectDir}/libs" }\n'
32 | );
33 | } else {
34 | throw new Error(
35 | 'Failed to find the end of repositories in the android/build.gradle file`'
36 | );
37 | }
38 | return modConfig;
39 | });
40 |
41 | const withSettingsGradleMod: ConfigPlugin = (config) =>
42 | withSettingsGradle(config, (modConfig) => {
43 | modConfig.modResults.contents += `
44 | include ':unityLibrary'
45 | project(':unityLibrary').projectDir=new File('../unity/builds/android/unityLibrary')
46 | `;
47 | return modConfig;
48 | });
49 |
50 | const withGradlePropertiesMod: ConfigPlugin = (config) =>
51 | withGradleProperties(config, (modConfig) => {
52 | modConfig.modResults.push({
53 | type: 'property',
54 | key: 'unityStreamingAssets',
55 | value: '.unity3d',
56 | });
57 | return modConfig;
58 | });
59 |
60 | // add string
61 | const withStringsXMLMod: ConfigPlugin = (config) =>
62 | withStringsXml(config, (config) => {
63 | config.modResults = AndroidConfig.Strings.setStringItem(
64 | [
65 | {
66 | _: 'Game View',
67 | $: {
68 | name: 'game_view_content_description',
69 | },
70 | },
71 | ],
72 | config.modResults
73 | );
74 | return config;
75 | });
76 |
77 | export default withUnity;
78 |
--------------------------------------------------------------------------------
/example/ios/example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationAlwaysAndWhenInUseUsageDescription
39 | Приложение будет использовать локацию для прокладки маршрутов для туров.
40 | NSLocationAlwaysUsageDescription
41 | Приложение будет использовать локацию для прокладки маршрутов для туров.
42 | NSLocationTemporaryUsageDescriptionDictionary
43 |
44 | wantAccurateLocation
45 | Приложение будет использовать локацию для прокладки маршрутов для туров.
46 |
47 | NSLocationWhenInUseUsageDescription
48 | Приложение будет использовать локацию для прокладки маршрутов для туров.
49 | UILaunchStoryboardName
50 | LaunchScreen
51 | UIRequiredDeviceCapabilities
52 |
53 | armv7
54 |
55 | UISupportedInterfaceOrientations
56 |
57 | UIInterfaceOrientationPortrait
58 | UIInterfaceOrientationLandscapeLeft
59 | UIInterfaceOrientationLandscapeRight
60 |
61 | NSCameraUsageDescription
62 | sdfsdfbd jsgdjfg sjhfd sjhdf hsjg df
63 | UIViewControllerBasedStatusBarAppearance
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | executors:
4 | default:
5 | docker:
6 | - image: circleci/node:10
7 | working_directory: ~/project
8 |
9 | commands:
10 | attach_project:
11 | steps:
12 | - attach_workspace:
13 | at: ~/project
14 |
15 | jobs:
16 | install-dependencies:
17 | executor: default
18 | steps:
19 | - checkout
20 | - attach_project
21 | - restore_cache:
22 | keys:
23 | - dependencies-{{ checksum "package.json" }}
24 | - dependencies-
25 | - restore_cache:
26 | keys:
27 | - dependencies-example-{{ checksum "example/package.json" }}
28 | - dependencies-example-
29 | - run:
30 | name: Install dependencies
31 | command: |
32 | yarn install --cwd example --frozen-lockfile
33 | yarn install --frozen-lockfile
34 | - save_cache:
35 | key: dependencies-{{ checksum "package.json" }}
36 | paths: node_modules
37 | - save_cache:
38 | key: dependencies-example-{{ checksum "example/package.json" }}
39 | paths: example/node_modules
40 | - persist_to_workspace:
41 | root: .
42 | paths: .
43 |
44 | lint:
45 | executor: default
46 | steps:
47 | - attach_project
48 | - run:
49 | name: Lint files
50 | command: |
51 | yarn lint
52 |
53 | typescript:
54 | executor: default
55 | steps:
56 | - attach_project
57 | - run:
58 | name: Typecheck files
59 | command: |
60 | yarn typescript
61 |
62 | unit-tests:
63 | executor: default
64 | steps:
65 | - attach_project
66 | - run:
67 | name: Run unit tests
68 | command: |
69 | yarn test --coverage
70 | - store_artifacts:
71 | path: coverage
72 | destination: coverage
73 |
74 | build-package:
75 | executor: default
76 | steps:
77 | - attach_project
78 | - run:
79 | name: Build package
80 | command: |
81 | yarn prepare
82 |
83 | workflows:
84 | build-and-test:
85 | jobs:
86 | - install-dependencies
87 | - lint:
88 | requires:
89 | - install-dependencies
90 | - typescript:
91 | requires:
92 | - install-dependencies
93 | - unit-tests:
94 | requires:
95 | - install-dependencies
96 | - build-package:
97 | requires:
98 | - install-dependencies
99 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "lint": "eslint .",
9 | "start": "react-native start",
10 | "test": "jest"
11 | },
12 | "dependencies": {
13 | "@azesmway/react-native-unity": "^0.4.0",
14 | "@react-navigation/native": "^6.1.9",
15 | "@react-navigation/native-stack": "^6.9.17",
16 | "react": "18.2.0",
17 | "react-native": "0.72.7",
18 | "react-native-reanimated": "^3.5.4",
19 | "react-native-safe-area-context": "^4.7.4",
20 | "react-native-screens": "^3.27.0"
21 | },
22 | "devDependencies": {
23 | "@babel/core": "^7.23.3",
24 | "@babel/plugin-proposal-class-properties": "^7.18.6",
25 | "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
26 | "@babel/plugin-proposal-private-methods": "^7.18.6",
27 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
28 | "@babel/plugin-syntax-jsx": "^7.23.3",
29 | "@babel/plugin-transform-flow-strip-types": "^7.23.3",
30 | "@babel/preset-react": "^7.23.3",
31 | "@babel/preset-typescript": "^7.23.3",
32 | "@babel/runtime": "^7.23.2",
33 | "@react-native/eslint-config": "^0.74.0",
34 | "@react-native/metro-config": "^0.74.0",
35 | "@tsconfig/react-native": "^3.0.2",
36 | "@types/i18n-js": "^3.8.8",
37 | "@types/jest": "^29.5.8",
38 | "@types/lodash": "^4.14.201",
39 | "@types/metro-config": "^0.76.3",
40 | "@types/react": "^18.2.37",
41 | "@types/react-native": "^0.72.6",
42 | "@types/react-native-actionsheet": "^2.4.6",
43 | "@types/react-test-renderer": "^18.0.6",
44 | "@typescript-eslint/eslint-plugin": "^6.10.0",
45 | "@typescript-eslint/parser": "^6.10.0",
46 | "babel-jest": "^29.7.0",
47 | "babel-plugin-module-resolver": "^5.0.0",
48 | "babel-plugin-transform-remove-console": "^6.9.4",
49 | "eslint": "8.53.0",
50 | "eslint-plugin-ft-flow": "^3.0.1",
51 | "eslint-plugin-jest": "^27.6.0",
52 | "eslint-plugin-prettier": "5.0.1",
53 | "eslint-plugin-simple-import-sort": "^10.0.0",
54 | "husky": "^8.0.3",
55 | "jest": "^29.7.0",
56 | "jest-environment-jsdom": "^29.7.0",
57 | "jest-transform-stub": "^2.0.0",
58 | "metro-react-native-babel-preset": "^0.77.0",
59 | "npm-check-updates": "^16.14.6",
60 | "patch-package": "^8.0.0",
61 | "prettier": "^3.0.3",
62 | "react-native-dotenv": "^3.4.9",
63 | "react-test-renderer": "18.2.0",
64 | "ts-interface-builder": "^0.3.3",
65 | "ts-jest": "^29.1.1",
66 | "typescript": "^5.2.2"
67 | },
68 | "engines": {
69 | "node": ">=16"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/example/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.6)
5 | rexml
6 | activesupport (7.0.8)
7 | concurrent-ruby (~> 1.0, >= 1.0.2)
8 | i18n (>= 1.6, < 2)
9 | minitest (>= 5.1)
10 | tzinfo (~> 2.0)
11 | addressable (2.8.5)
12 | public_suffix (>= 2.0.2, < 6.0)
13 | algoliasearch (1.27.5)
14 | httpclient (~> 2.8, >= 2.8.3)
15 | json (>= 1.5.1)
16 | atomos (0.1.3)
17 | claide (1.1.0)
18 | cocoapods (1.14.2)
19 | addressable (~> 2.8)
20 | claide (>= 1.0.2, < 2.0)
21 | cocoapods-core (= 1.14.2)
22 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
23 | cocoapods-downloader (>= 2.0)
24 | cocoapods-plugins (>= 1.0.0, < 2.0)
25 | cocoapods-search (>= 1.0.0, < 2.0)
26 | cocoapods-trunk (>= 1.6.0, < 2.0)
27 | cocoapods-try (>= 1.1.0, < 2.0)
28 | colored2 (~> 3.1)
29 | escape (~> 0.0.4)
30 | fourflusher (>= 2.3.0, < 3.0)
31 | gh_inspector (~> 1.0)
32 | molinillo (~> 0.8.0)
33 | nap (~> 1.0)
34 | ruby-macho (>= 2.3.0, < 3.0)
35 | xcodeproj (>= 1.23.0, < 2.0)
36 | cocoapods-core (1.14.2)
37 | activesupport (>= 5.0, < 8)
38 | addressable (~> 2.8)
39 | algoliasearch (~> 1.0)
40 | concurrent-ruby (~> 1.1)
41 | fuzzy_match (~> 2.0.4)
42 | nap (~> 1.0)
43 | netrc (~> 0.11)
44 | public_suffix (~> 4.0)
45 | typhoeus (~> 1.0)
46 | cocoapods-deintegrate (1.0.5)
47 | cocoapods-downloader (2.0)
48 | cocoapods-plugins (1.0.0)
49 | nap
50 | cocoapods-search (1.0.1)
51 | cocoapods-trunk (1.6.0)
52 | nap (>= 0.8, < 2.0)
53 | netrc (~> 0.11)
54 | cocoapods-try (1.2.0)
55 | colored2 (3.1.2)
56 | concurrent-ruby (1.2.2)
57 | escape (0.0.4)
58 | ethon (0.16.0)
59 | ffi (>= 1.15.0)
60 | ffi (1.16.3)
61 | fourflusher (2.3.1)
62 | fuzzy_match (2.0.4)
63 | gh_inspector (1.1.3)
64 | httpclient (2.8.3)
65 | i18n (1.14.1)
66 | concurrent-ruby (~> 1.0)
67 | json (2.6.3)
68 | minitest (5.20.0)
69 | molinillo (0.8.0)
70 | nanaimo (0.3.0)
71 | nap (1.1.0)
72 | netrc (0.11.0)
73 | public_suffix (4.0.7)
74 | rexml (3.2.6)
75 | ruby-macho (2.5.1)
76 | typhoeus (1.4.1)
77 | ethon (>= 0.9.0)
78 | tzinfo (2.0.6)
79 | concurrent-ruby (~> 1.0)
80 | xcodeproj (1.23.0)
81 | CFPropertyList (>= 2.3.3, < 4.0)
82 | atomos (~> 0.1.3)
83 | claide (>= 1.0.2, < 2.0)
84 | colored2 (~> 3.1)
85 | nanaimo (~> 0.3.0)
86 | rexml (~> 3.2.4)
87 |
88 | PLATFORMS
89 | ruby
90 |
91 | DEPENDENCIES
92 | activesupport (>= 6.1.7.3, < 7.1.0)
93 | cocoapods (~> 1.13)
94 |
95 | RUBY VERSION
96 | ruby 2.7.7p221
97 |
98 | BUNDLED WITH
99 | 2.1.4
100 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/ios/RNUnityViewManager.mm:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 | #import
5 |
6 | @interface RNUnityViewManager : RCTViewManager
7 | @end
8 |
9 | @implementation RNUnityViewManager
10 |
11 | RCT_EXPORT_MODULE(RNUnityView)
12 | RCT_EXPORT_VIEW_PROPERTY(onUnityMessage, RCTBubblingEventBlock)
13 | RCT_EXPORT_VIEW_PROPERTY(onPlayerUnload, RCTBubblingEventBlock)
14 | RCT_EXPORT_VIEW_PROPERTY(onPlayerQuit, RCTBubblingEventBlock)
15 |
16 | RNUnityView *unity;
17 |
18 | - (UIView *)view {
19 | unity = [[RNUnityView alloc] init];
20 | UIWindow * main = [[[UIApplication sharedApplication] delegate] window];
21 |
22 | if(main != nil) {
23 | [main makeKeyAndVisible];
24 | }
25 |
26 | return unity;
27 | }
28 |
29 | - (dispatch_queue_t)methodQueue {
30 | return dispatch_get_main_queue();
31 | }
32 |
33 | + (BOOL)requiresMainQueueSetup {
34 | return YES;
35 | }
36 |
37 | RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber*) reactTag gameObject:(NSString*_Nonnull) gameObject methodName:(NSString*_Nonnull) methodName message:(NSString*_Nonnull) message) {
38 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
39 | RNUnityView *view = (RNUnityView*) viewRegistry[reactTag];
40 | if (!view || ![view isKindOfClass:[RNUnityView class]]) {
41 | RCTLogError(@"Cannot find NativeView with tag #%@", reactTag);
42 | return;
43 | }
44 | [unity postMessage:(NSString *)gameObject methodName:(NSString *)methodName message:(NSString *)message];
45 | }];
46 | }
47 |
48 | RCT_EXPORT_METHOD(pauseUnity:(nonnull NSNumber*) reactTag pause:(BOOL * _Nonnull)pause) {
49 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
50 | RNUnityView *view = (RNUnityView*) viewRegistry[reactTag];
51 | if (!view || ![view isKindOfClass:[RNUnityView class]]) {
52 | RCTLogError(@"Cannot find NativeView with tag #%@", reactTag);
53 | return;
54 | }
55 | [unity pauseUnity:(BOOL * _Nonnull)pause];
56 | }];
57 | }
58 |
59 | RCT_EXPORT_METHOD(resumeUnity:(nonnull NSNumber*) reactTag) {
60 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
61 | RNUnityView *view = (RNUnityView*) viewRegistry[reactTag];
62 | if (!view || ![view isKindOfClass:[RNUnityView class]]) {
63 | RCTLogError(@"Cannot find NativeView with tag #%@", reactTag);
64 | return;
65 | }
66 | [unity pauseUnity:(BOOL * _Nonnull)false];
67 | }];
68 | }
69 |
70 | RCT_EXPORT_METHOD(unloadUnity:(nonnull NSNumber*) reactTag) {
71 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) {
72 | RNUnityView *view = (RNUnityView*) viewRegistry[reactTag];
73 | if (!view || ![view isKindOfClass:[RNUnityView class]]) {
74 | RCTLogError(@"Cannot find NativeView with tag #%@", reactTag);
75 | return;
76 | }
77 | [unity unloadUnity];
78 | }];
79 | }
80 |
81 | - (NSArray *)supportedEvents {
82 | return @[@"onUnityMessage", @"onPlayerUnload", @"onPlayerQuit"];
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
2 |
3 | # Getting Started
4 |
5 | >**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.
6 |
7 | ## Step 1: Start the Metro Server
8 |
9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.
10 |
11 | To start Metro, run the following command from the _root_ of your React Native project:
12 |
13 | ```bash
14 | # using npm
15 | npm start
16 |
17 | # OR using Yarn
18 | yarn start
19 | ```
20 |
21 | ## Step 2: Start your Application
22 |
23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:
24 |
25 | ### For Android
26 |
27 | ```bash
28 | # using npm
29 | npm run android
30 |
31 | # OR using Yarn
32 | yarn android
33 | ```
34 |
35 | ### For iOS
36 |
37 | ```bash
38 | # using npm
39 | npm run ios
40 |
41 | # OR using Yarn
42 | yarn ios
43 | ```
44 |
45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.
46 |
47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.
48 |
49 | ## Step 3: Modifying your App
50 |
51 | Now that you have successfully run the app, let's modify it.
52 |
53 | 1. Open `App.tsx` in your text editor of choice and edit some lines.
54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes!
55 |
56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes!
57 |
58 | ## Congratulations! :tada:
59 |
60 | You've successfully run and modified your React Native App. :partying_face:
61 |
62 | ### Now what?
63 |
64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).
66 |
67 | # Troubleshooting
68 |
69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
70 |
71 | # Learn More
72 |
73 | To learn more about React Native, take a look at the following resources:
74 |
75 | - [React Native Website](https://reactnative.dev) - learn more about React Native.
76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
80 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 |
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:7.4.2"
9 | }
10 | }
11 |
12 | def isNewArchitectureEnabled() {
13 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
14 | }
15 |
16 | apply plugin: "com.android.library"
17 |
18 | if (isNewArchitectureEnabled()) {
19 | apply plugin: "com.facebook.react"
20 | }
21 |
22 | def getExtOrDefault(name) {
23 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Unity_" + name]
24 | }
25 |
26 | def getExtOrIntegerDefault(name) {
27 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Unity_" + name]).toInteger()
28 | }
29 |
30 | def supportsNamespace() {
31 | def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
32 | def major = parsed[0].toInteger()
33 | def minor = parsed[1].toInteger()
34 |
35 | // Namespace support was added in 7.3.0
36 | return (major == 7 && minor >= 3) || major >= 8
37 | }
38 |
39 | android {
40 | if (supportsNamespace()) {
41 | namespace "com.azesmwayreactnativeunity"
42 |
43 | sourceSets {
44 | main {
45 | manifest.srcFile "src/main/AndroidManifestNew.xml"
46 | }
47 | }
48 | }
49 |
50 | compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
51 |
52 | defaultConfig {
53 | minSdkVersion getExtOrIntegerDefault("minSdkVersion")
54 | targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
55 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
56 |
57 | }
58 |
59 | buildFeatures {
60 | buildConfig true
61 | }
62 |
63 | buildTypes {
64 | release {
65 | minifyEnabled false
66 | }
67 | }
68 |
69 | lintOptions {
70 | disable "GradleCompatible"
71 | }
72 |
73 | compileOptions {
74 | sourceCompatibility JavaVersion.VERSION_1_8
75 | targetCompatibility JavaVersion.VERSION_1_8
76 | }
77 |
78 | sourceSets {
79 | main {
80 | if (isNewArchitectureEnabled()) {
81 | java.srcDirs += [
82 | "src/newarch",
83 | // This is needed to build Kotlin project with NewArch enabled
84 | "${project.buildDir}/generated/source/codegen/java"
85 | ]
86 | } else {
87 | java.srcDirs += ["src/oldarch"]
88 | }
89 | }
90 | }
91 | }
92 |
93 | repositories {
94 | mavenLocal()
95 | maven {
96 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
97 | url("$rootDir/../node_modules/react-native/android")
98 | }
99 | google()
100 | mavenCentral()
101 | jcenter()
102 | }
103 |
104 |
105 | dependencies {
106 | // For < 0.71, this will be from the local maven repo
107 | // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
108 | //noinspection GradleDynamicVersion
109 | implementation "com.facebook.react:react-native:+"
110 | implementation project(':unityLibrary')
111 | implementation files("${project(':unityLibrary').projectDir}/libs/unity-classes.jar")
112 | }
113 |
114 | if (isNewArchitectureEnabled()) {
115 | react {
116 | jsRootDir = file("../src/")
117 | libraryName = "RNUnityView"
118 | codegenJavaPackageName = "com.azesmwayreactnativeunity"
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/example/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.example;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
21 | import com.facebook.react.ReactInstanceEventListener;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | /**
28 | * Class responsible of loading Flipper inside your React Native application. This is the debug
29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup.
30 | */
31 | public class ReactNativeFlipper {
32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
33 | if (FlipperUtils.shouldEnableFlipper(context)) {
34 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
35 |
36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
37 | client.addPlugin(new DatabasesFlipperPlugin(context));
38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
39 | client.addPlugin(CrashReporterPlugin.getInstance());
40 |
41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
42 | NetworkingModule.setCustomClientBuilder(
43 | new NetworkingModule.CustomClientBuilder() {
44 | @Override
45 | public void apply(OkHttpClient.Builder builder) {
46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
47 | }
48 | });
49 | client.addPlugin(networkFlipperPlugin);
50 | client.start();
51 |
52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
53 | // Hence we run if after all native modules have been initialized
54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
55 | if (reactContext == null) {
56 | reactInstanceManager.addReactInstanceEventListener(
57 | new ReactInstanceEventListener() {
58 | @Override
59 | public void onReactContextInitialized(ReactContext reactContext) {
60 | reactInstanceManager.removeReactInstanceEventListener(this);
61 | reactContext.runOnNativeModulesQueueThread(
62 | new Runnable() {
63 | @Override
64 | public void run() {
65 | client.addPlugin(new FrescoFlipperPlugin());
66 | }
67 | });
68 | }
69 | });
70 | } else {
71 | client.addPlugin(new FrescoFlipperPlugin());
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
33 |
36 |
39 |
42 |
45 |
48 |
51 |
52 |
59 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
75 |
76 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import android.app.Activity;
4 | import android.content.res.Configuration;
5 | import android.widget.FrameLayout;
6 |
7 | import com.unity3d.player.*;
8 |
9 | import java.lang.reflect.Constructor;
10 | import java.lang.reflect.InvocationTargetException;
11 | import java.lang.reflect.Method;
12 |
13 | public class UPlayer {
14 | private static UnityPlayer unityPlayer;
15 |
16 | public UPlayer(final Activity activity, final ReactNativeUnity.UnityPlayerCallback callback) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
17 | super();
18 | Class> _player = null;
19 |
20 | try {
21 | _player = Class.forName("com.unity3d.player.UnityPlayerForActivityOrService");
22 | } catch (ClassNotFoundException e) {
23 | _player = Class.forName("com.unity3d.player.UnityPlayer");
24 | }
25 |
26 | Constructor> constructor = _player.getConstructors()[1];
27 | unityPlayer = (UnityPlayer) constructor.newInstance(activity, new IUnityPlayerLifecycleEvents() {
28 | @Override
29 | public void onUnityPlayerUnloaded() {
30 | callback.onUnload();
31 | }
32 |
33 | @Override
34 | public void onUnityPlayerQuitted() {
35 | callback.onQuit();
36 | }
37 | });
38 | }
39 |
40 | public static void UnitySendMessage(String gameObject, String methodName, String message) {
41 | UnityPlayer.UnitySendMessage(gameObject, methodName, message);
42 | }
43 |
44 | public void pause() {
45 | unityPlayer.pause();
46 | }
47 |
48 | public void windowFocusChanged(boolean b) {
49 | unityPlayer.windowFocusChanged(b);
50 | }
51 |
52 | public void resume() {
53 | unityPlayer.resume();
54 | }
55 |
56 | public void unload() {
57 | unityPlayer.unload();
58 | }
59 |
60 | public Object getParentPlayer() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
61 | try {
62 | Method getFrameLayout = unityPlayer.getClass().getMethod("getFrameLayout");
63 | FrameLayout frame = (FrameLayout) this.requestFrame();
64 |
65 | return frame.getParent();
66 | } catch (NoSuchMethodException e) {
67 | Method getParent = unityPlayer.getClass().getMethod("getParent");
68 |
69 | return getParent.invoke(unityPlayer);
70 | }
71 | }
72 |
73 | public void configurationChanged(Configuration newConfig) {
74 | unityPlayer.configurationChanged(newConfig);
75 | }
76 |
77 | public void destroy() {
78 | unityPlayer.destroy();
79 | }
80 |
81 | public void requestFocusPlayer() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
82 | try {
83 | Method getFrameLayout = unityPlayer.getClass().getMethod("getFrameLayout");
84 |
85 | FrameLayout frame = (FrameLayout) this.requestFrame();
86 | frame.requestFocus();
87 | } catch (NoSuchMethodException e) {
88 | Method requestFocus = unityPlayer.getClass().getMethod("requestFocus");
89 |
90 | requestFocus.invoke(unityPlayer);
91 | }
92 | }
93 |
94 | public FrameLayout requestFrame() throws NoSuchMethodException {
95 | try {
96 | Method getFrameLayout = unityPlayer.getClass().getMethod("getFrameLayout");
97 |
98 | return (FrameLayout) getFrameLayout.invoke(unityPlayer);
99 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
100 | return unityPlayer;
101 | }
102 | }
103 |
104 | public void setZ(float v) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
105 | try {
106 | Method setZ = unityPlayer.getClass().getMethod("setZ");
107 |
108 | setZ.invoke(unityPlayer, v);
109 | } catch (NoSuchMethodException e) {}
110 | }
111 |
112 | public Object getContextPlayer() {
113 | return unityPlayer.getContext();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/example/ios/example/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 |
10 | jobs:
11 | lint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup
18 | uses: ./.github/actions/setup
19 |
20 | - name: Lint files
21 | run: yarn lint
22 |
23 | - name: Typecheck files
24 | run: yarn typecheck
25 |
26 | test:
27 | runs-on: ubuntu-latest
28 | steps:
29 | - name: Checkout
30 | uses: actions/checkout@v3
31 |
32 | - name: Setup
33 | uses: ./.github/actions/setup
34 |
35 | - name: Run unit tests
36 | run: yarn test --maxWorkers=2 --coverage
37 |
38 | build-library:
39 | runs-on: ubuntu-latest
40 | steps:
41 | - name: Checkout
42 | uses: actions/checkout@v3
43 |
44 | - name: Setup
45 | uses: ./.github/actions/setup
46 |
47 | - name: Build package
48 | run: yarn prepare
49 |
50 | build-android:
51 | runs-on: ubuntu-latest
52 | env:
53 | TURBO_CACHE_DIR: .turbo/android
54 | steps:
55 | - name: Checkout
56 | uses: actions/checkout@v3
57 |
58 | - name: Setup
59 | uses: ./.github/actions/setup
60 |
61 | - name: Cache turborepo for Android
62 | uses: actions/cache@v3
63 | with:
64 | path: ${{ env.TURBO_CACHE_DIR }}
65 | key: ${{ runner.os }}-turborepo-android-${{ hashFiles('**/yarn.lock') }}
66 | restore-keys: |
67 | ${{ runner.os }}-turborepo-android-
68 |
69 | - name: Check turborepo cache for Android
70 | run: |
71 | TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
72 |
73 | if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
74 | echo "turbo_cache_hit=1" >> $GITHUB_ENV
75 | fi
76 |
77 | - name: Install JDK
78 | if: env.turbo_cache_hit != 1
79 | uses: actions/setup-java@v3
80 | with:
81 | distribution: 'zulu'
82 | java-version: '11'
83 |
84 | - name: Finalize Android SDK
85 | if: env.turbo_cache_hit != 1
86 | run: |
87 | /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"
88 |
89 | - name: Cache Gradle
90 | if: env.turbo_cache_hit != 1
91 | uses: actions/cache@v3
92 | with:
93 | path: |
94 | ~/.gradle/wrapper
95 | ~/.gradle/caches
96 | key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
97 | restore-keys: |
98 | ${{ runner.os }}-gradle-
99 |
100 | - name: Build example for Android
101 | run: |
102 | yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
103 |
104 | build-ios:
105 | runs-on: macos-latest
106 | env:
107 | TURBO_CACHE_DIR: .turbo/ios
108 | steps:
109 | - name: Checkout
110 | uses: actions/checkout@v3
111 |
112 | - name: Setup
113 | uses: ./.github/actions/setup
114 |
115 | - name: Cache turborepo for iOS
116 | uses: actions/cache@v3
117 | with:
118 | path: ${{ env.TURBO_CACHE_DIR }}
119 | key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('**/yarn.lock') }}
120 | restore-keys: |
121 | ${{ runner.os }}-turborepo-ios-
122 |
123 | - name: Check turborepo cache for iOS
124 | run: |
125 | TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
126 |
127 | if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
128 | echo "turbo_cache_hit=1" >> $GITHUB_ENV
129 | fi
130 |
131 | - name: Cache cocoapods
132 | if: env.turbo_cache_hit != 1
133 | id: cocoapods-cache
134 | uses: actions/cache@v3
135 | with:
136 | path: |
137 | **/ios/Pods
138 | key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }}
139 | restore-keys: |
140 | ${{ runner.os }}-cocoapods-
141 |
142 | - name: Install cocoapods
143 | if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
144 | run: |
145 | yarn pod-install example/ios
146 | env:
147 | NO_FLIPPER: 1
148 |
149 | - name: Build example for iOS
150 | run: |
151 | yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
152 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@azesmway/react-native-unity",
3 | "version": "1.0.11",
4 | "description": "React Native Unity",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "types": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "unity",
17 | "*.podspec",
18 | "!ios/build",
19 | "!android/build",
20 | "!android/gradle",
21 | "!android/gradlew",
22 | "!android/gradlew.bat",
23 | "!android/local.properties",
24 | "!**/__tests__",
25 | "!**/__fixtures__",
26 | "!**/__mocks__",
27 | "!**/.*"
28 | ],
29 | "scripts": {
30 | "example": "yarn workspace react-native-unity-example",
31 | "test": "jest",
32 | "typecheck": "tsc --noEmit",
33 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
34 | "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
35 | "prepare": "bob build",
36 | "release": "release-it"
37 | },
38 | "keywords": [
39 | "react-native",
40 | "ios",
41 | "android",
42 | "unity"
43 | ],
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/azesmway/react-native-unity.git"
47 | },
48 | "author": "Alexey Zolotaryov (https://github.com/azesmway)",
49 | "license": "MIT",
50 | "bugs": {
51 | "url": "https://github.com/azesmway/react-native-unity/issues"
52 | },
53 | "homepage": "https://github.com/azesmway/react-native-unity#readme",
54 | "publishConfig": {
55 | "registry": "https://registry.npmjs.org/",
56 | "access": "public"
57 | },
58 | "devDependencies": {
59 | "@commitlint/config-conventional": "^17.0.2",
60 | "@evilmartians/lefthook": "^1.5.0",
61 | "@expo/config-plugins": "^7.9.1",
62 | "@react-native/eslint-config": "^0.72.2",
63 | "@release-it/conventional-changelog": "^5.0.0",
64 | "@types/jest": "^28.1.2",
65 | "@types/react": "~17.0.21",
66 | "@types/react-native": "0.70.0",
67 | "commitlint": "^17.0.2",
68 | "del-cli": "^5.0.0",
69 | "eslint": "^8.4.1",
70 | "eslint-config-prettier": "^8.5.0",
71 | "eslint-plugin-jest": "^28.5.0",
72 | "eslint-plugin-prettier": "^4.0.0",
73 | "expo-module-scripts": "^3.5.1",
74 | "jest": "^29.7.0",
75 | "pod-install": "^0.1.0",
76 | "prettier": "^2.0.5",
77 | "react": "18.2.0",
78 | "react-native": "0.72.7",
79 | "react-native-builder-bob": "^0.20.0",
80 | "release-it": "^15.0.0",
81 | "turbo": "^1.10.7",
82 | "typescript": "^5.0.2"
83 | },
84 | "resolutions": {
85 | "@types/react": "17.0.21"
86 | },
87 | "peerDependencies": {
88 | "react": "*",
89 | "react-native": "*"
90 | },
91 | "workspaces": [
92 | "example"
93 | ],
94 | "packageManager": "yarn@3.6.1",
95 | "engines": {
96 | "node": ">= 18.0.0"
97 | },
98 | "jest": {
99 | "preset": "react-native",
100 | "modulePathIgnorePatterns": [
101 | "/example/node_modules",
102 | "/lib/"
103 | ]
104 | },
105 | "commitlint": {
106 | "extends": [
107 | "@commitlint/config-conventional"
108 | ]
109 | },
110 | "release-it": {
111 | "git": {
112 | "commitMessage": "chore: release ${version}",
113 | "tagName": "v${version}"
114 | },
115 | "npm": {
116 | "publish": true,
117 | "access": "public"
118 | },
119 | "github": {
120 | "release": true
121 | },
122 | "plugins": {
123 | "@release-it/conventional-changelog": {
124 | "preset": "angular"
125 | }
126 | }
127 | },
128 | "eslintConfig": {
129 | "root": true,
130 | "extends": [
131 | "@react-native",
132 | "prettier"
133 | ],
134 | "rules": {
135 | "prettier/prettier": [
136 | "error",
137 | {
138 | "quoteProps": "consistent",
139 | "singleQuote": true,
140 | "tabWidth": 2,
141 | "trailingComma": "es5",
142 | "useTabs": false
143 | }
144 | ]
145 | }
146 | },
147 | "eslintIgnore": [
148 | "node_modules/",
149 | "lib/"
150 | ],
151 | "prettier": {
152 | "quoteProps": "consistent",
153 | "singleQuote": true,
154 | "tabWidth": 2,
155 | "trailingComma": "es5",
156 | "useTabs": false
157 | },
158 | "react-native-builder-bob": {
159 | "source": "src",
160 | "output": "lib",
161 | "targets": [
162 | "commonjs",
163 | "module",
164 | [
165 | "typescript",
166 | {
167 | "project": "tsconfig.build.json"
168 | }
169 | ]
170 | ]
171 | },
172 | "codegenConfig": {
173 | "name": "unityview",
174 | "type": "components",
175 | "jsSrcsDir": "src"
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "com.facebook.react"
3 |
4 | /**
5 | * This is the configuration block to customize your React Native Android app.
6 | * By default you don't need to apply any configuration, just uncomment the lines you need.
7 | */
8 | react {
9 | /* Folders */
10 | // The root of your project, i.e. where "package.json" lives. Default is '..'
11 | // root = file("../")
12 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native
13 | // reactNativeDir = file("../node_modules/react-native")
14 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
15 | // codegenDir = file("../node_modules/@react-native/codegen")
16 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
17 | // cliFile = file("../node_modules/react-native/cli.js")
18 |
19 | /* Variants */
20 | // The list of variants to that are debuggable. For those we're going to
21 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
22 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
23 | // debuggableVariants = ["liteDebug", "prodDebug"]
24 |
25 | /* Bundling */
26 | // A list containing the node command and its flags. Default is just 'node'.
27 | // nodeExecutableAndArgs = ["node"]
28 | //
29 | // The command to run when bundling. By default is 'bundle'
30 | // bundleCommand = "ram-bundle"
31 | //
32 | // The path to the CLI configuration file. Default is empty.
33 | // bundleConfig = file(../rn-cli.config.js)
34 | //
35 | // The name of the generated asset file containing your JS bundle
36 | // bundleAssetName = "MyApplication.android.bundle"
37 | //
38 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
39 | // entryFile = file("../js/MyApplication.android.js")
40 | //
41 | // A list of extra flags to pass to the 'bundle' commands.
42 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
43 | // extraPackagerArgs = []
44 |
45 | /* Hermes Commands */
46 | // The hermes compiler command to run. By default it is 'hermesc'
47 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
48 | //
49 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
50 | // hermesFlags = ["-O", "-output-source-map"]
51 | }
52 |
53 | /**
54 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
55 | */
56 | def enableProguardInReleaseBuilds = false
57 |
58 | /**
59 | * The preferred build flavor of JavaScriptCore (JSC)
60 | *
61 | * For example, to use the international variant, you can use:
62 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
63 | *
64 | * The international variant includes ICU i18n library and necessary data
65 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
66 | * give correct results when using with locales other than en-US. Note that
67 | * this variant is about 6MiB larger per architecture than default.
68 | */
69 | def jscFlavor = 'org.webkit:android-jsc:+'
70 |
71 | configurations.all {
72 | resolutionStrategy {
73 | eachDependency {
74 | if ((requested.group == "org.jetbrains.kotlin") && (requested.name.startsWith("kotlin-stdlib"))) {
75 | useVersion("1.8.0")
76 | }
77 | }
78 | }
79 | }
80 |
81 | android {
82 | ndkVersion rootProject.ext.ndkVersion
83 |
84 | compileSdkVersion rootProject.ext.compileSdkVersion
85 |
86 | namespace "com.example"
87 | defaultConfig {
88 | applicationId "com.example"
89 | minSdkVersion rootProject.ext.minSdkVersion
90 | targetSdkVersion rootProject.ext.targetSdkVersion
91 | versionCode 1
92 | versionName "1.0"
93 | ndk {
94 | abiFilters 'arm64-v8a'
95 | }
96 | }
97 | splits {
98 | abi {
99 | reset()
100 | // enable enableSeparateBuildPerCPUArchitecture
101 | universalApk false // If true, also generate a universal APK
102 | include "arm64-v8a"
103 | }
104 | }
105 | signingConfigs {
106 | debug {
107 | storeFile file('debug.keystore')
108 | storePassword 'android'
109 | keyAlias 'androiddebugkey'
110 | keyPassword 'android'
111 | }
112 | }
113 | buildTypes {
114 | debug {
115 | signingConfig signingConfigs.debug
116 | }
117 | release {
118 | // Caution! In production, you need to generate your own keystore file.
119 | // see https://reactnative.dev/docs/signed-apk-android.
120 | signingConfig signingConfigs.debug
121 | minifyEnabled enableProguardInReleaseBuilds
122 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
123 | }
124 | }
125 | }
126 |
127 | dependencies {
128 | // The version of react-native is set by the React Native Gradle Plugin
129 | implementation("com.facebook.react:react-android")
130 |
131 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
132 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
133 | exclude group:'com.squareup.okhttp3', module:'okhttp'
134 | }
135 |
136 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
137 | if (hermesEnabled.toBoolean()) {
138 | implementation("com.facebook.react:hermes-android")
139 | } else {
140 | implementation jscFlavor
141 | }
142 | }
143 |
144 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
145 |
--------------------------------------------------------------------------------
/android/src/main/java/com/azesmwayreactnativeunity/ReactNativeUnity.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import android.app.Activity;
4 | import android.graphics.PixelFormat;
5 | import android.os.Build;
6 | import android.view.ViewGroup;
7 | import android.view.WindowManager;
8 |
9 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
10 |
11 | import java.lang.reflect.InvocationTargetException;
12 |
13 | public class ReactNativeUnity {
14 | private static UPlayer unityPlayer;
15 | public static boolean _isUnityReady;
16 | public static boolean _isUnityPaused;
17 | public static boolean _fullScreen;
18 |
19 | public static UPlayer getPlayer() {
20 | if (!_isUnityReady) {
21 | return null;
22 | }
23 | return unityPlayer;
24 | }
25 |
26 | public static boolean isUnityReady() {
27 | return _isUnityReady;
28 | }
29 |
30 | public static boolean isUnityPaused() {
31 | return _isUnityPaused;
32 | }
33 |
34 | public static void createPlayer(final Activity activity, final UnityPlayerCallback callback) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
35 | if (unityPlayer != null) {
36 | callback.onReady();
37 |
38 | return;
39 | }
40 |
41 | if (activity != null) {
42 | activity.runOnUiThread(new Runnable() {
43 | @Override
44 | public void run() {
45 | activity.getWindow().setFormat(PixelFormat.RGBA_8888);
46 | int flag = activity.getWindow().getAttributes().flags;
47 | boolean fullScreen = false;
48 | if ((flag & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN) {
49 | fullScreen = true;
50 | }
51 |
52 | try {
53 | unityPlayer = new UPlayer(activity, callback);
54 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {}
55 |
56 | try {
57 | // wait a moment. fix unity cannot start when startup.
58 | Thread.sleep(1000);
59 | } catch (Exception e) {}
60 |
61 | // start unity
62 | try {
63 | addUnityViewToBackground();
64 | } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {}
65 |
66 | unityPlayer.windowFocusChanged(true);
67 |
68 | try {
69 | unityPlayer.requestFocusPlayer();
70 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {}
71 |
72 | unityPlayer.resume();
73 |
74 | if (!fullScreen) {
75 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
76 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
77 | }
78 |
79 | _isUnityReady = true;
80 |
81 | try {
82 | callback.onReady();
83 | } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {}
84 | }
85 | });
86 | }
87 | }
88 |
89 | public static void pause() {
90 | if (unityPlayer != null) {
91 | unityPlayer.pause();
92 | _isUnityPaused = true;
93 | }
94 | }
95 |
96 | public static void resume() {
97 | if (unityPlayer != null) {
98 | unityPlayer.resume();
99 | _isUnityPaused = false;
100 | }
101 | }
102 |
103 | public static void unload() {
104 | if (unityPlayer != null) {
105 | unityPlayer.unload();
106 | _isUnityPaused = false;
107 | }
108 | }
109 |
110 | public static void addUnityViewToBackground() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
111 | if (unityPlayer == null) {
112 | return;
113 | }
114 |
115 | if (unityPlayer.getParentPlayer() != null) {
116 | // NOTE: If we're being detached as part of the transition, make sure
117 | // to explicitly finish the transition first, as it might still keep
118 | // the view's parent around despite calling `removeView()` here. This
119 | // prevents a crash on an `addContentView()` later on.
120 | // Otherwise, if there's no transition, it's a no-op.
121 | // See https://stackoverflow.com/a/58247331
122 | ((ViewGroup) unityPlayer.getParentPlayer()).endViewTransition(unityPlayer.requestFrame());
123 | ((ViewGroup) unityPlayer.getParentPlayer()).removeView(unityPlayer.requestFrame());
124 | }
125 |
126 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
127 | unityPlayer.setZ(-1f);
128 | }
129 |
130 | final Activity activity = ((Activity) unityPlayer.getContextPlayer());
131 | ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(1, 1);
132 | activity.addContentView(unityPlayer.requestFrame(), layoutParams);
133 | }
134 |
135 | public static void addUnityViewToGroup(ViewGroup group) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
136 | if (unityPlayer == null) {
137 | return;
138 | }
139 |
140 | if (unityPlayer.getParentPlayer() != null) {
141 | ((ViewGroup) unityPlayer.getParentPlayer()).removeView(unityPlayer.requestFrame());
142 | }
143 |
144 | ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT);
145 | group.addView(unityPlayer.requestFrame(), 0, layoutParams);
146 | unityPlayer.windowFocusChanged(true);
147 | unityPlayer.requestFocusPlayer();
148 | unityPlayer.resume();
149 | }
150 |
151 | public interface UnityPlayerCallback {
152 | void onReady() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;
153 |
154 | void onUnload();
155 |
156 | void onQuit();
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/ios/RNUnityView.mm:
--------------------------------------------------------------------------------
1 | #import "RNUnityView.h"
2 | #ifdef DEBUG
3 | #include
4 | #endif
5 | #ifdef RCT_NEW_ARCH_ENABLED
6 | using namespace facebook::react;
7 | #endif
8 |
9 | NSString *bundlePathStr = @"/Frameworks/UnityFramework.framework";
10 | int gArgc = 1;
11 |
12 | UnityFramework* UnityFrameworkLoad() {
13 | NSString* bundlePath = nil;
14 | bundlePath = [[NSBundle mainBundle] bundlePath];
15 | bundlePath = [bundlePath stringByAppendingString: bundlePathStr];
16 |
17 | NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
18 | if ([bundle isLoaded] == false) [bundle load];
19 |
20 | UnityFramework* ufw = [bundle.principalClass getInstance];
21 | if (![ufw appController])
22 | {
23 | #ifdef DEBUG
24 | [ufw setExecuteHeader: &_mh_dylib_header];
25 | #else
26 | [ufw setExecuteHeader: &_mh_execute_header];
27 | #endif
28 | }
29 |
30 | [ufw setDataBundleId: [bundle.bundleIdentifier cStringUsingEncoding:NSUTF8StringEncoding]];
31 |
32 | return ufw;
33 | }
34 |
35 | @implementation RNUnityView
36 |
37 | NSDictionary* appLaunchOpts;
38 |
39 | static RNUnityView *sharedInstance;
40 |
41 | - (bool)unityIsInitialized {
42 | return [self ufw] && [[self ufw] appController];
43 | }
44 |
45 | - (void)initUnityModule {
46 | @try {
47 | if([self unityIsInitialized]) {
48 | return;
49 | }
50 |
51 | [self setUfw: UnityFrameworkLoad()];
52 | [[self ufw] registerFrameworkListener: self];
53 |
54 | unsigned count = (int) [[[NSProcessInfo processInfo] arguments] count];
55 | char **array = (char **)malloc((count + 1) * sizeof(char*));
56 |
57 | for (unsigned i = 0; i < count; i++)
58 | {
59 | array[i] = strdup([[[[NSProcessInfo processInfo] arguments] objectAtIndex:i] UTF8String]);
60 | }
61 | array[count] = NULL;
62 |
63 | [[self ufw] runEmbeddedWithArgc: gArgc argv: array appLaunchOpts: appLaunchOpts];
64 | [[self ufw] appController].quitHandler = ^(){ NSLog(@"AppController.quitHandler called"); };
65 | [self.ufw.appController.rootView removeFromSuperview];
66 |
67 | if (@available(iOS 13.0, *)) {
68 | [[[[self ufw] appController] window] setWindowScene: nil];
69 | } else {
70 | [[[[self ufw] appController] window] setScreen: nil];
71 | }
72 |
73 | [[[[self ufw] appController] window] addSubview: self.ufw.appController.rootView];
74 | [[[[self ufw] appController] window] makeKeyAndVisible];
75 | [[[[[[self ufw] appController] window] rootViewController] view] setNeedsLayout];
76 |
77 | [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self];
78 | }
79 | @catch (NSException *e) {
80 | NSLog(@"%@",e);
81 | }
82 | }
83 |
84 | - (void)layoutSubviews {
85 | [super layoutSubviews];
86 |
87 | if([self unityIsInitialized]) {
88 | self.ufw.appController.rootView.frame = self.bounds;
89 | [self addSubview:self.ufw.appController.rootView];
90 | }
91 | }
92 |
93 | - (void)pauseUnity:(BOOL * _Nonnull)pause {
94 | if([self unityIsInitialized]) {
95 | [[self ufw] pause:pause];
96 | }
97 | }
98 |
99 | - (void)unloadUnity {
100 | UIWindow * main = [[[UIApplication sharedApplication] delegate] window];
101 | if(main != nil) {
102 | [main makeKeyAndVisible];
103 |
104 | if([self unityIsInitialized]) {
105 | [[self ufw] unloadApplication];
106 | }
107 | }
108 | }
109 |
110 | - (void)sendMessageToMobileApp:(NSString *)message {
111 | if (self.onUnityMessage) {
112 | NSDictionary* data = @{
113 | @"message": message
114 | };
115 |
116 | self.onUnityMessage(data);
117 | }
118 | }
119 |
120 | - (void)unityDidUnload:(NSNotification*)notification {
121 | if([self unityIsInitialized]) {
122 | [[self ufw] unregisterFrameworkListener:self];
123 | [self setUfw: nil];
124 |
125 | if (self.onPlayerUnload) {
126 | self.onPlayerUnload(nil);
127 | }
128 | }
129 | }
130 |
131 | - (void)unityDidQuit:(NSNotification*)notification {
132 | if([self unityIsInitialized]) {
133 | [[self ufw] unregisterFrameworkListener:self];
134 | [self setUfw: nil];
135 |
136 | if (self.onPlayerQuit) {
137 | self.onPlayerQuit(nil);
138 | }
139 | }
140 | }
141 |
142 | - (dispatch_queue_t)methodQueue {
143 | return dispatch_get_main_queue();
144 | }
145 |
146 | - (NSArray *)supportedEvents {
147 | return @[@"onUnityMessage", @"onPlayerUnload", @"onPlayerQuit"];
148 | }
149 |
150 | - (void)postMessage:(NSString *)gameObject methodName:(NSString*)methodName message:(NSString*) message {
151 | dispatch_async(dispatch_get_main_queue(), ^{
152 | [[self ufw] sendMessageToGOWithName:[gameObject UTF8String] functionName:[methodName UTF8String] message:[message UTF8String]];
153 | });
154 | }
155 |
156 | #ifdef RCT_NEW_ARCH_ENABLED
157 | - (void)prepareForRecycle {
158 | [super prepareForRecycle];
159 |
160 | if ([self unityIsInitialized]) {
161 | [[self ufw] unloadApplication];
162 |
163 | NSArray *viewsToRemove = self.subviews;
164 | for (UIView *v in viewsToRemove) {
165 | [v removeFromSuperview];
166 | }
167 |
168 | [self setUfw:nil];
169 | }
170 | }
171 |
172 | + (ComponentDescriptorProvider)componentDescriptorProvider {
173 | return concreteComponentDescriptorProvider();
174 | }
175 |
176 | - (instancetype)initWithFrame:(CGRect)frame {
177 | if (self = [super initWithFrame:frame]) {
178 | static const auto defaultProps = std::make_shared();
179 | _props = defaultProps;
180 |
181 | self.onUnityMessage = [self](NSDictionary* data) {
182 | if (_eventEmitter != nil) {
183 | auto gridViewEventEmitter = std::static_pointer_cast(_eventEmitter);
184 | facebook::react::RNUnityViewEventEmitter::OnUnityMessage event = {
185 | .message=[[data valueForKey:@"message"] UTF8String]
186 | };
187 | gridViewEventEmitter->onUnityMessage(event);
188 | }
189 | };
190 | }
191 |
192 | return self;
193 | }
194 |
195 | - (void)updateEventEmitter:(EventEmitter::Shared const &)eventEmitter {
196 | [super updateEventEmitter:eventEmitter];
197 | }
198 |
199 | - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
200 | if (![self unityIsInitialized]) {
201 | [self initUnityModule];
202 | }
203 |
204 | [super updateProps:props oldProps:oldProps];
205 | }
206 |
207 | - (void)handleCommand:(nonnull const NSString *)commandName args:(nonnull const NSArray *)args {
208 | RCTRNUnityViewHandleCommand(self, commandName, args);
209 | }
210 |
211 | Class RNUnityViewCls(void) {
212 | return RNUnityView.class;
213 | }
214 |
215 | #else
216 |
217 | -(id)initWithFrame:(CGRect)frame {
218 | self = [super initWithFrame:frame];
219 |
220 | if (self) {
221 | [self initUnityModule];
222 | }
223 |
224 | return self;
225 | }
226 |
227 | #endif
228 |
229 | @end
230 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @azesmway/react-native-unity
2 |
3 | The plugin that allows you to embed a Unity project into React Native as a full-fledged component. The plugin now supports the new architecture.
4 |
5 | ### Android
6 | Attention! Added support for Unity 2023 and above
7 |
8 | > [!IMPORTANT]
9 | > For iOS, it is no longer necessary to embed a project created with Unity. Only the built `UnityFramework` is used. It should be placed in the plugin folder at the path - `/unity/builds/ios`
10 |
11 | ## Device Support:
12 |
13 | | Platform | Supported |
14 | | ---------------- | --------- |
15 | | iOS Simulator | ❌ |
16 | | iOS Device | ✅ |
17 | | Android Emulator | ✅ |
18 | | Android Device | ✅ |
19 |
20 | # Installation
21 |
22 | ## Install this package in your react-native project:
23 |
24 | ```sh
25 | npm install @azesmway/react-native-unity
26 |
27 | or
28 |
29 | yarn add @azesmway/react-native-unity
30 | ```
31 |
32 | ## Configure your Unity project:
33 |
34 | 1. Copy the contents of the folder `unity` to the root of your Unity project. This folder contains the necessary scripts and settings for the Unity project. You can find these files in your react-native project under `node_modules/@azesmway/react-native-unity/unity`. This is necessary to ensure iOS has access to the `NativeCallProxy` class from this library.
35 |
36 | 2. (optional) If you're following along with the example, you can add the following code to the `ButtonBehavior.cs` script in your Unity project. This allows the button press in Unity to communicate with your react-native app.
37 |
38 |
39 | ButtonBehavior.cs
40 |
41 | ```csharp
42 | using System;
43 | using System.Collections;
44 | using System.Collections.Generic;
45 | using System.Runtime.InteropServices;
46 | using UnityEngine.UI;
47 | using UnityEngine;
48 |
49 | public class NativeAPI {
50 | #if UNITY_IOS && !UNITY_EDITOR
51 | [DllImport("__Internal")]
52 | public static extern void sendMessageToMobileApp(string message);
53 | #endif
54 | }
55 |
56 | public class ButtonBehavior : MonoBehaviour
57 | {
58 | public void ButtonPressed()
59 | {
60 | if (Application.platform == RuntimePlatform.Android)
61 | {
62 | using (AndroidJavaClass jc = new AndroidJavaClass("com.azesmwayreactnativeunity.ReactNativeUnityViewManager"))
63 | {
64 | jc.CallStatic("sendMessageToMobileApp", "The button has been tapped!");
65 | }
66 | }
67 | else if (Application.platform == RuntimePlatform.IPhonePlayer)
68 | {
69 | #if UNITY_IOS && !UNITY_EDITOR
70 | NativeAPI.sendMessageToMobileApp("The button has been tapped!");
71 | #endif
72 | }
73 | }
74 | }
75 | ```
76 |
77 |
78 |
79 | ## Export iOS Unity Project:
80 |
81 | After you've moved the files from the `unity` folder to your Unity project, you can export the iOS unity project by following these steps:
82 |
83 | 1. Open your Unity project
84 | 2. Build Unity project for ios in ANY folder - just not the main RN project folder!!!
85 | 3. Open the created project in XCode
86 | 4. Select Data folder and set a checkbox in the "Target Membership" section to "UnityFramework" 
87 | 5. You need to select the NativeCallProxy.h inside the `Unity-iPhone/Libraries/Plugins/iOS` folder of the Unity-iPhone project and change UnityFramework’s target membership from Project to Public. Don’t forget this step! (if you don't see these files in your Xcode project, you didn't copy over the `unity` folder to your Unity project correctly in previous steps) 
88 | 6. If required - sign the project `UnityFramework.framework` and build a framework 
89 | 7. Open the folder with the built framework (by right-clicking) and move it to the plugin folder (`/unity/builds/ios`) 
90 | 8. Remove your `Pods` cache and lockfile with this command in the root of the main RN project `rm -rf ios/Pods && rm -f ios/Podfile.lock && npx pod-install`
91 |
92 | ### Android
93 |
94 | 1. Open your Unity project
95 | 2. Export Unity app to `/unity/builds/android`
96 | 3. Remove `...` from `/unity/builds/android/unityLibrary/src/main/AndroidManifest.xml` at unityLibrary to leave only integrated version.
97 |
98 | If you're using expo, you're done. The built-in expo plugin will handle the rest. If you're not using expo, you'll need to follow the steps below.
99 |
100 | 1. Add the following lines to `android/settings.gradle`:
101 | ```groovy
102 | include ':unityLibrary'
103 | project(':unityLibrary').projectDir=new File('..\\unity\\builds\\android\\unityLibrary')
104 | ```
105 | 2. Add into `android/build.gradle`
106 | ```groovy
107 | allprojects {
108 | repositories {
109 | // this
110 | flatDir {
111 | dirs "${project(':unityLibrary').projectDir}/libs"
112 | }
113 | // ...
114 | }
115 | }
116 | ```
117 | 3. Add into `android/gradle.properties`
118 | ```gradle
119 | unityStreamingAssets=.unity3d
120 | ```
121 | 4. Add strings to `android/app/src/main/res/values/strings.xml`
122 |
123 | ```javascript
124 | Game view
125 | ```
126 |
127 | # Known issues
128 |
129 | - Does not work on the iOS simulator.
130 | - On iOS the Unity view is waiting for a parent with dimensions greater than 0 (from RN side). Please take care of this because if it is not the case, your app will crash with the native message `MTLTextureDescriptor has width of zero`.
131 |
132 | # Usage
133 |
134 | ## Sample code
135 |
136 | ```jsx
137 | import React, { useRef, useEffect } from 'react';
138 |
139 | import UnityView from '@azesmway/react-native-unity';
140 | import { View } from 'react-native';
141 |
142 | interface IMessage {
143 | gameObject: string;
144 | methodName: string;
145 | message: string;
146 | }
147 |
148 | const Unity = () => {
149 | const unityRef = useRef(null);
150 |
151 | useEffect(() => {
152 | if (unityRef?.current) {
153 | const message: IMessage = {
154 | gameObject: 'gameObject',
155 | methodName: 'methodName',
156 | message: 'message',
157 | };
158 | unityRef.current.postMessage(
159 | message.gameObject,
160 | message.methodName,
161 | message.message
162 | );
163 | }
164 | }, []);
165 |
166 | return (
167 |
168 | {
172 | console.log('onUnityMessage', result.nativeEvent.message);
173 | }}
174 | />
175 |
176 | );
177 | };
178 |
179 | export default Unity;
180 | ```
181 |
182 | ## Props
183 |
184 | - `style: ViewStyle` - styles the UnityView. (Won't show on Android without dimensions. Recommended to give it `flex: 1` as in the example)
185 | - `onUnityMessage?: (event: NativeSyntheticEvent)` - receives a message from a Unity
186 | - `androidKeepPlayerMounted?: boolean` - if set to true, keep the player mounted even when the view that contains it has lost focus. The player will be paused on blur and resumed on focus. **ANDROID ONLY**
187 | - `fullScreen?: boolean` - defaults to true. If set to false, will not request full screen access. **ANDROID ONLY**
188 |
189 | ## Methods
190 |
191 | - `postMessage(gameObject, methodName, message)` - sends a message to the Unity. **FOR IOS:** The native method of unity is used to send a message
192 | `sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg;`, more details can be found in the [documentation](https://docs.unity3d.com/2021.1/Documentation/Manual/UnityasaLibrary-iOS.html)
193 | - `unloadUnity()` - the Unity is unloaded automatically when the react-native component is unmounted, but if you want to unload the Unity, you can call this method
194 | - `pauseUnity?: (pause: boolean)` - pause the Unity
195 | - `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**
196 |
197 | # Contributing
198 |
199 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
200 |
201 | # License
202 |
203 | MIT
204 |
--------------------------------------------------------------------------------
/android/src/main/java/com/azesmwayreactnativeunity/ReactNativeUnityViewManager.java:
--------------------------------------------------------------------------------
1 | package com.azesmwayreactnativeunity;
2 |
3 | import static com.azesmwayreactnativeunity.ReactNativeUnity.*;
4 |
5 | import android.os.Handler;
6 | import android.view.View;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 |
11 | import com.facebook.infer.annotation.Assertions;
12 | import com.facebook.react.bridge.Arguments;
13 | import com.facebook.react.bridge.LifecycleEventListener;
14 | import com.facebook.react.bridge.ReactApplicationContext;
15 | import com.facebook.react.bridge.ReactContext;
16 | import com.facebook.react.bridge.ReadableArray;
17 | import com.facebook.react.bridge.WritableMap;
18 | import com.facebook.react.common.MapBuilder;
19 | import com.facebook.react.module.annotations.ReactModule;
20 | import com.facebook.react.uimanager.ThemedReactContext;
21 | import com.facebook.react.uimanager.annotations.ReactProp;
22 | import com.facebook.react.uimanager.events.RCTEventEmitter;
23 |
24 | import java.lang.reflect.InvocationTargetException;
25 | import java.util.Map;
26 |
27 | @ReactModule(name = ReactNativeUnityViewManager.NAME)
28 | public class ReactNativeUnityViewManager extends ReactNativeUnityViewManagerSpec implements LifecycleEventListener, View.OnAttachStateChangeListener {
29 | ReactApplicationContext context;
30 | static ReactNativeUnityView view;
31 | public static final String NAME = "RNUnityView";
32 |
33 | public ReactNativeUnityViewManager(ReactApplicationContext context) {
34 | super();
35 | this.context = context;
36 | context.addLifecycleEventListener(this);
37 | }
38 |
39 | @NonNull
40 | @Override
41 | public String getName() {
42 | return NAME;
43 | }
44 |
45 | @NonNull
46 | @Override
47 | public ReactNativeUnityView createViewInstance(@NonNull ThemedReactContext context) {
48 | view = new ReactNativeUnityView(this.context);
49 | view.addOnAttachStateChangeListener(this);
50 |
51 | if (getPlayer() != null) {
52 | try {
53 | view.setUnityPlayer(getPlayer());
54 | } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {}
55 | } else {
56 | try {
57 | createPlayer(context.getCurrentActivity(), new UnityPlayerCallback() {
58 | @Override
59 | public void onReady() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
60 | view.setUnityPlayer(getPlayer());
61 | }
62 |
63 | @Override
64 | public void onUnload() {
65 | WritableMap data = Arguments.createMap();
66 | data.putString("message", "MyMessage");
67 | ReactContext reactContext = (ReactContext) view.getContext();
68 | reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(view.getId(), "onPlayerUnload", data);
69 | }
70 |
71 | @Override
72 | public void onQuit() {
73 | WritableMap data = Arguments.createMap();
74 | data.putString("message", "MyMessage");
75 | ReactContext reactContext = (ReactContext) view.getContext();
76 | reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(view.getId(), "onPlayerQuit", data);
77 | }
78 | });
79 | } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {}
80 | }
81 |
82 | return view;
83 | }
84 |
85 | @Override
86 | public Map getExportedCustomDirectEventTypeConstants() {
87 | Map export = super.getExportedCustomDirectEventTypeConstants();
88 |
89 | if (export == null) {
90 | export = MapBuilder.newHashMap();
91 | }
92 |
93 | export.put("onUnityMessage", MapBuilder.of("registrationName", "onUnityMessage"));
94 | export.put("onPlayerUnload", MapBuilder.of("registrationName", "onPlayerUnload"));
95 | export.put("onPlayerQuit", MapBuilder.of("registrationName", "onPlayerQuit"));
96 |
97 | return export;
98 | }
99 |
100 | @Override
101 | public void receiveCommand(@NonNull ReactNativeUnityView view, String commandType, @Nullable ReadableArray args) {
102 | Assertions.assertNotNull(view);
103 | Assertions.assertNotNull(args);
104 |
105 | switch (commandType) {
106 | case "postMessage":
107 | assert args != null;
108 | postMessage(view, args.getString(0), args.getString(1), args.getString(2));
109 | return;
110 | case "unloadUnity":
111 | unloadUnity(view);
112 | return;
113 | case "pauseUnity":
114 | assert args != null;
115 | pauseUnity(view, args.getBoolean(0));
116 | return;
117 | case "resumeUnity":
118 | resumeUnity(view);
119 | return;
120 | case "windowFocusChanged":
121 | assert args != null;
122 | windowFocusChanged(view, args.getBoolean(0));
123 | return;
124 | default:
125 | throw new IllegalArgumentException(String.format(
126 | "Unsupported command %s received by %s.",
127 | commandType,
128 | getClass().getSimpleName()));
129 | }
130 | }
131 |
132 | @Override
133 | public void unloadUnity(ReactNativeUnityView view) {
134 | if (isUnityReady()) {
135 | unload();
136 | }
137 | }
138 |
139 | @Override
140 | public void pauseUnity(ReactNativeUnityView view, boolean pause) {
141 | if (isUnityReady()) {
142 | assert getPlayer() != null;
143 | getPlayer().pause();
144 | }
145 | }
146 |
147 | @Override
148 | public void resumeUnity(ReactNativeUnityView view) {
149 | if (isUnityReady()) {
150 | assert getPlayer() != null;
151 | getPlayer().resume();
152 | }
153 | }
154 |
155 | @Override
156 | public void windowFocusChanged(ReactNativeUnityView view, boolean hasFocus) {
157 | if (isUnityReady()) {
158 | assert getPlayer() != null;
159 | getPlayer().windowFocusChanged(hasFocus);
160 | }
161 | }
162 |
163 | public static void sendMessageToMobileApp(String message) {
164 | WritableMap data = Arguments.createMap();
165 | data.putString("message", message);
166 | ReactContext reactContext = (ReactContext) view.getContext();
167 | reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(view.getId(), "onUnityMessage", data);
168 | }
169 |
170 | @Override
171 | public void onDropViewInstance(ReactNativeUnityView view) {
172 | view.removeOnAttachStateChangeListener(this);
173 | super.onDropViewInstance(view);
174 | }
175 |
176 | @Override
177 | public void onHostResume() {
178 | if (isUnityReady()) {
179 | assert getPlayer() != null;
180 | getPlayer().resume();
181 | restoreUnityUserState();
182 | }
183 | }
184 |
185 | @Override
186 | public void onHostPause() {
187 | if (isUnityReady()) {
188 | assert getPlayer() != null;
189 | getPlayer().pause();
190 | }
191 | }
192 |
193 | @Override
194 | public void onHostDestroy() {
195 | if (isUnityReady()) {
196 | assert getPlayer() != null;
197 | getPlayer().destroy();
198 | }
199 | }
200 |
201 | private void restoreUnityUserState() {
202 | // restore the unity player state
203 | if (isUnityPaused()) {
204 | Handler handler = new Handler();
205 | handler.postDelayed(new Runnable() {
206 | @Override
207 | public void run() {
208 | if (getPlayer() != null) {
209 | getPlayer().pause();
210 | }
211 | }
212 | }, 300);
213 | }
214 | }
215 |
216 | @Override
217 | public void onViewAttachedToWindow(View v) {
218 | restoreUnityUserState();
219 | }
220 |
221 | @Override
222 | public void onViewDetachedFromWindow(View v) {}
223 |
224 | @ReactProp(name = "androidKeepPlayerMounted", defaultBoolean = false)
225 | public void setAndroidKeepPlayerMounted(ReactNativeUnityView view, boolean keepPlayerMounted) {
226 | view.keepPlayerMounted = keepPlayerMounted;
227 | }
228 |
229 | @ReactProp(name = "fullScreen", defaultBoolean = true)
230 | public void setFullScreen(ReactNativeUnityView view, boolean fullScreen) {
231 | _fullScreen = fullScreen;
232 | }
233 |
234 | @Override
235 | public void postMessage(ReactNativeUnityView view, String gameObject, String methodName, String message) {
236 | if (isUnityReady()) {
237 | assert getPlayer() != null;
238 | UPlayer.UnitySendMessage(gameObject, methodName, message);
239 | }
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project.
4 |
5 | ## Development workflow
6 |
7 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package:
8 |
9 | ```sh
10 | yarn
11 | ```
12 |
13 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development.
14 |
15 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app.
16 |
17 | To start the packager:
18 |
19 | ```sh
20 | yarn example start
21 | ```
22 |
23 | To run the example app on Android:
24 |
25 | ```sh
26 | yarn example android
27 | ```
28 |
29 | To run the example app on iOS:
30 |
31 | ```sh
32 | yarn example ios
33 | ```
34 |
35 | Make sure your code passes TypeScript and ESLint. Run the following to verify:
36 |
37 | ```sh
38 | yarn typescript
39 | yarn lint
40 | ```
41 |
42 | To fix formatting errors, run the following:
43 |
44 | ```sh
45 | yarn lint --fix
46 | ```
47 |
48 | Remember to add tests for your change if possible. Run the unit tests by:
49 |
50 | ```sh
51 | yarn test
52 | ```
53 |
54 | To edit the Objective-C files, open `example/ios/ReactNativeUnityExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > @azesmway/react-native-unity`.
55 |
56 | To edit the Kotlin files, open `example/android` in Android studio and find the source files at `azesmwayreactnativeunity` under `Android`.
57 |
58 | ### Commit message convention
59 |
60 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
61 |
62 | - `fix`: bug fixes, e.g. fix crash due to deprecated method.
63 | - `feat`: new features, e.g. add new method to the module.
64 | - `refactor`: code refactor, e.g. migrate from class components to hooks.
65 | - `docs`: changes into documentation, e.g. add usage example for the module..
66 | - `test`: adding or updating tests, e.g. add integration tests using detox.
67 | - `chore`: tooling changes, e.g. change CI config.
68 |
69 | Our pre-commit hooks verify that your commit message matches this format when committing.
70 |
71 | ### Linting and tests
72 |
73 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
74 |
75 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
76 |
77 | Our pre-commit hooks verify that the linter and tests pass when committing.
78 |
79 | ### Publishing to npm
80 |
81 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
82 |
83 | To publish new versions, run the following:
84 |
85 | ```sh
86 | yarn release
87 | ```
88 |
89 | ### Scripts
90 |
91 | The `package.json` file contains various scripts for common tasks:
92 |
93 | - `yarn bootstrap`: setup project by installing all dependencies and pods.
94 | - `yarn typescript`: type-check files with TypeScript.
95 | - `yarn lint`: lint files with ESLint.
96 | - `yarn test`: run unit tests with Jest.
97 | - `yarn example start`: start the Metro server for the example app.
98 | - `yarn example android`: run the example app on Android.
99 | - `yarn example ios`: run the example app on iOS.
100 |
101 | ### Sending a pull request
102 |
103 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
104 |
105 | When you're sending a pull request:
106 |
107 | - Prefer small pull requests focused on one change.
108 | - Verify that linters and tests are passing.
109 | - Review the documentation to make sure it looks good.
110 | - Follow the pull request template when opening a pull request.
111 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
112 |
113 | ## Code of Conduct
114 |
115 | ### Our Pledge
116 |
117 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
118 |
119 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
120 |
121 | ### Our Standards
122 |
123 | Examples of behavior that contributes to a positive environment for our community include:
124 |
125 | - Demonstrating empathy and kindness toward other people
126 | - Being respectful of differing opinions, viewpoints, and experiences
127 | - Giving and gracefully accepting constructive feedback
128 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
129 | - Focusing on what is best not just for us as individuals, but for the overall community
130 |
131 | Examples of unacceptable behavior include:
132 |
133 | - The use of sexualized language or imagery, and sexual attention or
134 | advances of any kind
135 | - Trolling, insulting or derogatory comments, and personal or political attacks
136 | - Public or private harassment
137 | - Publishing others' private information, such as a physical or email
138 | address, without their explicit permission
139 | - Other conduct which could reasonably be considered inappropriate in a
140 | professional setting
141 |
142 | ### Enforcement Responsibilities
143 |
144 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
145 |
146 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
147 |
148 | ### Scope
149 |
150 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
151 |
152 | ### Enforcement
153 |
154 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly.
155 |
156 | All community leaders are obligated to respect the privacy and security of the reporter of any incident.
157 |
158 | ### Enforcement Guidelines
159 |
160 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
161 |
162 | #### 1. Correction
163 |
164 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
165 |
166 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
167 |
168 | #### 2. Warning
169 |
170 | **Community Impact**: A violation through a single incident or series of actions.
171 |
172 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
173 |
174 | #### 3. Temporary Ban
175 |
176 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
177 |
178 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
179 |
180 | #### 4. Permanent Ban
181 |
182 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
183 |
184 | **Consequence**: A permanent ban from any sort of public interaction within the community.
185 |
186 | ### Attribution
187 |
188 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
189 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
190 |
191 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
192 |
193 | [homepage]: https://www.contributor-covenant.org
194 |
195 | For answers to common questions about this code of conduct, see the FAQ at
196 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
197 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - boost (1.76.0)
3 | - DoubleConversion (1.1.6)
4 | - FBLazyVector (0.72.7)
5 | - FBReactNativeSpec (0.72.7):
6 | - RCT-Folly (= 2021.07.22.00)
7 | - RCTRequired (= 0.72.7)
8 | - RCTTypeSafety (= 0.72.7)
9 | - React-Core (= 0.72.7)
10 | - React-jsi (= 0.72.7)
11 | - ReactCommon/turbomodule/core (= 0.72.7)
12 | - fmt (6.2.1)
13 | - glog (0.3.5)
14 | - hermes-engine (0.72.7):
15 | - hermes-engine/Pre-built (= 0.72.7)
16 | - hermes-engine/Pre-built (0.72.7)
17 | - libevent (2.1.12)
18 | - RCT-Folly (2021.07.22.00):
19 | - boost
20 | - DoubleConversion
21 | - fmt (~> 6.2.1)
22 | - glog
23 | - RCT-Folly/Default (= 2021.07.22.00)
24 | - RCT-Folly/Default (2021.07.22.00):
25 | - boost
26 | - DoubleConversion
27 | - fmt (~> 6.2.1)
28 | - glog
29 | - RCT-Folly/Futures (2021.07.22.00):
30 | - boost
31 | - DoubleConversion
32 | - fmt (~> 6.2.1)
33 | - glog
34 | - libevent
35 | - RCTRequired (0.72.7)
36 | - RCTTypeSafety (0.72.7):
37 | - FBLazyVector (= 0.72.7)
38 | - RCTRequired (= 0.72.7)
39 | - React-Core (= 0.72.7)
40 | - React (0.72.7):
41 | - React-Core (= 0.72.7)
42 | - React-Core/DevSupport (= 0.72.7)
43 | - React-Core/RCTWebSocket (= 0.72.7)
44 | - React-RCTActionSheet (= 0.72.7)
45 | - React-RCTAnimation (= 0.72.7)
46 | - React-RCTBlob (= 0.72.7)
47 | - React-RCTImage (= 0.72.7)
48 | - React-RCTLinking (= 0.72.7)
49 | - React-RCTNetwork (= 0.72.7)
50 | - React-RCTSettings (= 0.72.7)
51 | - React-RCTText (= 0.72.7)
52 | - React-RCTVibration (= 0.72.7)
53 | - React-callinvoker (0.72.7)
54 | - React-Codegen (0.72.7):
55 | - DoubleConversion
56 | - FBReactNativeSpec
57 | - glog
58 | - hermes-engine
59 | - RCT-Folly
60 | - RCTRequired
61 | - RCTTypeSafety
62 | - React-Core
63 | - React-jsi
64 | - React-jsiexecutor
65 | - React-NativeModulesApple
66 | - React-rncore
67 | - ReactCommon/turbomodule/bridging
68 | - ReactCommon/turbomodule/core
69 | - React-Core (0.72.7):
70 | - glog
71 | - hermes-engine
72 | - RCT-Folly (= 2021.07.22.00)
73 | - React-Core/Default (= 0.72.7)
74 | - React-cxxreact
75 | - React-hermes
76 | - React-jsi
77 | - React-jsiexecutor
78 | - React-perflogger
79 | - React-runtimeexecutor
80 | - React-utils
81 | - SocketRocket (= 0.6.1)
82 | - Yoga
83 | - React-Core/CoreModulesHeaders (0.72.7):
84 | - glog
85 | - hermes-engine
86 | - RCT-Folly (= 2021.07.22.00)
87 | - React-Core/Default
88 | - React-cxxreact
89 | - React-hermes
90 | - React-jsi
91 | - React-jsiexecutor
92 | - React-perflogger
93 | - React-runtimeexecutor
94 | - React-utils
95 | - SocketRocket (= 0.6.1)
96 | - Yoga
97 | - React-Core/Default (0.72.7):
98 | - glog
99 | - hermes-engine
100 | - RCT-Folly (= 2021.07.22.00)
101 | - React-cxxreact
102 | - React-hermes
103 | - React-jsi
104 | - React-jsiexecutor
105 | - React-perflogger
106 | - React-runtimeexecutor
107 | - React-utils
108 | - SocketRocket (= 0.6.1)
109 | - Yoga
110 | - React-Core/DevSupport (0.72.7):
111 | - glog
112 | - hermes-engine
113 | - RCT-Folly (= 2021.07.22.00)
114 | - React-Core/Default (= 0.72.7)
115 | - React-Core/RCTWebSocket (= 0.72.7)
116 | - React-cxxreact
117 | - React-hermes
118 | - React-jsi
119 | - React-jsiexecutor
120 | - React-jsinspector (= 0.72.7)
121 | - React-perflogger
122 | - React-runtimeexecutor
123 | - React-utils
124 | - SocketRocket (= 0.6.1)
125 | - Yoga
126 | - React-Core/RCTActionSheetHeaders (0.72.7):
127 | - glog
128 | - hermes-engine
129 | - RCT-Folly (= 2021.07.22.00)
130 | - React-Core/Default
131 | - React-cxxreact
132 | - React-hermes
133 | - React-jsi
134 | - React-jsiexecutor
135 | - React-perflogger
136 | - React-runtimeexecutor
137 | - React-utils
138 | - SocketRocket (= 0.6.1)
139 | - Yoga
140 | - React-Core/RCTAnimationHeaders (0.72.7):
141 | - glog
142 | - hermes-engine
143 | - RCT-Folly (= 2021.07.22.00)
144 | - React-Core/Default
145 | - React-cxxreact
146 | - React-hermes
147 | - React-jsi
148 | - React-jsiexecutor
149 | - React-perflogger
150 | - React-runtimeexecutor
151 | - React-utils
152 | - SocketRocket (= 0.6.1)
153 | - Yoga
154 | - React-Core/RCTBlobHeaders (0.72.7):
155 | - glog
156 | - hermes-engine
157 | - RCT-Folly (= 2021.07.22.00)
158 | - React-Core/Default
159 | - React-cxxreact
160 | - React-hermes
161 | - React-jsi
162 | - React-jsiexecutor
163 | - React-perflogger
164 | - React-runtimeexecutor
165 | - React-utils
166 | - SocketRocket (= 0.6.1)
167 | - Yoga
168 | - React-Core/RCTImageHeaders (0.72.7):
169 | - glog
170 | - hermes-engine
171 | - RCT-Folly (= 2021.07.22.00)
172 | - React-Core/Default
173 | - React-cxxreact
174 | - React-hermes
175 | - React-jsi
176 | - React-jsiexecutor
177 | - React-perflogger
178 | - React-runtimeexecutor
179 | - React-utils
180 | - SocketRocket (= 0.6.1)
181 | - Yoga
182 | - React-Core/RCTLinkingHeaders (0.72.7):
183 | - glog
184 | - hermes-engine
185 | - RCT-Folly (= 2021.07.22.00)
186 | - React-Core/Default
187 | - React-cxxreact
188 | - React-hermes
189 | - React-jsi
190 | - React-jsiexecutor
191 | - React-perflogger
192 | - React-runtimeexecutor
193 | - React-utils
194 | - SocketRocket (= 0.6.1)
195 | - Yoga
196 | - React-Core/RCTNetworkHeaders (0.72.7):
197 | - glog
198 | - hermes-engine
199 | - RCT-Folly (= 2021.07.22.00)
200 | - React-Core/Default
201 | - React-cxxreact
202 | - React-hermes
203 | - React-jsi
204 | - React-jsiexecutor
205 | - React-perflogger
206 | - React-runtimeexecutor
207 | - React-utils
208 | - SocketRocket (= 0.6.1)
209 | - Yoga
210 | - React-Core/RCTSettingsHeaders (0.72.7):
211 | - glog
212 | - hermes-engine
213 | - RCT-Folly (= 2021.07.22.00)
214 | - React-Core/Default
215 | - React-cxxreact
216 | - React-hermes
217 | - React-jsi
218 | - React-jsiexecutor
219 | - React-perflogger
220 | - React-runtimeexecutor
221 | - React-utils
222 | - SocketRocket (= 0.6.1)
223 | - Yoga
224 | - React-Core/RCTTextHeaders (0.72.7):
225 | - glog
226 | - hermes-engine
227 | - RCT-Folly (= 2021.07.22.00)
228 | - React-Core/Default
229 | - React-cxxreact
230 | - React-hermes
231 | - React-jsi
232 | - React-jsiexecutor
233 | - React-perflogger
234 | - React-runtimeexecutor
235 | - React-utils
236 | - SocketRocket (= 0.6.1)
237 | - Yoga
238 | - React-Core/RCTVibrationHeaders (0.72.7):
239 | - glog
240 | - hermes-engine
241 | - RCT-Folly (= 2021.07.22.00)
242 | - React-Core/Default
243 | - React-cxxreact
244 | - React-hermes
245 | - React-jsi
246 | - React-jsiexecutor
247 | - React-perflogger
248 | - React-runtimeexecutor
249 | - React-utils
250 | - SocketRocket (= 0.6.1)
251 | - Yoga
252 | - React-Core/RCTWebSocket (0.72.7):
253 | - glog
254 | - hermes-engine
255 | - RCT-Folly (= 2021.07.22.00)
256 | - React-Core/Default (= 0.72.7)
257 | - React-cxxreact
258 | - React-hermes
259 | - React-jsi
260 | - React-jsiexecutor
261 | - React-perflogger
262 | - React-runtimeexecutor
263 | - React-utils
264 | - SocketRocket (= 0.6.1)
265 | - Yoga
266 | - React-CoreModules (0.72.7):
267 | - RCT-Folly (= 2021.07.22.00)
268 | - RCTTypeSafety (= 0.72.7)
269 | - React-Codegen (= 0.72.7)
270 | - React-Core/CoreModulesHeaders (= 0.72.7)
271 | - React-jsi (= 0.72.7)
272 | - React-RCTBlob
273 | - React-RCTImage (= 0.72.7)
274 | - ReactCommon/turbomodule/core (= 0.72.7)
275 | - SocketRocket (= 0.6.1)
276 | - React-cxxreact (0.72.7):
277 | - boost (= 1.76.0)
278 | - DoubleConversion
279 | - glog
280 | - hermes-engine
281 | - RCT-Folly (= 2021.07.22.00)
282 | - React-callinvoker (= 0.72.7)
283 | - React-debug (= 0.72.7)
284 | - React-jsi (= 0.72.7)
285 | - React-jsinspector (= 0.72.7)
286 | - React-logger (= 0.72.7)
287 | - React-perflogger (= 0.72.7)
288 | - React-runtimeexecutor (= 0.72.7)
289 | - React-debug (0.72.7)
290 | - React-hermes (0.72.7):
291 | - DoubleConversion
292 | - glog
293 | - hermes-engine
294 | - RCT-Folly (= 2021.07.22.00)
295 | - RCT-Folly/Futures (= 2021.07.22.00)
296 | - React-cxxreact (= 0.72.7)
297 | - React-jsi
298 | - React-jsiexecutor (= 0.72.7)
299 | - React-jsinspector (= 0.72.7)
300 | - React-perflogger (= 0.72.7)
301 | - React-jsi (0.72.7):
302 | - boost (= 1.76.0)
303 | - DoubleConversion
304 | - glog
305 | - hermes-engine
306 | - RCT-Folly (= 2021.07.22.00)
307 | - React-jsiexecutor (0.72.7):
308 | - DoubleConversion
309 | - glog
310 | - hermes-engine
311 | - RCT-Folly (= 2021.07.22.00)
312 | - React-cxxreact (= 0.72.7)
313 | - React-jsi (= 0.72.7)
314 | - React-perflogger (= 0.72.7)
315 | - React-jsinspector (0.72.7)
316 | - React-logger (0.72.7):
317 | - glog
318 | - react-native-safe-area-context (4.7.4):
319 | - React-Core
320 | - react-native-unity (0.4.0):
321 | - React-Core
322 | - React-NativeModulesApple (0.72.7):
323 | - hermes-engine
324 | - React-callinvoker
325 | - React-Core
326 | - React-cxxreact
327 | - React-jsi
328 | - React-runtimeexecutor
329 | - ReactCommon/turbomodule/bridging
330 | - ReactCommon/turbomodule/core
331 | - React-perflogger (0.72.7)
332 | - React-RCTActionSheet (0.72.7):
333 | - React-Core/RCTActionSheetHeaders (= 0.72.7)
334 | - React-RCTAnimation (0.72.7):
335 | - RCT-Folly (= 2021.07.22.00)
336 | - RCTTypeSafety (= 0.72.7)
337 | - React-Codegen (= 0.72.7)
338 | - React-Core/RCTAnimationHeaders (= 0.72.7)
339 | - React-jsi (= 0.72.7)
340 | - ReactCommon/turbomodule/core (= 0.72.7)
341 | - React-RCTAppDelegate (0.72.7):
342 | - RCT-Folly
343 | - RCTRequired
344 | - RCTTypeSafety
345 | - React-Core
346 | - React-CoreModules
347 | - React-hermes
348 | - React-NativeModulesApple
349 | - React-RCTImage
350 | - React-RCTNetwork
351 | - React-runtimescheduler
352 | - ReactCommon/turbomodule/core
353 | - React-RCTBlob (0.72.7):
354 | - hermes-engine
355 | - RCT-Folly (= 2021.07.22.00)
356 | - React-Codegen (= 0.72.7)
357 | - React-Core/RCTBlobHeaders (= 0.72.7)
358 | - React-Core/RCTWebSocket (= 0.72.7)
359 | - React-jsi (= 0.72.7)
360 | - React-RCTNetwork (= 0.72.7)
361 | - ReactCommon/turbomodule/core (= 0.72.7)
362 | - React-RCTImage (0.72.7):
363 | - RCT-Folly (= 2021.07.22.00)
364 | - RCTTypeSafety (= 0.72.7)
365 | - React-Codegen (= 0.72.7)
366 | - React-Core/RCTImageHeaders (= 0.72.7)
367 | - React-jsi (= 0.72.7)
368 | - React-RCTNetwork (= 0.72.7)
369 | - ReactCommon/turbomodule/core (= 0.72.7)
370 | - React-RCTLinking (0.72.7):
371 | - React-Codegen (= 0.72.7)
372 | - React-Core/RCTLinkingHeaders (= 0.72.7)
373 | - React-jsi (= 0.72.7)
374 | - ReactCommon/turbomodule/core (= 0.72.7)
375 | - React-RCTNetwork (0.72.7):
376 | - RCT-Folly (= 2021.07.22.00)
377 | - RCTTypeSafety (= 0.72.7)
378 | - React-Codegen (= 0.72.7)
379 | - React-Core/RCTNetworkHeaders (= 0.72.7)
380 | - React-jsi (= 0.72.7)
381 | - ReactCommon/turbomodule/core (= 0.72.7)
382 | - React-RCTSettings (0.72.7):
383 | - RCT-Folly (= 2021.07.22.00)
384 | - RCTTypeSafety (= 0.72.7)
385 | - React-Codegen (= 0.72.7)
386 | - React-Core/RCTSettingsHeaders (= 0.72.7)
387 | - React-jsi (= 0.72.7)
388 | - ReactCommon/turbomodule/core (= 0.72.7)
389 | - React-RCTText (0.72.7):
390 | - React-Core/RCTTextHeaders (= 0.72.7)
391 | - React-RCTVibration (0.72.7):
392 | - RCT-Folly (= 2021.07.22.00)
393 | - React-Codegen (= 0.72.7)
394 | - React-Core/RCTVibrationHeaders (= 0.72.7)
395 | - React-jsi (= 0.72.7)
396 | - ReactCommon/turbomodule/core (= 0.72.7)
397 | - React-rncore (0.72.7)
398 | - React-runtimeexecutor (0.72.7):
399 | - React-jsi (= 0.72.7)
400 | - React-runtimescheduler (0.72.7):
401 | - glog
402 | - hermes-engine
403 | - RCT-Folly (= 2021.07.22.00)
404 | - React-callinvoker
405 | - React-debug
406 | - React-jsi
407 | - React-runtimeexecutor
408 | - React-utils (0.72.7):
409 | - glog
410 | - RCT-Folly (= 2021.07.22.00)
411 | - React-debug
412 | - ReactCommon/turbomodule/bridging (0.72.7):
413 | - DoubleConversion
414 | - glog
415 | - hermes-engine
416 | - RCT-Folly (= 2021.07.22.00)
417 | - React-callinvoker (= 0.72.7)
418 | - React-cxxreact (= 0.72.7)
419 | - React-jsi (= 0.72.7)
420 | - React-logger (= 0.72.7)
421 | - React-perflogger (= 0.72.7)
422 | - ReactCommon/turbomodule/core (0.72.7):
423 | - DoubleConversion
424 | - glog
425 | - hermes-engine
426 | - RCT-Folly (= 2021.07.22.00)
427 | - React-callinvoker (= 0.72.7)
428 | - React-cxxreact (= 0.72.7)
429 | - React-jsi (= 0.72.7)
430 | - React-logger (= 0.72.7)
431 | - React-perflogger (= 0.72.7)
432 | - RNReanimated (3.5.4):
433 | - DoubleConversion
434 | - FBLazyVector
435 | - glog
436 | - hermes-engine
437 | - RCT-Folly
438 | - RCTRequired
439 | - RCTTypeSafety
440 | - React-callinvoker
441 | - React-Core
442 | - React-Core/DevSupport
443 | - React-Core/RCTWebSocket
444 | - React-CoreModules
445 | - React-cxxreact
446 | - React-hermes
447 | - React-jsi
448 | - React-jsiexecutor
449 | - React-jsinspector
450 | - React-RCTActionSheet
451 | - React-RCTAnimation
452 | - React-RCTAppDelegate
453 | - React-RCTBlob
454 | - React-RCTImage
455 | - React-RCTLinking
456 | - React-RCTNetwork
457 | - React-RCTSettings
458 | - React-RCTText
459 | - ReactCommon/turbomodule/core
460 | - Yoga
461 | - RNScreens (3.27.0):
462 | - RCT-Folly (= 2021.07.22.00)
463 | - React-Core
464 | - SocketRocket (0.6.1)
465 | - Yoga (1.14.0)
466 |
467 | DEPENDENCIES:
468 | - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
469 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
470 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
471 | - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
472 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
473 | - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
474 | - libevent (~> 2.1.12)
475 | - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
476 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
477 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
478 | - React (from `../node_modules/react-native/`)
479 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
480 | - React-Codegen (from `build/generated/ios`)
481 | - React-Core (from `../node_modules/react-native/`)
482 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
483 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
484 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
485 | - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`)
486 | - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
487 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
488 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
489 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
490 | - React-logger (from `../node_modules/react-native/ReactCommon/logger`)
491 | - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
492 | - "react-native-unity (from `../node_modules/@azesmway/react-native-unity`)"
493 | - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
494 | - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
495 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
496 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
497 | - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
498 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
499 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
500 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
501 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
502 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
503 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
504 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
505 | - React-rncore (from `../node_modules/react-native/ReactCommon`)
506 | - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
507 | - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
508 | - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
509 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
510 | - RNReanimated (from `../node_modules/react-native-reanimated`)
511 | - RNScreens (from `../node_modules/react-native-screens`)
512 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
513 |
514 | SPEC REPOS:
515 | trunk:
516 | - fmt
517 | - libevent
518 | - SocketRocket
519 |
520 | EXTERNAL SOURCES:
521 | boost:
522 | :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
523 | DoubleConversion:
524 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
525 | FBLazyVector:
526 | :path: "../node_modules/react-native/Libraries/FBLazyVector"
527 | FBReactNativeSpec:
528 | :path: "../node_modules/react-native/React/FBReactNativeSpec"
529 | glog:
530 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
531 | hermes-engine:
532 | :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
533 | :tag: hermes-2023-08-07-RNv0.72.4-813b2def12bc9df02654b3e3653ae4a68d0572e0
534 | RCT-Folly:
535 | :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
536 | RCTRequired:
537 | :path: "../node_modules/react-native/Libraries/RCTRequired"
538 | RCTTypeSafety:
539 | :path: "../node_modules/react-native/Libraries/TypeSafety"
540 | React:
541 | :path: "../node_modules/react-native/"
542 | React-callinvoker:
543 | :path: "../node_modules/react-native/ReactCommon/callinvoker"
544 | React-Codegen:
545 | :path: build/generated/ios
546 | React-Core:
547 | :path: "../node_modules/react-native/"
548 | React-CoreModules:
549 | :path: "../node_modules/react-native/React/CoreModules"
550 | React-cxxreact:
551 | :path: "../node_modules/react-native/ReactCommon/cxxreact"
552 | React-debug:
553 | :path: "../node_modules/react-native/ReactCommon/react/debug"
554 | React-hermes:
555 | :path: "../node_modules/react-native/ReactCommon/hermes"
556 | React-jsi:
557 | :path: "../node_modules/react-native/ReactCommon/jsi"
558 | React-jsiexecutor:
559 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
560 | React-jsinspector:
561 | :path: "../node_modules/react-native/ReactCommon/jsinspector"
562 | React-logger:
563 | :path: "../node_modules/react-native/ReactCommon/logger"
564 | react-native-safe-area-context:
565 | :path: "../node_modules/react-native-safe-area-context"
566 | react-native-unity:
567 | :path: "../node_modules/@azesmway/react-native-unity"
568 | React-NativeModulesApple:
569 | :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
570 | React-perflogger:
571 | :path: "../node_modules/react-native/ReactCommon/reactperflogger"
572 | React-RCTActionSheet:
573 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
574 | React-RCTAnimation:
575 | :path: "../node_modules/react-native/Libraries/NativeAnimation"
576 | React-RCTAppDelegate:
577 | :path: "../node_modules/react-native/Libraries/AppDelegate"
578 | React-RCTBlob:
579 | :path: "../node_modules/react-native/Libraries/Blob"
580 | React-RCTImage:
581 | :path: "../node_modules/react-native/Libraries/Image"
582 | React-RCTLinking:
583 | :path: "../node_modules/react-native/Libraries/LinkingIOS"
584 | React-RCTNetwork:
585 | :path: "../node_modules/react-native/Libraries/Network"
586 | React-RCTSettings:
587 | :path: "../node_modules/react-native/Libraries/Settings"
588 | React-RCTText:
589 | :path: "../node_modules/react-native/Libraries/Text"
590 | React-RCTVibration:
591 | :path: "../node_modules/react-native/Libraries/Vibration"
592 | React-rncore:
593 | :path: "../node_modules/react-native/ReactCommon"
594 | React-runtimeexecutor:
595 | :path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
596 | React-runtimescheduler:
597 | :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler"
598 | React-utils:
599 | :path: "../node_modules/react-native/ReactCommon/react/utils"
600 | ReactCommon:
601 | :path: "../node_modules/react-native/ReactCommon"
602 | RNReanimated:
603 | :path: "../node_modules/react-native-reanimated"
604 | RNScreens:
605 | :path: "../node_modules/react-native-screens"
606 | Yoga:
607 | :path: "../node_modules/react-native/ReactCommon/yoga"
608 |
609 | SPEC CHECKSUMS:
610 | boost: 57d2868c099736d80fcd648bf211b4431e51a558
611 | DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
612 | FBLazyVector: 5fbbff1d7734827299274638deb8ba3024f6c597
613 | FBReactNativeSpec: 638095fe8a01506634d77b260ef8a322019ac671
614 | fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
615 | glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
616 | hermes-engine: 9180d43df05c1ed658a87cc733dc3044cf90c00a
617 | libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
618 | RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
619 | RCTRequired: 83bca1c184feb4d2e51c72c8369b83d641443f95
620 | RCTTypeSafety: 13c4a87a16d7db6cd66006ce9759f073402ef85b
621 | React: e67aa9f99957c7611c392b5e49355d877d6525e2
622 | React-callinvoker: 2790c09d964c2e5404b5410cde91b152e3746b7b
623 | React-Codegen: e6e05e105ca7cdb990f4d609985a2a689d8d0653
624 | React-Core: 9283f1e7d0d5e3d33ad298547547b1b43912534c
625 | React-CoreModules: 6312c9b2fec4329d9ae6a2b8c350032d1664c51b
626 | React-cxxreact: 7da72565656c8ac7f97c9a031d0b199bbdec0640
627 | React-debug: 4accb2b9dc09b575206d2c42f4082990a52ae436
628 | React-hermes: 1299a94f255f59a72d5baa54a2ca2e1eee104947
629 | React-jsi: 2208de64c3a41714ac04e86975386fc49116ea13
630 | React-jsiexecutor: c49502e5d02112247ee4526bc3ccfc891ae3eb9b
631 | React-jsinspector: 8baadae51f01d867c3921213a25ab78ab4fbcd91
632 | React-logger: 8edc785c47c8686c7962199a307015e2ce9a0e4f
633 | react-native-safe-area-context: 2cd91d532de12acdb0a9cbc8d43ac72a8e4c897c
634 | react-native-unity: 1f1d89026c30330545b146123b7a7a0ffe7a3843
635 | React-NativeModulesApple: b6868ee904013a7923128892ee4a032498a1024a
636 | React-perflogger: 31ea61077185eb1428baf60c0db6e2886f141a5a
637 | React-RCTActionSheet: 392090a3abc8992eb269ef0eaa561750588fc39d
638 | React-RCTAnimation: 4b3cc6a29474bc0d78c4f04b52ab59bf760e8a9b
639 | React-RCTAppDelegate: 89b015b29885109addcabecdf3b2e833905437c7
640 | React-RCTBlob: 3e23dcbe6638897b5605e46d0d62955d78e8d27b
641 | React-RCTImage: 8a5d339d614a90a183fc1b8b6a7eb44e2e703943
642 | React-RCTLinking: b37dfbf646d77c326f9eae094b1fcd575b1c24c7
643 | React-RCTNetwork: 8bed9b2461c7d8a7d14e63df9b16181c448beebc
644 | React-RCTSettings: 506a5f09a455123a8873801b70aa7b4010b76b01
645 | React-RCTText: 3c71ecaad8ee010b79632ea2590f86c02f5cce17
646 | React-RCTVibration: d1b78ca38f61ea4b3e9ebb2ddbd0b5662631d99b
647 | React-rncore: bfc2f6568b6fecbae6f2f774e95c60c3c9e95bf2
648 | React-runtimeexecutor: 47b0a2d5bbb416db65ef881a6f7bdcfefa0001ab
649 | React-runtimescheduler: 7649c3b46c8dee1853691ecf60146a16ae59253c
650 | React-utils: 56838edeaaf651220d1e53cd0b8934fb8ce68415
651 | ReactCommon: 5f704096ccf7733b390f59043b6fa9cc180ee4f6
652 | RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
653 | RNScreens: 3c2d122f5e08c192e254c510b212306da97d2581
654 | SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
655 | Yoga: 4c3aa327e4a6a23eeacd71f61c81df1bcdf677d5
656 |
657 | PODFILE CHECKSUM: b8f41d35042dd0b300ea17543a9f95b281f136ae
658 |
659 | COCOAPODS: 1.15.2
660 |
--------------------------------------------------------------------------------