Lean Engineer

リーンエンジニア〜効率的に実験しながら、技術を学ぶブログ

【iOS編】ReactNativeをネイティブアプリとハイブリッドに使ってみる

 まずは、ネイティブアプリとのインテグレーションをやっていく。僕は、iOSのエンジニアなので、まずはiOSから。そのうち、Androidもやりたいと思っているが、Androidはメルカリの人がよいチュートリアルを書いている。

ReactNativeのインテグレーションは、公式のドキュメントが参考になるので、それを見ながら進めていく。

ReactNativeプロジェクトを作る

まず、ReactNativeプロジェクトを作成する。

react-native init YOUR_APP_NAME

これで、ReactNativeプロジェクトが作成される。動かしてみたい場合は、 react-native run-iosとすれば良い。ReactNativeが作ってくれる、デフォルトのアプリケーションが立ち上がる。

既存アプリで置き換える

さて、既存のアプリでiosフォルダを置き換えよう。もし、git submoduleにしても良い。その場合は、iosフォルダを削除して、

git submodule add GIT_ADDRESS_HERE ios

git submoduleはそれはそれでめんどくさいので、単純に置き換えるほうが便利かなとは思っている。

既存アプリに導入する

iOSの既存アプリにReactNativeを導入するには、cocoapodsを使うのが楽だ。下記のようなPodfileを用意して、pod install する。

target 'YOUR_APPLICATION_NAME' do

  pod 'React', :path => '../node_modules/react-native', :subspecs => [
    'Core',
    'CxxBridge', 
    'DevSupport',
    'RCTText',
    'RCTNetwork',
    'RCTLinkingIOS',
    'RCTImage',
    'RCTWebSocket',
    'RCTAnimation', 
  ]
  pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'

  pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
  pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
  pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
  
end

これで、準備は整った。ViewControllerにStyoryboadからIBActionをつなげて、下記のよう書く。以下はモーダルで表示する例だ。ViewController.mで作業していることを想定している。

#import <React/RCTRootView.h>

- (IBAction)actionOfShowApp:(id)sender {
    NSURL *jsCodeLocation;
    jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
    
    RCTRootView *rootView = [[RCTRootView alloc] 
        initWithBundleURL:jsCodeLocation
        moduleName:@"YOUR_APP_NAME"
        initialProperties:nil
        launchOptions:nil];
    
    UIViewController *vc = [[UIViewController alloc] init];
    vc.view = rootView;
    [self presentViewController:vc animated:YES completion:nil];
   
}

これで動かすと、既存アプリにReactNativeが導入できる。動かすときは、

  • npm run start
  • react-native run-ios

とする必要がある。ReactNative単体でアプリを作っているときは、react-native run-iosだけでnpm run startは自動的に動き、パッケージマネージャが動いてくれる。しかし、ハイブリッドにするとこれが同時に動いてくれないみたいなので、別々に動かす必要がある。なので、react-native run-iosはXcodeのビルドでも良い。

ReactNativeとネイティブを繋ぐ

さて、モーダルで表示すると、戻れなくなる。ので、Native Moduleを使って、モーダルを閉じる。

まずは、ViewController.hを編集する。

#import <React/RCTBridgeModule.h>
@interface ViewController : ViewController<RCTBridgeModule>

@end

次に、ViewController.mを編集する。注意点は、UIの変更になるので、メインキューを使えるようにしていること。また、RCT_EXPORT_METHODの中でdismissViewControllerAnimatedを直接呼びたいのだが、どうもうまく動かないので、NSNotificationCenterでイベントを飛ばして、ViewControllerでObserveしてから、dismissViewControllerAnimatedするようにしている。

NSString *const DISMISS_MODAL = @"DISMISS_MODAL";


RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(dismissEvent)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:DISMISS_MODAL object:self userInfo:nil];
    });
}

+ (BOOL)requiresMainQueueSetup
{
    return YES;
}


- (void) dismissModal{
    [self dismissViewControllerAnimated:YES completion:nil];
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissModal) name:DISMISS_MODAL object:nil];
}

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

次に、ReactNative側からネイティブ側でExportしたメソッドを呼び出す。App.jsを変更する

import { Button, NativeModules } from 'react-native';

export default class App extends Component {
  render() {
    return (
        ...
       <Button
          onPress={() => { NativeModules.ViewController.dismissEvent() }}
          title="Dismiss Modal"
          color="#841584"
        />
        ...
    )
  }
}

これで、ネイティブ側でExportしたメソッドを呼び出すことができ、モーダルを閉じることができる。

以上で、ハイブリッドアプリへの第一歩が踏み出せた。とりあえず、固定のViewをReactNativeで作って表示したいのようなような場合には使えるだろう。