24
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

内部実装から追うReact Native

Last updated at Posted at 2015-03-28

書いた人間はCとC++からの類推解釈でObjective-C読めるけれど、iOSのAPIはちゃんと理解していない。Sampleはすでに動かした前提で書く。

TL;DR

documentに依存しないVirtualDOM (= React.js)だけであれば、iOSでもJavaScriptCore上で実行可能なので、React.jsをそのまま投げ込める。

index.ios.bundle

AppDelegate.mindex.ios.bundleへのURLが定義されている。エミュレータで起動した際、同時にhttpサーバが立ち上がっているのはindex.ios.jsをbundleしたものをこのURLとして返すためにある。

ところで、React.jsを使う大原則として、React ElementをrenderするコンテナにはReact.jsが管理しているDOM以外のDOM(htmlで書いたものとか)を配置することができない。この大原則はReact Nativeでも生きていて、RCTRootViewの中にはReact Native以外の方法でUIを配置することはできない。

一方で、RCTRootViewの中に配置されている要素がすべてReact Nativeの管理下にあるならば、bundleしたJavaScriptを読み込み直してRCTRootViewの中身を空にすることで、アプリ自体を再ビルドすることなくUIのリロードが可能になる。エミュレータ実行時にCmd + RでJavaScriptの変更が反映できるのはこのような仕組みが働いていることにある。

JavaScriptCore

URL指定されたJavaScriptコードは読み込みの過程を経てここで実行される。RCTJavaScriptExecutor自体は@protocolとして宣言されているが、デフォルト扱いで実装されているRCTContextExecutorを見る。

React/Executors/RCTContextExecutor.m#235
- (void)executeApplicationScript:(NSString *)script
                       sourceURL:(NSURL *)url
                      onComplete:(RCTJavaScriptCompleteBlock)onComplete
{
  RCTAssert(url != nil, @"url should not be nil");
  RCTAssert(onComplete != nil, @"onComplete block should not be nil");
  [self executeBlockOnJavaScriptQueue:^{
    JSValueRef jsError = NULL;
    JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script);
    JSStringRef sourceURL = JSStringCreateWithCFString((__bridge CFStringRef)url.absoluteString);
    JSValueRef result = JSEvaluateScript(_context, execJSString, NULL, sourceURL, 0, &jsError);
    JSStringRelease(sourceURL);
    JSStringRelease(execJSString);

    NSError *error;
    if (!result) {
      error = RCTNSErrorFromJSError(_context, jsError);
    }

    onComplete(error);
  }];
}

- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
  if ([NSThread currentThread] != _javaScriptThread) {
    [self performSelector:@selector(executeBlockOnJavaScriptQueue:)
                 onThread:_javaScriptThread withObject:block waitUntilDone:NO];
  } else {
    block();
  }
}

ざっくり言えば、別スレッドでJavaScriptCoreを使って読み込んだJavaScriptコードを実行している。JavaScriptCoreはブラウザやDOMに依存するdocumentを扱うことはできないが、React.js含むVirtualDOMは実際にレンダリングするまでは単なるオブジェクトの操作なので、documentやDOMを使わず読み込みが可能。documentに依存しないiOSネイティブなUIを代わりに呼び出せば、React.jsからiOSのUIも呼び出しができるということである。

まとめ

React Nativeは、コンセプトにあるとおりiOSのUIをReact.jsと同じ方式で組み立てることに主眼が置かれている。

  • React.jsの作法をiOSのUI設計に適用できる
  • React.jsの作法に従うことでスマートUIのようなバッドパターンをある程度避けられる
  • 当然React Nativeで管理しているUIをReact.js以外で触ることはご法度
  • フロントエンドでこれまで使用していたツールセットが利用できる

React.jsをパターンとして捉えることで、上記のような特徴を得られる。APIのほとんどが2014年頃のJavaScriptスタンダードを逸脱していないので、Titanium Mobileのような「あのAPI何だっけかな」的なフレームワークそのものへのロックインを避けることはできる。

代わりにTitanium MobileのようなObjective-CとiOS APIを抽象化してJavaScriptで扱えるような仕組み自体はないので、「JavaScriptだけでアプリが作れる」風便利フレームワークではない。

24
22
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
24
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?