将React Native集成到Xamarin项目中

我的任务是查看是否可以将React Native集成到Xamarin.Forms项目中.
我认为我已经非常接近实现这一点,但我不能肯定地说.我知道这是一个奇怪/倒退的解决方案,但无论如何我还想去看看它是否能击败它……

介绍
我的雇主想要看看是否可以使用React Native for UI并使用C#作为业务逻辑.它正在作为一种解决方案进行探索,以便UI / UX团队可以与RN合作,我们(开发团队)可以将逻辑链接到它.

到目前为止我尝试过的
我接受了React Native输出的Xcode项目,并通过cd’ing终端删除了本地Node服务的依赖性到项目目录并运行react-native bundle –entry-file index.ios.js –platform ios – -dev false –bundle-output ios / main.jsbundle –assets-dest ios(取自this篇博文).然后我更改了AppDelegate行,它正在寻找main.jsbundle文件.
然后我添加了一个静态库作为项目的目标.与应用程序的构建阶段相比,我添加了所有相同的链接库
在此之后,我创建了一个Xamarin.Forms解决方案.因为我只创建了iOS库,所以我创建了一个iOS.Binding项目.我添加了Xcode .a lib作为本机引用.在ApiDefinition.cs文件中,我使用以下代码创建了接口

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

在Xcode项目中,创建了一个TheViewController类. setMainViewController:以下列方式实现:

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

我有效地试图从Xamarin传递一个UIViewController来为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编译器进入我的lib并尝试做它的魔法并在React Native项目中抛出许多链接器错误,如下所示.


Pastebin dump of full Build Output

我打算在绑定中设置更多方法来设置回调等,以开始构建一些关于使用Objective-C来回传递信息的功能,我将使用一些本机代码链接将其传递给React Native.

摘要
我知道它已经很长时间了,但如果我们能够实现这一点,那么我们基本上可以在C#中完成所有(相当复杂的)业务逻辑,并将所有UI更改留给专门的UI团队,他们有强烈的偏好对于React Native(足够公平,他们的原型状况非常好).真的,这只是我为应用程序的下一个主要版本组装的另一个POC.
如果有人能想到更好的方法,我会全力以赴.当然,我已经对一些细节进行了釉面处理,所以如果有任何需要澄清的话,请询问,我会推荐.
非常感谢.
卢克

我能够使用以下步骤来实现这一点.这里有很多,所以如果我错过了一个细节,请原谅我.

构建静态库

>在Xcode中创建一个Cocoa Touch静态库项目.
>在同一目录中安装React Native.

npm install react-native

>将所有React Xcode项目添加到项目中. (Screenshot)您可以查看现有React Native应用程序的.pbxproj文件,以获取有关如何查找所有这些内容的线索.
>将React添加到Target Dependencies构建阶段. (Screenshot)
>在Link Binary With Libraries构建阶段包含所有React目标. (Screenshot)
>确保在其他链接器标志构建设置中包含-lc.
>使用lipo创建通用库(胖文件).请参阅Xamarin documentation中的“构建通用本机库”部分.

创建一个Xamarin应用程序

>在Visual Studio中创建一个新的iOS Single View App项目/解决方案. (Screenshot)
>将iOS Bindings Library项目添加到解决方案中. (Screenshot)
>将通用静态库添加为绑定库项目的本机引用.
>在本机引用的属性中将Frameworks设置为JavaScriptCore和Linker Flags为-lstdc.这可以修复原始问题中提到的链接器错误.还可以启用强制加载. (Screenshot)
>将以下代码添加到ApiDefinition.cs.确保包含System,Foundation和UIKit的using语句.

// @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.确保包含System,System.Runtime.InteropServices和Foundation的using语句.

[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);
}

>在应用程序项目中添加对bindings库项目的引用.
>将以下代码添加到AppDelegate.cs中的FinishedLaunching方法.不要忘记为绑定库的命名空间添加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 Packager(react-native start)来运行任何React Native应用程序.以下部分将向您展示如何从React Native调用C#.

创建本机模块

>在iOS应用项目中添加课程.
>让类继承RCTBridgeModule(来自绑定库).

public class TestClass : RCTBridgeModule

>将ModuleName方法添加到您的类.将返回的值更改为您想在JavaScript中调用该类的任何值.您可以指定空字符串以使用原始字符串.

[Export("moduleName")]
public static string ModuleName() => "TestClass";

>将RequiresMainQueueSetup方法添加到您的类中.我认为如果您实现本机(UI)组件,则需要返回true.

[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签名.
>我不确定什么是同步.

>在AppDelegate.cs中启动视图之前注册您的课程.类的名称将是带有下划线而不是点的完全限定名称.这是一个例子.

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

从JavaScript调用您的本机模块

>将NativeModules导入JavaScript文件.

import { NativeModules } from 'react-native';

>调用您导出的方法之一.

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom