LoginSignup
8
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-07-17

背景

既存の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. が吐かれていることが確認できる。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3