React Nativeと元モジュールデータ通信(二)(iOS)

17884 ワード

(一)はじめに
今日は引き続き原生モジュールのいくつかの特性を見てみます.例えば、フィードバック方法関数、Promises、マルチスレッド、定数設定、イベントはJavaScriptに送信され、ライフサイクルイベントを監督し、パッケージSwift原生モジュールなどの関連特性を取得します.
(二)Callback
オリジナルモジュールは特殊なパラメータタイプ-コールバック関数を同時にサポートします.多くの例では、JavaScriptにデータを転送するためにコールする方法が提供され得る.使用方法は以下の通りです
RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
  NSArray *events = ...
  callback(@[[NSNull null], events]);
}
RCTResponseSenderBlockは、JavaScriptフィードバック方法に渡すためのパラメータ配列しか受け入れられません.現在の例ではNode.jsのいくつかの開発習慣を使っています.最初のパラメータはerrorオブジェクトです.(もちろんエラー情報がない場合は、デフォルトはnullです.)他のパラメータはこのコールバック方法の戻り値情報です.JavaScriptコール方法を見てください.
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
import React,{
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
} from 'react-native';
///    NativeModules  CalendarManger  
import { NativeModules } from 'react-native';
var CalendarManager = NativeModules.CalendarManager;
class CustomButton extends React.Component {
  render() {
    return (
      
        {this.props.text}
      
    );
  }
}
class ModulesDemo extends Component {
  constructor(props){
    super(props);
    this.state={
        events:'',
    }
  }
  render() {
    return (
      
        
              iOS      
        
          'Callback      :'+{this.state.events}
        
        CalendarManager.findEvents((error,events)=>{
                if(error){
                  console.error(error);
                }else{
                  this.setState({events:events,});
                }
              }
            )}
        />
      
    );
  }
}
const styles = StyleSheet.create({
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  button: {
    margin:5,
    backgroundColor: 'white',
    padding: 10,
    borderWidth:1,
    borderColor: '#cdcdcd',
  },
});
AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
元モジュールのコールバックは一回だけサポートされますが、コールバックを保存してからいつか使えます.
(三)Promises
上のコールバック関数の使用を見ましたが、上の書き方にはいくつかの煩雑さがありますか?もちろん原生モジュールはPromiseを使うこともできます.そうすると、私たちが作成したコードを簡単にできます.ES 2016標準のasync/awaitの文法を使った方がいいです.ブリッジされた原生法の最後のパラメータがRCTPromiseResoloveBlockとRCTPromiseRejectBlockタイプであれば、JS方法はPromiseオブジェクトに戻ります.Promiseオブジェクトを用いて再構成前のコールバック関数法を実行した.具体的なコードは以下の通りです
RCT_REMAP_METHOD(findEventsPromise,
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSArray *events =@[@"  ",@"  ",@"  ",@"  "];
  if (events) {
    resolve(events);
  } else {
    NSError *error=[NSError errorWithDomain:@"  Promise      ..." code:101 userInfo:nil];
    reject(@"no_events", @"There were no events", error);
  }
}
このように処理した後、JavaScript端末の方法はPromiseオブジェクトに戻ります.このようにasyncキーワードの修飾方法でawaitキーワードを使って処理してデータの戻りを待つことができます.具体的なJavaScript端末での処理方法は以下の通りです.
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
import React,{
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
} from 'react-native';
///    NativeModules  CalendarManger  
import { NativeModules } from 'react-native';
var CalendarManager = NativeModules.CalendarManager;
class CustomButton extends React.Component {
  render() {
    return (
      
        {this.props.text}
      
    );
  }
}
class ModulesDemo extends Component {
  constructor(props){
    super(props);
    this.state={
        events:'',
    }
  }
  //  Promise    
  async _updateEvents(){
    try{
        var events=await CalendarManager.findEventsPromise();
        this.setState({events});
    }catch(e){
        console.error(e);
    }
  }
  render() {
    return (
      
        
              iOS      
        
        
          'Callback      :'+{this.state.events}
        
          CalendarManager.findEvents((error,events)=>{
                if(error){
                  console.error(error);
                }else{
                  this.setState({events:events,});
                }
              }
            )}
        />
      
    );
  }
}
const styles = StyleSheet.create({
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  button: {
    margin:5,
    backgroundColor: 'white',
    padding: 10,
    borderWidth:1,
    borderColor: '#cdcdcd',
  },
});
AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
(四)Threading(マルチスレッド)
原生モジュールが実行スレッドを呼び出していますが、私たちは一般的に構成を変更するべきではありません.React Nativeは独立したシリアルGCDのキューから生モジュールを呼び出すが、将来はこの方式が変わる可能性がある.を通じて、私たちは具体的なスレッドを指定できます.例えば、メインスレッドで実行しなければならないいくつかのAPIを呼び出す必要があるなら、次のように呼び出すことができる.
- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}
同様に、元のモジュールの操作に時間がかかります.元のモジュールはメインスレッドをブロックしてはいけません.単独のスレッドで実行します.例えば、RCTAsync Local Storrageのようなサブスレッドを作成し、以下のディスクに格納されている動作を実行するのに時間がかかるかもしれないが、React自体のメッセージスレッド列をブロックすることはない.例えば以下のように呼び出します
- (dispatch_queue_t)methodQueue
{
  return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}
上で作成したmethodQueメソッドは、パッケージ化されたモジュールのすべての方法で共有されています.もしパッケージ化の方法の中で、ごく少数または一つの方法だけが時間がかかります.この方法でdispatchを使うことができます.async方法は他のスレッドで実行され、他の方法に影響を与えず、具体的な使用方法:
//        ,  Thread  
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //          
    // You can invoke callback from any thread/queue
    callback(@[[NSNull null],@"        ..."]);
  });
}
(五)Exporting Contsパッケージの定数は呼び出しに使用されます.
オリジナル・パッケージ・モジュールは、JavaScriptに対して常時起動するための定数データをパッケージ化して提供することができ、このように、ブリッジ通信によっていくつかの静的データを転送することができる.使い方:
- (NSDictionary *)constantsToExport
{
  return @{ @"firstDayOfTheWeek": @"Monday" };
}
その後、JavaScriptはこのデータに同期してアクセスできます.
console.log(CalendarManager.firstDayOfTheWeek);
しかし、静的データについては、この方法でカプセル化されたデータは一度だけ初期化され、つまり、動作中にconstants ToExportの戻り値が修正されても、JavaScript端末の呼び出しの結果には影響しないことを知るべきである.
(六)Enum Constantsパッケージのエニュメレーション定数は呼び出しに供する.
NS_を使うENUM定義のエニュメレーションのタイプは、RCTConvert方法を拡張した後、方法における伝達のパラメータとして必要である.例えば、以下の列挙定義を実装する必要があります.
typedef NS_ENUM(NSInteger, UIStatusBarAnimation) {
    UIStatusBarAnimationNone,
    UIStatusBarAnimationFade,
    UIStatusBarAnimationSlide,
};
下記の通りRCTConvertを実現しなければなりません.
@implementation RCTConvert (StatusBarAnimation)
  RCT_ENUM_CONVERTER(UIStatusBarAnimation, (@{ @"statusBarAnimationNone" : @(UIStatusBarAnimationNone),
                                               @"statusBarAnimationFade" : @(UIStatusBarAnimationFade),
                                               @"statusBarAnimationSlide" : @(UIStatusBarAnimationSlide)}),
                      UIStatusBarAnimationNone, integerValue)
@end
次に、パッケージ方法を定義して、エニュメレーションの定数をJavaScriptに入れて使用します.
- (NSDictionary *)constantsToExport
{
  return @{ @"statusBarAnimationNone" : @(UIStatusBarAnimationNone),
            @"statusBarAnimationFade" : @(UIStatusBarAnimationFade),
            @"statusBarAnimationSlide" : @(UIStatusBarAnimationSlide) }
};

RCT_EXPORT_METHOD(updateStatusBarAnimation:(UIStatusBarAnimation)animation
                                completion:(RCTResponseSenderBlock)callback)
今あなたが作成したエニュメレーションは上記の方法の中のタイプで変換されます.(例ではintegerValue)そしてパッケージの方法を伝えます.
(七)イベントをJavaScriptに送信する
原生モジュールは、起動されないままJavaScript端にイベントを直接送信することができます.最も簡単な方法は、eventDisplatchを使用することです.原生モジュールの処理方法は以下の通りです.
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"

@implementation CalendarManager

@synthesize bridge = _bridge;

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
  NSString *eventName = notification.userInfo[@"name"];
  [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                               body:@{@"name": eventName}];
}

@end
JavaScriptは次に次のように購読してイベントを受信します.
import { NativeAppEventEmitter } from 'react-native';

var subscription = NativeAppEventEmitter.addListener(
  'EventReminder',
  (reminder) => console.log(reminder.name)
);
...
// Don't forget to unsubscribe, typically in componentWillUnmount
subscription.remove();
更に多くのJavaScriptにイベントを送信する例については、RCTLocation Observerを参照することができます.
ここに書いた例を見てみます.中のコードには上記の特性テストコードが含まれています.
まずObjective-Cコードを見てください.
//
//  CalendarManager.m
//  ModulesDemo
//
//  Copyright © 2016  Facebook. All rights reserved.
//

#import "CalendarManager.h"
#import "RCTConvert.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"

@implementation CalendarManager

@synthesize bridge=_bridge;

//    
RCT_EXPORT_MODULE()
//        
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location){
  NSLog(@"Pretending to create an event %@ at %@", name, location);
}
//        ,            secondsSinceUnixEpoch
RCT_EXPORT_METHOD(addEventMore:(NSString *)name location:(NSString *)location data:(NSNumber*)secondsSinceUnixEpoch){
   NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
}
//        ,            ISO8601DateString
RCT_EXPORT_METHOD(addEventMoreTwo:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
{
  NSDate *date = [RCTConvert NSDate:ISO8601DateString];
}
//        ,                  
RCT_EXPORT_METHOD(addEventMoreDate:(NSString *)name location:(NSString *)location date:(NSDate *)date)
{
   NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
  [formatter setDateFormat:@"yyyy-MM-dd"];
   NSLog(@"       :%@,  :%@,  :%@",name,location,[formatter stringFromDate:date]);
}

//        ,                  
RCT_EXPORT_METHOD(addEventMoreDetails:(NSString *)name details:(NSDictionary *) dictionary)
{
  NSString *location = [RCTConvert NSString:dictionary[@"location"]];
  NSDate *time = [RCTConvert NSDate:dictionary[@"time"]];
  NSString *description=[RCTConvert NSString:dictionary[@"description"]];
  NSLog(@"       :%@,  :%@,  :%@,    :%@",name,location,time,description);

}

//        ,  Callback
RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
   NSArray *events=@[@"  ",@"  ",@"  "];
   callback(@[[NSNull null],events]);
}

//        ,  Promise  
RCT_REMAP_METHOD(findEventsPromise,
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSArray *events =@[@"  ",@"  ",@"  ",@"  "];
  if (events) {
    resolve(events);
  } else {
    NSError *error=[NSError errorWithDomain:@"  Promise      ..." code:101 userInfo:nil];
    reject(@"no_events", @"There were no events", error);
  }
}

//        ,  Thread  
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //          
    // You can invoke callback from any thread/queue
    callback(@[[NSNull null],@"        ..."]);
  });
}

//         JavaScript    
-(NSDictionary *)constantsToExport{
  return @{@"firstDayOfTheWeek":@"Monday"};
}
//          
RCT_EXPORT_METHOD(sendNotification:(NSString *)name){
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(calendarEventReminderReceived:) name:nil object:nil];
}

//           JavaScript 
- (void)calendarEventReminderReceived:(NSNotification *)notification
{
  [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder"
                                               body:@{@"name": @"  "}];
}
@end
以下はJavaScriptの先端コードです.
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
import React,{
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
} from 'react-native';
///    NativeModules  CalendarManger  
import { NativeModules } from 'react-native';
import { NativeAppEventEmitter } from 'react-native';
var subscription;
var CalendarManager = NativeModules.CalendarManager;
class CustomButton extends React.Component {
  render() {
    return (
      
        {this.props.text}
      
    );
  }
}
class ModulesDemo extends Component {
  constructor(props){
    super(props);
    this.state={
        events:'',
        notice:'',
    }
  }
  componentDidMount(){
    console.log('      ...');
    subscription = NativeAppEventEmitter.addListener(
         'EventReminder',
          (reminder) => console.log('    :'+reminder.name)
         );
  }
  componentWillUnmount(){
     subscription.remove();
  }
  //  Promise    
  async _updateEvents(){
    try{
        var events=await CalendarManager.findEventsPromise();
        this.setState({events});
    }catch(e){
        console.error(e);
    }
  }
  render() {
    return (
      
        
              iOS      
        
        CalendarManager.addEvent('    ', '        ')}
        />
        CalendarManager.addEventMoreDate('    ', '        ',1463987752)}
        />
        CalendarManager.addEventMoreDetails('    ', {
              location:'          ',
              time:1463987752,
              description:'       ~'
            })}
        />
        
          'Callback      :'+{this.state.events}
        
        CalendarManager.findEvents((error,events)=>{
                if(error){
                  console.error(error);
                }else{
                  this.setState({events:events,});
                }
              }
            )}
        />
        CalendarManager.findEvents((error,events)=>{
                if(error){
                  console.error(error);
                }else{
                  this.setState({events:events,});
                }
              }
            )}
        />
        
          '     :'+{CalendarManager.firstDayOfTheWeek}
        
        
          '      :'+{this.state.notice}
        
        CalendarManager.sendNotification('      ...')}
        />
      
    );
  }
}
const styles = StyleSheet.create({
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  button: {
    margin:5,
    backgroundColor: 'white',
    padding: 10,
    borderWidth:1,
    borderColor: '#cdcdcd',
  },
});
AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
*(8)パッケージSwiftメソッド**
Swiftはマクロ定義をサポートしていません.Swiftの一部のモジュールと方法を実装してJavaScriptを呼び出すにはもっと多くの構成が必要ですが、基本的にはObejctitve-Cのパッケージ構成方法と同じです.
今の例ではSwfit類のCalendarManagerがあります.
// CalendarManager.swift

@objc(CalendarManager)
class CalendarManager: NSObject {

  @objc func addEvent(name: String, location: String, date: NSNumber) -> Void {
    // Date is ready to use!
  }

}
[特に注意する].こちらは@objcラベルを使ってパッケージの種類と方法を修正してObjctive-Cにアクセスできるようにする必要があります.
その後、私たちはプライベートな実装クラスを作成し、React Nativeブリッジに必要な情報を登録します.具体的なコードは以下の通りです
/ CalendarManagerBridge.m
#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(CalendarManager, NSObject)

RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)

@end
あなたがプロジェクトの中で同時に二つの言語を使って開発する場合、追加のブリッジファイルを作成する必要があります.このファイルはブリッジファイルと言います.これはObjective-Cファイルを提供してSwiftに呼び出しを行います.Xcode IDEを通じてフォルダを選択すると、新しいファイルを作成してSwiftファイルをプロジェクトに追加します.Xcodeは自動的にあなたに先頭ファイルを作成します.頭ファイルにRCTB RidgeModule.hを導入するだけでいいです.具体的なコードは以下の通りです
// CalendarManager-Bridging-Header.h
#import "RCTBridgeModule.h"
同様に、あなたもRCT_を使うことができます.EXTERNREMAP_MODULEとRCT_EXTERNREMAP_METHODはモジュールとメソッドのJavaScript呼び出し名を設定します.
小さい広告を打ちます:これは私の今搾っているRNプロジェクトで、現在完成したのは網易のニュース、美の団>>>があって、また引き続きいくつか機能を添加して、支持を望みます~~