美文网首页React Native
iOS 已有项目利用Pod集成RN

iOS 已有项目利用Pod集成RN

作者: All_Be_Alright | 来源:发表于2021-07-31 10:38 被阅读0次

    一、背景

    对于已经存在的iOS项目,以模块化引入,OC与RN混编怎么做呢?

    我们可以利用cocopods来集成,直接使用pod install就可以让其他同事也快速集成。

    由于RN用npx react-native init NewProject 命令创建的新项目,外层是RN的代码,里面iOS文件夹下才是我们的工程代码,这样显得很杂乱,所以我的想法是在同级目录下创建两个文件夹iOSCode和RNCode,一个放原生的代码,另外一个放RN代码,这样会比较清晰。

    目录结构

    二、操作

    1、环境

    "react": "16.13.1",
    "react-native": "0.63.4",
    

    2、ruby脚本

    pod工具就是通过ruby语言编写的,所以我们可以插入ruby脚本来做一些自动化的操作。 在iOS工程目录下创建ruby脚本文件 Podfile_ReactNative.rb

    # 定义一个函数,在 Podfile文件中调用此函数即可
    def installReactNativeSdk()
    
        # 设置 react_native_pods.rb 文件路径
        node_mudle_pod_file = "../RNCode/node_modules/react-native/scripts/react_native_pods.rb"
    
        # 判断该文件是否存在,如果已经存在,表示RN环境已经配置,如果没有存在表示RN环境还未集成到项目
        if File.exist?(node_mudle_pod_file)
            Pod::UI.puts "\nReactNative 环境已存在!\n\n"
            # 修改路径
            # changePath(node_mudle_pod_file)
        else
            Pod::UI.puts "ReactNative 环境不存在,准备下载···"
            # 判断是否安装 node环境
            if system "node -v > /dev/null"
                # 切换目录到../RNCode/
                if Dir.chdir("../RNCode/")
                    # 使用 yarn 或 npm 下载依赖
                    if system "yarn install || npm install"
                        Pod::UI.puts "ReactNative 环境安装成功!\n\n"
                        # 修改路径
                        changePath(node_mudle_pod_file)
                    else
                        Pod::UI.puts "ReactNative 环境安装失败!请安装yarn,在命令行执行:npm install -g yarn"
                        Kernel.exit(false)
                    end
                else
                    Pod::UI.puts "不存在RNCode文件夹"
                    Kernel.exit(false)
                end
            else
                #如果没有安装,提示自行安装node环境
                Pod::UI.puts "环境下载失败!请先安装node环境,详细见:https://reactnative.cn/docs/environment-setup"
                Kernel.exit(false)
            end
        end
    end
    
    # 这个函数将 react_native_pods.rb 文件中 ../node_modules 目录改为当前目录 ./RNCode/node_modules
    def changePath(node_mudle_pod_file) 
        configString = ""
        File.open(node_mudle_pod_file, "r+") {|f|
            configString = f.read.gsub(/..\/node_modules/,"../RNCode/node_modules")
        }
        File.open(node_mudle_pod_file,"w"){|f|
            f.write configString
        }
    end
    
    

    3、Podfile文件

    # Uncomment the next line to define a global platform for your project
    
    # 设置下载源
    source 'https://github.com/CocoaPods/Specs.git'
    # 导入我们自定义的脚本
    require_relative './Podfile_ReactNative'
    # 执行我们编写的RN环境检测代码
    installReactNativeSdk()
    # 设置RN配置 依赖,这里需要注意,不要使用 ../node_modules/,而是../RNCode/node_modules/
    require_relative '../RNCode/node_modules/react-native/scripts/react_native_pods'
    
    platform :ios, '10.0'
    
    target 'iOSCode' do
      # Comment the next line if you don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for iOSCode
      
      pod 'Masonry'
    
      # 设置RN Path 依赖
      use_react_native!(:path => "../RNCode/node_modules/react-native")
    
    end
    

    4、package.json

    node所有的依赖配置为 package.json文件控制,依赖全部下载在 node_modules目录下,所以我们在RNCode目录下创建package.json文件

    {
      "name": "iOSCode",
      "version": "0.0.1",
      "private": true,
      "scripts": {
        "android": "react-native run-android",
        "ios": "react-native run-ios",
        "start": "react-native start",
        "test": "jest",
        "lint": "eslint ."
      },
      "dependencies": {
        "react": "16.13.1",
        "react-native": "0.63.4"
      },
      "devDependencies": {
        "@babel/core": "^7.14.8",
        "@babel/runtime": "^7.14.8",
        "@react-native-community/eslint-config": "^3.0.0",
        "babel-jest": "^27.0.6",
        "eslint": "^7.31.0",
        "jest": "^27.0.6",
        "metro-react-native-babel-preset": "^0.66.2",
        "react-test-renderer": "16.13.1"
      },
      "jest": {
        "preset": "react-native"
      }
    }
    

    5、最后我们执行pod install即可

    ➜  iOSRNTest git:(main) ✗ pod install
    ReactNative 环境不存在,准备下载···
    yarn install v1.22.10
    warning ../../../package.json: No license field
    [1/4] 🔍  Resolving packages...
    

    三、文件过滤

    这里需要注意的是,node_modules目录为RN依赖的资源,没必要提交到git工程,可以在.gitignore文件中过滤掉。
    目前我们项目通过这中方式快速集成RN,RN的模块直接在对应的位置通过 RCTRootView展示,其他开发者不太需要关心RN的配置,只要会执行 pod install即可!

    四、iOS代码部分

    如果想在iOS原生页面跳转RN页面怎么做呢?
    我们需要封装几个基类,防止每次访问的时候都会重新加载,出现页面的空白

    KGRNManager.h

    //
    //  KGRNManager.h
    //  iOSCode
    //
    //  Created by jm on 2021/7/28.
    //
    
    #import <Foundation/Foundation.h>
    #import <React/RCTBridge.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface KGRNManager : NSObject
    
    + (instancetype)shareInstance;
    
    // 全局唯一的bridge
    @property (nonatomic, readonly, strong) RCTBridge *bridge;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    KGRNManager.m

    //
    //  KGRNManager.m
    //  iOSCode
    //
    //  Created by jm on 2021/7/28.
    //
    
    #import "KGRNManager.h"
    #import <React/RCTBundleURLProvider.h>
    
    #import "RCTPushy.h"
    
    @interface KGRNManager ()<RCTBridgeDelegate>
    
    @end
    
    @implementation KGRNManager
    
    static KGRNManager *_instance = nil;
    + (instancetype)shareInstance{
        if (_instance == nil) {
            _instance = [[self alloc] init];
        }
        return _instance;
    }
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone{
        if (_instance == nil) {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                _instance = [super allocWithZone:zone];
            });
        }
        return _instance;
    }
    
    -(instancetype)init{
        if (self = [super init]) {
            _bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
        }
        return self;
    }
    
    #pragma mark - RCTBridgeDelegate
    - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
    # if DEBUG
        //模拟器
       // return [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
    //    return [NSURL URLWithString:@"http://192.168.2.47:8081/index.bundle?platform=ios"];
    
      //真机,真机和电脑处于同一ip地址
      return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"main" fallbackResource:nil];
    # else
        return [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"jsbundle"];
    #endif
    }
    
    @end
    

    KGRNBaseVC.h

    //
    //  KGRNBaseVC.h
    //  iOSCode
    //
    //  Created by jm on 2021/7/28.
    //
    
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface KGRNBaseVC : UIViewController
    
    /**
     传递到React Native的参数
     */
    @property (nonatomic, strong) NSDictionary * initialProperty;
    
    /**
     React Native界面名称
     */
    @property (nonatomic, copy) NSString * pageName;
    
    + (instancetype)RNPageWithName:(NSString*)pageName initialProperty:(NSDictionary*)initialProperty;
    
    - (instancetype)initWithPageName:(NSString*)pageName initialProperty:(NSDictionary*)initialProperty;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    KGRNBaseVC.m

    //
    //  KGRNBaseVC.m
    //  iOSCode
    //
    //  Created by jm on 2021/7/28.
    //
    
    #import "KGRNBaseVC.h"
    #import <React/RCTRootView.h>
    #import "KGRNManager.h"
    
    @interface KGRNBaseVC ()
    
    @end
    
    @implementation KGRNBaseVC
    
    + (instancetype)RNPageWithName:(NSString*)pageName initialProperty:(NSDictionary*)initialProperty {
        KGRNBaseVC *vc = [[KGRNBaseVC alloc] initWithPageName:pageName initialProperty:initialProperty];
        return vc;
    }
    
    - (instancetype)initWithPageName:(NSString*)pageName initialProperty:(NSDictionary*)initialProperty {
        if (self = [super init]) {
            self.pageName = pageName;
            self.initialProperty = initialProperty;
        }
        return self;
    }
    
    //- (void)viewWillAppear:(BOOL)animated{
    //    [super viewWillAppear:animated];
    //    [self.navigationController setNavigationBarHidden:YES];
    //}
    //
    //- (void)viewWillDisappear:(BOOL)animated{
    //    [super viewWillDisappear:animated];
    //    [self.navigationController setNavigationBarHidden:NO];
    //}
    
    -(void)viewDidLoad {
        [super viewDidLoad];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(navagateBack) name:@"KGModuleNavigateBack" object:nil];
        
        RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:[KGRNManager shareInstance].bridge
                                                         moduleName:self.pageName
                                                  initialProperties:self.initialProperty];
        self.view = rootView;
    }
    
    - (void)navagateBack {
        [self.navigationController popViewControllerAnimated:YES];
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    
    @end
    

    当我们需要在原生页面跳转到RN页面的时候,可以创建一个KGRNBaseVC控制器push进入

    //  ViewController.m
    //  iOSCode
    //
    //  Created by jm on 2021/7/27.
    //
    
    #import "ViewController.h"
    #import <React/RCTRootView.h>
    #import "KGRNBaseVC.h"
    #import <Masonry/Masonry.h>
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self prepareUI];
    }
    
    - (void)prepareUI {
        self.title = @"React-Native";
        self.view.backgroundColor = [UIColor whiteColor];
        
        UIButton *btn1 = [[UIButton alloc] init];
        [btn1 addTarget:self action:@selector(btn1Action) forControlEvents:UIControlEventTouchUpInside];
        [btn1 setTitle:@"视图1" forState:UIControlStateNormal];
        [btn1 setBackgroundColor:[UIColor grayColor]];
        [self.view addSubview:btn1];
        
        CGFloat gap = KGScreenZoom(10);
        CGFloat width = (KGScreenW - gap)/ 4.0;
        [btn1 mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.view.mas_top).offset(KGNaviBarH + gap);
            make.left.equalTo(self.view.mas_left).offset(gap);
            make.width.mas_equalTo(width);
        }];
    }
    
    - (void)btn1Action {
        NSDictionary *dic = @{
            @"scores" : @[
              @{
                @"name" : @"Alex",
                @"value": @"AAAAAA"
               },
              @{
                @"name" : @"Joel",
                @"value": @"10"
              }
            ]
        };
        KGRNBaseVC *aVC = [[KGRNBaseVC alloc] initWithPageName:@"AView" initialProperty:dic];
        [self.navigationController pushViewController:aVC animated:YES];
    }
    
    @end
    

    相关文章

      网友评论

        本文标题:iOS 已有项目利用Pod集成RN

        本文链接:https://www.haomeiwen.com/subject/frycvltx.html