myl*_*gon 7 xamarin react-native
我的任务是查看是否可以将React Native集成到Xamarin.Forms项目中.
我认为我已经非常接近实现这一点,但我不能肯定地说.我知道这是一个奇怪/倒退的解决方案,但无论如何我还想去看看它是否能击败它......
简介
我的雇主想要看看是否可以使用React Native for UI并使用C#作为业务逻辑.它正在作为一种解决方案进行探索,以便UI/UX团队可以与RN合作,我们(开发团队)可以将逻辑链接到它.
到目前为止,我已经尝试
了通过cd'ing终端删除本地节点服务的依赖性并运行react-native bundle --entry-file index.ios.js --platform ios --dev false --bundle-output ios/main.jsbundle --assets-dest ios(从这篇博客文章中获取)的React Native输出和启动的Xcode项目.然后我更改了AppDelegate它正在寻找main.jsbundle文件的行.
然后我添加了一个静态库作为项目的目标.与应用程序的构建阶段相比,我添加了所有相同的链接库
在此之后,我创建了一个Xamarin.Forms解决方案.因为我只创建了iOS库,所以我创建了一个iOS.Binding项目.我添加了Xcode .a lib作为本机引用.在ApiDefinition.cs文件中,我使用以下代码创建了接口
BaseType(typeof(NSObject))]
interface TheViewController
{
[Export("setMainViewController:")]
void SetTheMainViewController(UIViewController viewController);
}
Run Code Online (Sandbox Code Playgroud)
在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];
}
Run Code Online (Sandbox Code Playgroud)
我有效地尝试UIViewController从Xamarin 传递React Native的东西来添加自己.
我用以下方式从Xamarin.iOS中调用它:
private Binding.TheViewController _theViewController;
public override void ViewDidLoad()
{
base.ViewDidLoad();
_theViewController = new TheViewController();
_theViewController.SetTheMainViewController(this);
}
Run Code Online (Sandbox Code Playgroud)
这个类正在实现PageRenderer,覆盖了Xamarin.Forms的ContentPage使用
[assembly:ExportRenderer(typeof(RNTest.MainViewController), typeof(RNTest.iOS.MainViewController))]
Run Code Online (Sandbox Code Playgroud)
好吧,经过所有这些,我去了部署到设备,并且,预计会遇到一些错误.AOT编译器进入我的lib并尝试做它的魔法并在React Native项目中抛出许多链接器错误,如下所示.
我打算在绑定中设置更多方法来设置回调等,以开始构建一些关于使用Objective-C来回传递信息的功能,我将使用一些本机代码链接将其传递给React Native.
总结
我知道它已经很长时间了,但如果我们能够实现这一目标,那么我们基本上可以在C#中完成所有(相当复杂的)业务逻辑,并将所有UI更改留给专门的UI团队,他们拥有强大的对React Native的偏好(公平,其原型状况非常好).真的,这只是我为应用程序的下一个主要版本组装的另一个POC.
如果有人能想到更好的方法,我会全力以赴.当然,我已经对一些细节进行了釉面处理,所以如果有任何需要澄清的话,请询问,我会推荐.
非常感谢.
卢克
我能够使用以下步骤来实现这一点.这里有很多,所以如果我错过了一个细节,请原谅我.
在同一目录中安装React Native.
npm install react-native
Run Code Online (Sandbox Code Playgroud)-lc++在Other Linker Flags构建设置中.JavaScriptCore和链接器标记,以-lstdc++在本地参考属性. 这可以修复原始问题中提到的链接器错误.还可以启用强制加载.(截图)将以下代码添加到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
{
}
Run Code Online (Sandbox Code Playgroud)将以下代码添加到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);
}
Run Code Online (Sandbox Code Playgroud)将以下代码添加到FinishedLaunchingAppDelegate.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();
Run Code Online (Sandbox Code Playgroud)将以下内容添加到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>
Run Code Online (Sandbox Code Playgroud)此时,您应该能够通过react-native start在相应目录中启动React Packager()来运行任何React Native应用程序.以下部分将向您展示如何从React Native调用C#.
让类继承RCTBridgeModule(来自绑定库).
public class TestClass : RCTBridgeModule
Run Code Online (Sandbox Code Playgroud)将ModuleName方法添加到您的班级.将返回的值更改为您想在JavaScript中调用该类的任何值.您可以指定空字符串以使用原始字符串.
[Export("moduleName")]
public static string ModuleName() => "TestClass";
Run Code Online (Sandbox Code Playgroud)将RequiresMainQueueSetup方法添加到您的班级.我认为true如果您实现本机(UI)组件,则需要返回.
[Export("requiresMainQueueSetup")]
public static bool RequiresMainQueueSetup() => false;
Run Code Online (Sandbox Code Playgroud)编写要导出的方法(从JavaScript调用).这是一个例子.
[Export("test:")]
public void Test(string msg) => Debug.WriteLine(msg);
Run Code Online (Sandbox Code Playgroud)对于导出的每个方法,请编写一个返回有关它的信息的其他方法.每个方法的名称都需要从一开始__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;
}
Run Code Online (Sandbox Code Playgroud)
jsName是您要从JavaScript调用方法的名称.您可以指定空字符串以使用原始字符串.objcName是该方法的等效Objective-C签名.isSync.在AppDelegate.cs中启动视图之前注册您的类.类的名称将是带有下划线而不是点的完全限定名称.这是一个例子.
CFunctions.RCTRegisterModule(ObjCRuntime.Class.GetHandle("ReactTest_TestClass"));
Run Code Online (Sandbox Code Playgroud)导入NativeModules您的JavaScript文件.
import { NativeModules } from 'react-native';
Run Code Online (Sandbox Code Playgroud)调用您导出的方法之一.
NativeModules.TestClass.test('C# called successfully.');
Run Code Online (Sandbox Code Playgroud)| 归档时间: |
|
| 查看次数: |
1621 次 |
| 最近记录: |