React Native製Mastodonクライアント「ikuradon」をgithubで公開しました。

potproject/ikuradon

React Nativeの勉強のためにちまちまと作ってたアプリなのですが、

最近、せっかくなので何かOSSとして公開したいなーと思っていたので公開します。アプリ名も安直です。

React Native、色々言われてる割には国内でのちゃんとしたアプリのソース公開も少ないので、参考になればいいんですけども。

内部のライブラリにReact Redux,React Navigationを使用しています。

Reduxは覚えるのに苦労しつつ、この実装でいいのか今もイマイチな感じがあるのですが、全体のデータを管理出来るので、Reduxか似たようなものが無いと設計段階でやはりキツいです。

まだまだ開発途中で、一部遷移が遅かったり、そもそも動かなかったりしますが、そこはちゃんと整備します。デザインも変えていくので許して。

とりあえず今のところはタイムライン表示機能とfav,boost,普通のTootとまあ本当に最低限の機能という感じ。

開発期間は割と短いし、Androidでは一切検証してなかったけど大体普通に動きます(バグあるけど)。

React Native(というかExpo)開発に関して

とりあえず、Expoが本当にすごい。

コード更新即反映即立ち上げのHot Reload機能、その他役に立つ解析機能やAPIなどかなり開発速度を早くできたのもこのおかげです。

そしてWindowsでも実機でiOSネイティブ開発出来ちゃいます。わざわざ埃被ってるMacBookを出さなくていいです。

ネックなのはデバッグモードがかなり遅いところですねえ。そのせいで本番と挙動が違う部分も出てきたりします。
この部分はアップデートとProduction Modeが追加されてかなりマシになったと思います。でもネイティブビルドと比べると遅いけどそれはしょうがない。

速度的には充分ネイティブ並みに動いてると感じます。UI自体はネイティブですし、よくあるUI部分を同期的に書いてフリーズする、みたいなことも基本無くてよろしい。
デフォルトでES6とES7の一部分をサポートとしており、JS界隈でありがちな「初期の構築いろいろあり過ぎて辛い」もなく、await/asyncが使えるのは非常に便利です。

開発は楽で楽しいんですけど=簡単に作れるってわけではないと思います。そもそもReactやRedux、ES6の知識が必要ですし。

WebSocketもデフォルトでサポートされていたので、Streaming APIの対応もすんなりいきました。100行程度でいけます。強いです。

動かし方

上でも書いたとおり、Expoというアプリを使用します。このアプリをダウンロードして、このCDNリンクからQRコードを読み込むだけ。

開発なら、cloneした後npm installからnpm startでQRコードが出てきます。これで終わり。後は同じ。

自分もこれで実機のiOSで開発しています。ほぼnpmで事足りるのでホームページからわざわざSDKダウンロードしたりすることも無し。なんともモダンって感じの開発環境です。

App StoreやGoogle Playの公開はもうちょっとできてから考えようって感じです。

また、時間があれば開発での辛いところとか書いていきます。

React Nativeのルーティングライブラリ比較

ルーティング(Router)ライブラリとは、画面遷移や戻る、進むなどを行うライブラリのことです。

WebではSPAのページを作成するときに必要なため、SPAでなければ使用することも無いと思いますが、

React Native単体ではルーター機能はほぼ無いに等しく、現在のバージョンではNavigatorIOSというAPIしかありません。

名前が示す通り、Androidでは使用できないため、これは使えません。

画面遷移を含まないアプリなんてまず無いと思いますので、RNアプリ作成するにはRouterライブラリは必須です。

RNで使用できるルーティングライブラリの上位3つを比較してみました。

主にこの3つがメインかなと思います。

一応、アプリ開発で全て触ってます。上から順に使っていった感じです。

そして、最終的にReact Navigationに落ち着きましたという記事です。

React Native Navigation

wix/react-native-navigation

★Star 4300 NativeModule使用

App-wide support for 100% native navigation with an easy cross-platform interface.

利点:

  • ネイティブの処理で遷移するため、速い。そのため、デザインもネイティブベースである。

欠点:

  • ネイティブモジュールを使用しているため、Expoを使用できない
  • ネイティブで動作するため、カスタマイズ性は低い

個人的に:
Reduxサポートはしているみたいだが、公式のredux-exampleがdeprecatedだったり、イマイチ書き方がわからなかった。ちょっとドキュメントが整備されていない?

AndroidとiOSでの表示のプレビューなどもないため、よくわからなかったという感じでした。

一番速度的には速いと思うのだが、Expoで開発したいというのもあるため、非採用

React Native Router

aksonov/react-native-router-flux

★Star 5200

First Declarative React Native Router

利点:

  • カスタマイズ性が高く作られている。NavBar/TabBar部分に独自のcomponentsを表示できる
  • react-routerライクで作られているため、使ったことのある人は入りやすい

欠点:

  • 独自な分、NavBar/TabBarっぽいデザインが用意されていない。デザインを作って指定する必要がある

個人的に:

たとえば公式APIにTabBarIOSがあるが、Androidには存在しない
そのため、iOSとAndroidでTabのデザインを分けるか、独自でデザインを作る必要が出てくる、それはやりたくないため見送り。

※ここで話しているのはv3の話です。v4ではReact Navigationがベースにカスタマイズ出来るよという感じらしいので、

デザインはReact Navigationと同じようなものが使えると思います。
v4が正式になったら、こっちのほうを採用したかも・・・

React Navigation

react-community/react-navigation

★Star 6400

Learn once, navigate anywhere.

利点:

  • ネイティブっぽいデザインがデフォルトであり、少ないコードで割とすぐにそれっぽく作れる。
  • AndroidとiOSでiOSはTabbar、AndroidはTabLayoutっぽいデザインになる。
  • Star数も一番多い。ドキュメントが豊富

欠点:

  • rnrfよりはカスタマイズ性は劣る。わかりやすいけどtitleをComponentsのstaticで指定する部分とか一部トリッキーだったりもする。

個人的に:

React Community製のライブラリ。
一番ドキュメントが充実しており、同じようにReact Community製のReduxサポートが充実していてわかりやすかった。

ネイティブっぽいデザインがデフォルトで表示され、開発しやすい。

rnrfよりはカスタマイズ性は劣るが、ネイティブっぽい表示が目的だったため現在これで開発中。

  • 動作速度重視ならReact Native Navigation
  • カスタマイズ性重視ならReact Native Router
  • 開発速度重視ならReact Navigation

という感じで、うまいこと差別化ができていると思います。

一番困るのは非常に名前が紛らわしい点でした。特にnpmは似たような名前のスパムすらあるから困る。

(今作ってるRNアプリはOSSとして公開するつもり。明日。頑張ろう。)

[React Native]ネイティブモジュール(Swift)を使う[iOS編]

potproject.net Advent Calendar 20169日目の記事です。


react-nativeをとりあえずすぐ実機で動かしてみる

で、とりあえず実機で動くかの確認は取れました。
で、今回は、React Nativeとネイティブコードの連携をやってみたいと思います。

React NativeはiOSやAndroidを共通のJavascriptで書いてマルチプラットフォームでしかもネイティブに動くよ!というのがウリです。

しかし、当然ながら、SwiftやObjective-Cを使うようなものも必要になります。カメラとかプッシュ機能。

まあメジャーなものは用意されてる場合もあるんですが、マイナーなものは無かったりするので、

自分で作ってね!ってことでネイティブモジュールという機能で補完できるようになってます。

Cordovaを思い出す。しかしこっちは完全ネイティブなのだ。

このあたりを参考にしました。しかしまあやっぱり例によってSwift3.0なので結構コード変えてたりもします。
React NativeでNative機能をSwiftで書いて使うには

Native Modules

Objective-Cは全く分からないのでSwiftです。でもブリッジしなくてはならないので多少はObjective-Cは避けられないんだなあこれが。
原則、React NativeはネイティブモジュールにSwiftを使うように設計されていません。生成されたプロジェクトもObjective-C用で生成されます。

なので、使うにはObjective-CとSwiftを連携させる必要があります。

swiftでモジュールを作成

まずは、モジュールを作成します。ちなみにこれだけだとまだ動きません。
端末の時間を取得してAny(Dictionary)型でコールバックを返します。

どうやらReact Nativeのjavascriptにデータを受け渡す際は必ずcallbackなのかな。
ちなみにPromiseも使えます。

ReactNativeModule.swift

import Foundation

@objc(ReactNativeModule)
class ReactNativeModule: NSObject {

  @objc func callbackMethod(_ callback: RCTResponseSenderBlock) -> Void {
    // システムのカレンダーを取得
    let cal = Calendar.current
    // 現在時刻のDateComponentsを取り出す
    var dataComps = cal.dateComponents([.year, .month, .day, .hour, .minute], from: Date())
    let object = [
      "year":dataComps.year!,
      "month":dataComps.month!,
      "day":dataComps.day!
    ]
    callback([object])
  }
}

配列で返しても大丈夫なの?と思いでしょうが、ちゃんとReact Native側はjavascript Object型で受け取ってくれます。優秀です。  
callbackMethodの第一引数が無名関数がなっているのは、このあたりの問題から。認識してくれないのです。Swift3.0での変更点となります。
Got “is not a recognized Objective-C method” when bridging Swift to React-Native – stackoverflow

Objective-Cでエクスポートする

ReactNativeModuleBridge.m

#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(ReactNativeModule, NSObject)

RCT_EXTERN_METHOD(callbackMethod:(RCTResponseSenderBlock)callback)
@end

実際はこのObjective-Cが読み込まれます。これが上で書いたSwiftコードの橋渡しになります。

Objective-Cの文法は相変わらず割と理解できていない。

testreactnative-Bridging-Header.h

#import "RCTBridgeModule.h"

RCTBridgeModule.hというObjective-CのコードをSwiftで読み込むために設置。
このあたりに関してはググるといっぱい出てくるため割愛。

React Native側のソースはこんな感じ。画面の更新もしたかったのだが、結構時間掛かりそうで・・・ただログ出すだけ。

index.ios.js

import React, { Component } from 'react';
import {
  NativeModules,
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';
var nm=NativeModules.ReactNativeModule;
nm.callbackMethod(function(res){
    console.log(res);
});

これでデバッグ画面からログが確認できました。

ちゃんと{"year":2016,"month":12,"day":9}と出てきます。Any型も受け渡せるのは楽でいいですねえ。

今回、タイトルが(iOS編)です。

これで気づいた人もいるかもですが、明日はネイティブモジュール(Java)を使う[Android編]をやります。多分。

react-nativeをとりあえずすぐ実機で動かしてみる

← 1日目 centos7+nginx+php-fpm+php7な新しい感じの環境を構築

potproject.net Advent Calendar 20162日目の記事です。


とりあえずreact-nativeをすぐ使いたい記事。

環境は一つ前のMacBook Proです。

https://facebook.github.io/react-native/docs/getting-started.html

とりあえずはここに沿っているだけなのでこの記事見るよりこっちのほうがいいかも。
で、ハマったところを紹介していきます。
こういうの、あまり使ったことないと時間かかったりすると思うので。

brew install node
brew install watchman
npm install -g react-native-cli

Androidビルド

export ANDROID_HOME=~/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools

Android SDKなんかがうまいこと設定されていればこれで準備OK。

丁寧にパスを通す方法も載ってます。やさしいですfacebook。

ちなみにANDROID_HOMEは通してなかった場合も結構あると思うので、ちゃんと通しましょう。

通さないとビルドに失敗します。ここもハマりポイント。

react-native init testreactnative
cd ./testreactnative
react-native run-android

testreactnativeというフォルダが作成され、テンプレ状態のものが作られるので、そのままビルド。
で、駄目。

A problem occurred configuring project ':app'.
> failed to find Build Tools revision 23.0.1
BUILD FAILED

どうやら、現状はbuild tool 23.0.1でしか動かないっぽい。
つーか普通に書いてあったがな。単なる見落とし。
つか普通にbuild.gradleを変更すればできる気がしますが、素直に23.0.1を入れて再挑戦。

Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Failed to establish session

今度はなんだろう。インストールに失敗した模様。ちゃんとUSBデバッグもやっているのだが・・・

と思ったらこれはOS特有問題で、MIUIのデフォルト設定の場合こうなるとのこと。

ちょうどMIUI8のredmi note 3 proを使っていました。

com.android.ddmlib.InstallException: Failed to establish session react-native

Developer OptionのTurn on MIUI optimizationをオフ。これは外部からのインストールを防ぐ機能なのですが、今回はそれが邪魔した模様。

testreactnative

他に、-bash: adb: command not foundとか出てたのでPATHが設定できてなかったという問題もありました。
後はAndroidもiOSも初回は起動しても表示に多少時間がかかる模様。早とちりだと見落とします。
まあいいや、次はiOSだ!

iOSビルド

react-native run-ios
reactnative-ios

iOSはXcodeの新しいものが入っている環境なら、問題なくエミュレータが立ち上がりました。

しかし、実機の場合はXcodeから実行しないと駄目なようです。

testreactnative/ios/testreactnative.xcodeprojをXcodeで開き、プロビジョニング設定後そのままRunすればOK。

特に詳しい設定もいらなそうです。ちょっと試すならこっちが楽ですね。

また時間があったら試してみます。ネタを続けるために次回に続く。


3日目→