Help us understand the problem. What is going on with this article?

SwiftのViewControllerで定義されているメソッドをReactNativeで実行する

More than 3 years have passed since last update.

背景

既存のSwiftで書かれているiOSアプリケーションにReactNativeを取り入れていく場合に、手っ取り早くViewController内で定義されたメソッドをjs側で呼び出したいことがある。

手法

SwiftとReactNativeとでイベントのやりとりをするには、ネイティブ側で編集するべきは下記の3つのファイルになる。

  1. appname-Bridging-Header.h (add or edit)
  2. FooViewController.m(add)
  3. FooViewController.swift(edit)

Swiftだけではメソッドの受け渡しができないため、少しだけ Objective-C を取り入れる必要がある。

1. appname-Bridging-Header.h

Reactのヘッダーファイルを読み込んでおく必要があるため、

#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>

を書き足す。Bridging-Headerファイルがない場合は新たに作成する。(その場合、2.の手順を先に行うと、Xcodeが Bridging-Headerファイルを作成するかどうか尋ねてくれる。)

2. FooViewController.m

新たに作成すべきファイル。メソッドをやりとりしたい {ViewController名}.m で作成する。

#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(ChatViewController, NSObject)

RCT_EXTERN_METHOD( testEvent:(NSString *)eventName )

@end

これで、ReactNative側とやりとりができるように宣言したことになる。

3. FooViewController.swift(edit)

さて、2. だけではまだswift側が対応できていないので、

class FooViewController: UIViewController {

    func testEvent( arg: String ) {
        // whatever
    }

}

という既存のコードにおける class, func それぞれに対して

@objc( FooViewController )
class FooViewController: UIViewController {

    @objc func testEvent( arg: String ) {
        // whatever
    }

}

と、 @objc を付け足す。
すると、ReactNative側では

import React, { Component } from 'react';
import {
  AppRegistry,
  Platform,
  NativeModules,
  NativeAppEventEmitter,
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
  TouchableNativeFeedback
} from 'react-native';

class FooComponent extends Component {

  constructor(props) {
    super(props);
  }

  onTouch() {
    NativeModules.FooViewController.testEvent('test');
  }

  render() {
    let self = this;

    let TouchableElement = TouchableHighlight;
    if (Platform.OS === 'android') {
      TouchableElement = TouchableNativeFeedback
    }

    return (
      <View>
        <TouchableElement onPress={this.onTouch.bind(self)}>
          <View>
            <Text>
              Button
            </Text>
          </View>
        </TouchableElement>
      </View>
    );
  }
}

AppRegistry.registerComponent('FooComponent', () => HelloWorld);

とすることで、 Button をタップしたときに、ViewController の testEvent が呼ばれることが確認できる。

また、 ReactNative -> Swift -> ReactNative と、もう一度 ReactNative に値か何かを返したい場合は、

import React

@objc( FooViewController )
class FooViewController: UIViewController {

    var bridge: RCTBridge!

    @objc func testEvent( arg: String ) {

        // whatever

        let eventName: String = arg
        self.bridge.eventDispatcher().sendAppEventWithName( eventName, body: "test event emitted." )

    }

}
class FooComponent extends Component {

  constructor(props) {
    super(props);

    NativeAppEventEmitter.addListener('test', (body) => {
      console.log(body);
    });
  }

  onTouch() {
    NativeModules.FooViewController.testEvent('test');
  }

  render() {
    ...
  }
}

とすれば、 console.log(body) 経由で test event emitted. が吐かれていることが確認できる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away