Reagent Integration in the Xamarin Project

I was tasked with figuring out whether React Native can be integrated into a Xamarin.Forms project.
I think I'm pretty close to that, but I can't say for sure. I know this is a bit weird / reverse solution, but I would still like to go to him to see if I can defeat him ...

Introduction
My employer wants to find out if React Native can be used for the user interface and can use C # for business logic. It is considered as a solution, so that the UI / UX team can create work with RN, and we (the development team) can connect the logic with it.

What I tried so far
I took the Xcode project that React Native pulled out and started by removing the dependency of the local Node service using the cd'ing terminal in 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). Then I made changes to the line AppDelegatewhere it searches for the main.jsbundle file.
Then I added a static library as a target for the project. Compared to the build steps of the application, I added all the same link libraries. After that I created the Xamarin.Forms solution. Since I only created the iOS library, I created the iOS.Binding project. I added Xcode.a lib as the main link. Inside the file, I created an interface with the following codeenter image description here
ApiDefinition.cs

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

Why the class is created in the Xcode project TheViewController. setMainViewController:was implemented as follows:

-(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];
}

UIViewController Xamarin React Native, . Xamarin.iOS :

private Binding.TheViewController _theViewController;

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

PageRenderer, Xamarin.Forms 'ContentPage,

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

, , , , . AOT React Native, .

enter image description here
Pastebin

.., Objective-C, React Native ,


, , , ( ) - # , React Native ( , ). , POC, .
- , . , , , , - , , , . , .

+4
2

, . , , , , .

  • Cocoa Touch Static Library Xcode.
  • React Native .

    npm install react-native
    
  • React Xcode. ( ) .pbxproj React Native, , .
  • React Target Dependencies. ( )
  • React Link Binary With Libraries. ( )
  • -lc++ .
  • lipo (fat file). . Xamarin.

Xamarin

  • iOS Single View App Visual Studio. ( )
  • iOS Bindings Library. ( )
  • .
  • JavaScriptCore -lstdc++ . , . Force Load. ( )
  • ApiDefinition.cs. using System, Foundation 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
    {
    
    }
    
  • Structs.cs. using System, System.Runtime.InteropServices 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);
    }
    
  • .
  • FinishedLaunching AppDelegate.cs. using React Native.

    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();
    
  • 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>
    

React Native, React Packager (react-native start) . , # React Native.

  • iOS.
  • RCTBridgeModule ( ).

    public class TestClass : RCTBridgeModule
    
  • ModuleName . , JavaScript. .

    [Export("moduleName")]
    public static string ModuleName() => "TestClass";
    
  • RequiresMainQueueSetup . , true, (UI) .

    [Export("requiresMainQueueSetup")]
    public static bool RequiresMainQueueSetup() => false;
    
  • , ( JavaScript). .

    [Export("test:")]
    public void Test(string msg) => Debug.WriteLine(msg);
    
  • , . __rct_export__. , . - IntPtr RCTMethodInfo. .

    [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;
    }
    
    • jsName - , JavaScript. .
    • objcName Objective-C .
    • , isSync.
  • AppDelegate.cs. . .

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

JavaScript

  • NativeModules JavaScript.

    import { NativeModules } from 'react-native';
    
  • , .

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

lib Xamarin Binding, Xamarin Binding .dll:

https://github.com/voydz/xamarin-react-native

+1

Source: https://habr.com/ru/post/1663257/


All Articles