Integrate React Native into a Xamarin project


my login

My task is to see if it is possible to integrate React Native into a Xamarin.Forms project.
I think I'm close to achieving this, but I can't say for sure. I know this is a weird/regressive solution, but I'd like to try it anyway and see if I can beat it...

Introduction
My employer wanted to see if it was possible to use React Native for UI and C# for business logic. People are exploring this as a solution so that UI/UX teams can use RN to do their work and we (the development team) can link logic to it.

Everything I've done so far has been done by
cd'ing the terminal into the project directory and running react-native bundle --entry-file index.ios.js --platform ios --dev false --bundle-output ios/main.jsbundle --assets-dest ios(taken from this blog post) , I selected the Xcode project that React Native outputs and started running . Then I made a change to the line that AppDelegatewas looking for the main.jsbundle file .
Then, I added a static library as the target of this project. Compare with the build phase of the app and then added all the same linked libraries. I have since created a Xamarin.Forms solution. As I only create iOS libraries, I create an iOS.Binding project. I added the Xcode .a lib as a native reference. In the file I have created the interface with the following codeenter image description here
ApiDefinition.cs

BaseType(typeof(NSObject))]
    interface TheViewController
    {
        [Export("setMainViewController:")]
        void SetTheMainViewController(UIViewController viewController);
    }

To which, in the Xcode project, created a TheViewController class. The setMainViewController: was implemented in the following way:

-(void)setMainViewController:(UIViewController *)viewController{

  AppDelegate * ad = (AppDelegate*)[UIApplication sharedApplication].delegate;

  NSURL * jsCodeLocation = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"main" ofType:@"jsbundle"]];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"prototyper"
                                               initialProperties:nil
                                                   launchOptions:ad.savedLaunchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  ad.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  viewController.view = rootView;
  ad.window.rootViewController = viewController;
  [ad.window makeKeyAndVisible];
}

Where I am effectively trying to pass in a UIViewController from Xamarin for the React Native stuff to add itself to.
I am calling this from Xamarin.iOS in the following way:

private Binding.TheViewController _theViewController;

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            _theViewController = new TheViewController();
            _theViewController.SetTheMainViewController(this);
        }

This class is implementing PageRenderer, overriding the Xamarin.Forms' ContentPage using

[assembly:ExportRenderer(typeof(RNTest.MainViewController), typeof(RNTest.iOS.MainViewController))]

Well, after all of this, I went to go and deploy to device and, expectedly, hit by a number of errors. The AOT compiler is going into my lib and trying to do it's magic and throws a number of linker errors in the React Native projects, as shown below.

enter image description here
Pastebin dump of full Build Output

I was intending on setting up more methods in the binding to set callbacks etc to start building some functionality regarding passing information back and forth with the Objective-C, which I was going to pass into the React Native with some native code link.

Summary
I know it's pretty long breathed, but if we can get this off the ground, then we can basically do all of our (fairly comlex) business logic in C# and leave all the UI changes to the dedicated UI team, who have a strong preference for React Native (fair enough, with their prototype being in pretty good condition). Really, it's all just another POC that I've been putting together for the next major release of our app.
If anyone can think of a better way of doing this, I am all ears. I have, of course, glazed over some of the details, so if anything needs clarifying then please ask and I will ammend.
Many, many thanks.
Luke

Kenny McClive

I was able to get this working using the steps below. There's a lot here so please forgive me if I missed a detail.

Build a Static Library

  1. Create a Cocoa Touch Static Library project in Xcode.
  2. Install React Native in the same directory.

    npm install react-native
    
  3. Add all the React Xcode projects to your project. (Screenshot) You can look at the .pbxproj file of an existing React Native app for clues on how to find all these.
  4. Add React to the Target Dependencies build phase. (Screenshot)
  5. Include all the React targets in the Link Binary With Libraries build phase. (Screenshot)
  6. Be sure to include -lc++ in the Other Linker Flags build setting.
  7. Use lipo to create a universal library (fat file). See Building Universal Native Libraries section in Xamarin documentation.

Create a Xamarin App

  1. Create a new iOS Single View App project/solution in Visual Studio. (Screenshot)
  2. Add an iOS Bindings Library project to the solution. (Screenshot)
  3. Add your universal static library as a Native Reference to the bindings library project.
  4. Set Frameworks to JavaScriptCore and Linker Flags to -lstdc++ in the properties for the native reference. This fixes the linker errors mentioned in the original question. Also enable Force Load. (Screenshot)
  5. Add the following code to ApiDefinition.cs. Be sure to include using statements for System, Foundation, and UIKit.

    // @interface RCTBundleURLProvider : NSObject
    [BaseType(typeof(NSObject))]
    interface RCTBundleURLProvider
    {
        // +(instancetype)sharedSettings;
        [Static]
        [Export("sharedSettings")]
        RCTBundleURLProvider SharedSettings();
    
        // -(NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackResource:(NSString *)resourceName;
        [Export("jsBundleURLForBundleRoot:fallbackResource:")]
        NSUrl JsBundleURLForBundleRoot(string bundleRoot, [NullAllowed] string resourceName);
    }
    
    // @interface RCTRootView : UIView
    [BaseType(typeof(UIView))]
    interface RCTRootView
    {
        // -(instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties launchOptions:(NSDictionary *)launchOptions;
        [Export("initWithBundleURL:moduleName:initialProperties:launchOptions:")]
        IntPtr Constructor(NSUrl bundleURL, string moduleName, [NullAllowed] NSDictionary initialProperties, [NullAllowed] NSDictionary launchOptions);
    }
    
    // @protocol RCTBridgeModule <NSObject>
    [Protocol, Model]
    [BaseType(typeof(NSObject))]
    interface RCTBridgeModule
    {
    
    }
    
  6. Add the following code to Structs.cs. Be sure to include using statements for System, System.Runtime.InteropServices, and Foundation.

    [StructLayout(LayoutKind.Sequential)]
    public struct RCTMethodInfo
    {
        public string jsName;
        public string objcName;
        public bool isSync;
    }
    
    public static class CFunctions
    {
        [DllImport ("__Internal")]
        public static extern void RCTRegisterModule(IntPtr module);
    }
    
  7. Add a reference to the bindings library project in the app project.
  8. Add the following code to the FinishedLaunching method in AppDelegate.cs. Don't forget to add a using statement for the namespace of your bindings library and specify the name of your React Native app.

    var jsCodeLocation = RCTBundleURLProvider.SharedSettings().JsBundleURLForBundleRoot("index", null);
    var rootView = new RCTRootView(jsCodeLocation, "<Name of your React app>", null, launchOptions);
    
    Window = new UIWindow(UIScreen.MainScreen.Bounds);
    Window.RootViewController = new UIViewController() { View = rootView };
    Window.MakeKeyAndVisible();
    
  9. Add the following to Info.plist.

    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>localhost</key>
            <dict>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>
    

At this point, you should be able to run any React Native app by launching the React Packager (react-native start) in the corresponding directory. The following sections will show you how to call C# from React Native.

Create a Native Module

  1. Add a class to your iOS app project.
  2. Have the class inherit RCTBridgeModule (from your bindings library).

    public class TestClass : RCTBridgeModule
    
  3. Add the ModuleName method to your class. Change the value returned to whatever you want to call the class in JavaScript. You can specify empty string to use the original.

    [Export("moduleName")]
    public static string ModuleName() => "TestClass";
    
  4. Add the RequiresMainQueueSetup method to your class. I think this will need to return true if you implement a native (UI) component.

    [Export("requiresMainQueueSetup")]
    public static bool RequiresMainQueueSetup() => false;
    
  5. Write the method that you want to export (call from JavaScript). Here is an example.

    [Export("test:")]
    public void Test(string msg) => Debug.WriteLine(msg);
    
  6. For each method that you export, write an additional method that returns information about it. The names of each of these methods will need to start with __rct_export__. The rest of the name doesn't matter as long as it is unique. The only way I could find to get this to work was to return an IntPtr instead of an RCTMethodInfo. Below is an example.

    [Export("__rct_export__test")]
    public static IntPtr TestExport()
    {
        var temp = new RCTMethodInfo()
        {
            jsName = string.Empty,
            objcName = "test: (NSString*) msg",
            isSync = false
        };
        var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(temp));
    
        Marshal.StructureToPtr(temp, ptr, false);
    
        return ptr;
    }
    
    • jsNameis the name you want to call the method from JavaScript. You can specify empty string to use the original.
    • objcName is the equivalent Objective-C signature of the method.
    • I'm not sure what isSync is.
  7. Please register your class before launching the view in AppDelegate.cs. The name of the class will be the fully qualified name with underscores instead of dots. Here is an example.

    CFunctions.RCTRegisterModule(ObjCRuntime.Class.GetHandle("ReactTest_TestClass"));
    

Calling native modules from JavaScript

  1. Import NativeModulesyour JavaScript file.

    import { NativeModules } from 'react-native';
    
  2. Call one of your exported methods.

    NativeModules.TestClass.test('C# called successfully.');
    

Related


How to integrate React-Native project to Android-Studio?

Diwan Dhanvani: I have tried importing the react-native project into android studio, but the "index.android.js" file is not showing up in the android-studio IDE. So what should I do? Santosh Anand: You just need to import the "android" folder.

Integrate React Native app with Contacts

Wiliam Brochensque Junior: I'm making a multi-platform VOIP dialer using react-native and I'm facing difficulties integrating my app with the OS's default contact list (Android in the example below) I haven't tried anything yet because I know neither Java (for

Integrate React Native into a Xamarin project

my login My task is to see if it is possible to integrate React Native into a Xamarin.Forms project. I think I'm close to achieving this, but I can't say for sure. I know this is a weird/regressive solution, but I'd like to try it anyway and see if I can beat

How to integrate React Native into an existing iOS project

huoxinbird Great to see Facebook open source React Native at F8 2015. Although they offer react-native-clito generate an empty React Native project, I still don't know how to integrate React Native into my current project using Xcode workspace and CocoaPods. B

How to integrate multilingual in React Native project

Ibrahim Bashipur I want to develop an app written in local language and support multiple languages like English, Turkish... What advice do you have for me? Sateesh yemireddi The ability to add multi-language support with react-native-localize . You can use rea

Integrate android project into React Native

ramaraju I'm new to React Native...I have an android project developed and now I want to integrate it into React Native. How do I import or migrate or integrate it into React Native? Any help…………! i.e. if I run react-native run-androidin command prompt I have

Integrate React Native app with Contacts

Wiliam Brochensque Junior: I'm making a multi-platform VOIP dialer using react-native and I'm facing difficulties integrating my app with the OS's default contact list (Android in the example below) I haven't tried anything yet because I know neither Java (for

Integrate React Native app with Contacts

Wiliam Brochensque Junior: I'm making a multi-platform VOIP dialer using react-native and I'm facing difficulties integrating my app with the OS's default contact list (Android in the example below) I haven't tried anything yet because I know neither Java (for

Integrate React Native app with Contacts

Wiliam Brochensque Junior: I'm making a multi-platform VOIP dialer using react-native and I'm facing difficulties integrating my app with the OS's default contact list (Android in the example below) I haven't tried anything yet because I know neither Java (for

How to integrate React-Native project to Android-Studio?

Diwan Dhanvani: I have tried importing the react-native project into android studio, but the "index.android.js" file is not showing up in the android-studio IDE. So what should I do? Santosh Anand: You just need to import the "android" folder.

Integrate Boilerplate Ignite with React Native

Sabrina I'm trying to integrate Boilerplate with react Native on Ubuntu, but somehow I'm stuck. For the web: I cloned a repository from git: Repository Moving to the clone directory, I've installed the npm command to install MongoDB by downloading and unzippin

Integrate native UI into react native?

fake account I'm using native Android ProgressBarin react native but some errors occur. Here is my ProgressBarViewManagercode: public class ProgressBarViewManager extends SimpleViewManager<ProgressBar> { public static final String REACT_CLASS = "ProgressBar"

Integrate React Native into a Xamarin project

my login My task is to see if it is possible to integrate React Native into a Xamarin.Forms project. I think I'm close to achieving this, but I can't say for sure. I know this is a weird/regressive solution, but I'd like to try it anyway and see if I can beat

Integrate React Native into a Xamarin project

my login My task is to see if it is possible to integrate React Native into a Xamarin.Forms project. I think I'm close to achieving this, but I can't say for sure. I know this is a weird/regressive solution, but I'd like to try it anyway and see if I can beat

How to integrate orientation in react native

Samantha Jakumar Currently, I'm working on a native android application because I need to do orientation actions, which means in portrait view or landscape view UI needs to be fixed to the device screen (flexible). I use windows.Dimension to resize the compone

How to integrate multilingual in React Native project

Ibrahim Bashipur I want to develop an app written in local language and support multiple languages like english, turkish... what advice do you have for me? Sateesh yemireddi The ability to add multi-language support with react-native-localize . You can use rea

How to integrate orientation in react native

Samantha Jeeyakumar Currently I'm working on a native android application as I need to perform orientation actions which means in portrait view or landscape view UI needs to be pinned into the device screen (flexible). I use windows.Dimension to resize the com

How to integrate React Native into an existing iOS project

huoxinbird Great to see Facebook open source React Native at F8 2015. Although they offer react-native-clito generate an empty React Native project, I still don't know how to integrate React Native into my current project using Xcode workspace and CocoaPods. B

How to integrate React-Native project to Android-Studio?

Diwan Dhanvani: I have tried importing the react-native project into android studio, but the "index.android.js" file is not showing up in the android-studio IDE. So what should I do? Santosh Anand: You just need to import the "android" folder.

Integrate React Native into a Xamarin project

my login My task is to see if it is possible to integrate React Native into a Xamarin.Forms project. I think I'm close to achieving this, but I can't say for sure. I know this is a weird/regressive solution, but I'd like to try it anyway and see if I can beat

How to integrate multilingual in React Native project

Ibrahim Bashipur I want to develop an app written in local language and support multiple languages like english, turkish... what advice do you have for me? Sateesh yemireddi The ability to add multi-language support with react-native-localize . You can use rea

Integrate React Native into a Xamarin project

my login My task is to see if it is possible to integrate React Native into a Xamarin.Forms project. I think I'm close to achieving this, but I can't say for sure. I know this is a weird/regressive solution, but I'd like to try it anyway and see if I can beat