jingliancui / ReactNativeDotNetSampleApp

0 stars 0 forks source link

How to implement Xamarin with React native using native module? #1

Open abrahamj1-els-ct opened 1 year ago

abrahamj1-els-ct commented 1 year ago

Hi,

I am trying to run the similar thing which you are doing with webview. I am following this project https://github.com/voydz/xamarin-react-native in this they are using your Nuget package Com.Facebook.React. But it is not working with latest React-Native version 0.71.4. In the Github project they are using React-Native version 0.55.3 So I had to downgrade my node to make it work which is not feasible for production. Is there any project or any new release of your Com.Facebook.React package?

jingliancui commented 1 year ago

Hi I just glance on the repo link you shared, I can't find any reference to the packages that created by myself. But interesting that can you tell me about which scenarios you are focussing on by using this package now?

abrahamj1-els-ct commented 1 year ago

Hi, I am working in a scenario where I'm trying to create a bridge between Xamarin Android and React Native. So I'm using Com.Facebook.React package for creating bridge. In that I'm using Native Module to call a method in C# class of Xamarin Android in Index.js file of React Native. Following is my C# class

using Com.Facebook.React.Bridge;
using Com.Facebook.React.Uimanager;
using Com.Facebook.React;
using Java.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Android.Widget;
using Android.Content;

namespace ReactNative.Droid
{
    public class NativePackage : Java.Lang.Object, IReactPackage
    {
        public IList<INativeModule> CreateNativeModules(ReactApplicationContext context)
        {
            var module = new NativeModule(context);
            var list = new List<INativeModule> { module };
            Console.WriteLine("Hello CreateNativeModules");
            return list;
        }

        public IList<ViewManager> CreateViewManagers(ReactApplicationContext context)
        {
            Console.WriteLine("Hello CreateViewManagers");
            return new List<ViewManager> { };
        }
    }

    public class NativeModule : ReactContextBaseJavaModule
    {
        public NativeModule(ReactApplicationContext reactContext) : base(reactContext) 
        {
            Console.WriteLine("Hello NativeModule");
        }

        public override string Name => "XNativeModule";
        [Export("Foo")]
        [ReactMethod]
        public void Foo()
        {
            Console.WriteLine("Hello Native World");
            Toast.MakeText(ReactApplicationContext, "Hello Native World", ToastLength.Long).Show();
        }
    }
}

Following is my Index.js code

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  Platform,
  StyleSheet,
  Text, Button,
  View,NativeModules 
} from 'react-native';
console.log(NativeModules);
const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload this is part of React,\n' +
    'Shake or press menu button for dev menu',
});
const {XNativeModule}=NativeModules;
const RNHelloWorld=()=> {

    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Hey there Judson! This is React native project
        </Text>
        <Text style={styles.instructions}>
          To get started, edit App.js
        </Text>
        <Text style={styles.instructions}>
          {instructions}
        </Text>
        <Button onPress={() => XNativeModule.Foo()} title="Call Native Module" />
      </View>
    );
  }

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

// Module name
AppRegistry.registerComponent('MyReactNativeApp', () => RNHelloWorld);

When I am calling foo method I'm getting error undefined is not an object (evaluating 'u.Foo')

This much I made it work after downgrading node from latest 18.8.0 to 9.11.1. I want it to work in latest Node and it should also support Latest React Native version.

jingliancui commented 1 year ago

Thanks, I'm curious about how react native invoke the xamarin.droid part. As I know the part of droid should be generated to .dll file instead of aar file. So could you please help to figure it out?

abrahamj1-els-ct commented 1 year ago

When I call XNativeModule.Foo() from my JavaScript code, React Native bridges the call to the native module by sending a message to the native side using a message queue. On the native side, the Xamarin runtime receives the message and routes it to the appropriate native module. In this case, it will call the Foo() method defined in the XNativeModule class. In Xamarin.Android, the ReactContextBaseJavaModule class is used to create a native module that can be called from the React Native JavaScript code.

jingliancui commented 1 year ago

Thanks, I mean, does react native reference the dll? If so, any docs to describe how react native config this stuff, of just import that dll files with same way as import the aar file?

abrahamj1-els-ct commented 1 year ago

I think in that project React-Native-0.55.3 is being compiled in ReactNative.Droid project. And ReactNative.Droid is referenced in SampleApp.Droid project. And we are getting the output from index.android.bundle which is exported from ReactNative.Droid and it contains the changes of Index.js. This is my MainActivity code

    [Activity(Label = "XamarinReactNative", MainLauncher = true, Icon = "@mipmap/icon", Theme = "@style/Theme.AppCompat.Light.NoActionBar")]
    public class MainActivity : AppCompatActivity, IDefaultHardwareBackBtnHandler
    {
        ReactRootView mReactRootView;

        ReactInstanceManager mReactInstanceManager;

        private const int OVERLAY_PERMISSION_REQ_CODE = 1;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                if (!Settings.CanDrawOverlays(this))
                {
                    Intent intent = new Intent(Settings.ActionManageOverlayPermission, Uri.Parse("package:" + PackageName));
                    StartActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
                }
            }

            mReactRootView = new ReactRootView(this);
            mReactInstanceManager = ReactInstanceManager.Builder()
                    .SetApplication(Application)
                    .SetBundleAssetName("index.android.bundle")
                    .SetJSMainModulePath("index")
                    .AddPackage(new MainReactPackage())
#if DEBUG
                    .SetUseDeveloperSupport(true)
#else
                    .SetUseDeveloperSupport(false)
#endif
                    .SetInitialLifecycleState(LifecycleState.Resumed)
                    .Build();

            mReactRootView.StartReactApplication(mReactInstanceManager, "MyReactNativeApp", null);

            SetContentView(mReactRootView);
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            if (requestCode == OVERLAY_PERMISSION_REQ_CODE)
            {
                if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
                {
                    if (!Settings.CanDrawOverlays(this))
                    {
                        // SYSTEM_ALERT_WINDOW permission not granted...
                    }
                }
            }
        }

        protected override void OnPause()
        {
            base.OnPause();

            if (mReactInstanceManager != null)
            {
                mReactInstanceManager.OnHostPause(this);
            }
        }

        protected override void OnResume()
        {
            base.OnResume();

            if (mReactInstanceManager != null)
            {
                mReactInstanceManager.OnHostResume(this, this);
            }
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();

            if (mReactInstanceManager != null)
            {
                mReactInstanceManager.OnHostDestroy(this);
            }
        }

        public override void OnBackPressed()
        {
            if (mReactInstanceManager != null)
            {
                mReactInstanceManager.OnBackPressed();
            }
            else
            {
                base.OnBackPressed();
            }
        }

        public void InvokeDefaultOnBackPressed()
        {
            base.OnBackPressed();
        }

        public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
        {
            if (keyCode == Keycode.Menu && mReactInstanceManager != null)
            {
                mReactInstanceManager.ShowDevOptionsDialog();
                return true;
            }

            return base.OnKeyUp(keyCode, e);
        }
    }

So I don't know how the react native reference the DLL? we are referencing ReactNative.Droid.dll in SampleApp Project.

jingliancui commented 1 year ago

Got it, so you would like to use xamarin/maui reference reactnative, instead of using reactnative reference xamarin/maui. Because I got the meaning from the mail written by John is using reactnative reference xamarin/maui.

abrahamj1-els-ct commented 1 year ago

Yes your right. We are trying to achieve what John has mentioned.

jingliancui commented 1 year ago

Hi @abrahamj1-els-ct , I've finished the binding work, but I haven't been do any testing or integration at this time, so I create this repo as the testing Sample, you can raise any pr to merge to this sample code as you want.