【ReactNative】単一ソースを複数アプリ(target)にビルドしたの話

背景

同じようなシステムを別のターゲットに少しだけ内容を変えてリリースしたいという状況だった。 具体的には、国家試験対策アプリを歯科医師用歯科衛生士用に出したかった。 歯科医師用アプリはiOS, Androidでリリース済みで、衛生士用も同じく両プラットフォームで出したかった。 システムはほぼ同じで、コンテンツ(クイズの内容・カテゴリ)と少しのUI、iconなどだけ変えて別アプリとして出したかった。

検討したこと

1. xcodeでtargetを2つ作る
2. 共通部分をnpm pacakge化
3. 【採用した】共通部分をgit submodule化

採用しなかったやつとその理由

1. xcodeでtargetを2つ作る

これが一番スマートだと思って、一度はやってみた。しかし、

- iOS, Android両方でビルドを分けるのは思ったより大変だった
- targetによって切り分ける処理はネイティブコードでしかできない(僕調べ)ので、環境変数
などで切り分けるしかなくてメリットが小さかった
- ※1 ReactNative特有の原因により、解決困難なエラーが出まくった
- だだでさえ不確定要素の多いReactNativeで、nativeでもあまりやらないことをすると危険

swiftとかで書かれたアプリなら、この方法(複数targetを作る)が良いんだと思う。この方法はネット上に意外と多く転がっているので、swiftなら時間かけずにできそう。

※1 ReactNative特有の原因により、解決困難なエラーが出まくった => 詳しくは割愛する(後日書くかも)が、ReactNativeの実装上、複数targetにビルドするのは想定されていない気がしている。

2. 共通部分をnpm pacakge化

2と3は似ているが、submoduleによる管理・共通化の方が運用が楽そうだった。(後述) あと単純に、npm pacakgeに詳しい人よりもgitに詳しい人の方が世の中に多そうなので引き継いだ人が楽になる可能性が高いと判断した。

【採用した】共通部分をgit submodule化

git submoduleについては詳しく触れないが、使いどころさえミスらなければ非常に便利だと思うので知っておくと良いと思う。 f:id:yooska14:20181206165212p:plain 今回のプロジェクトはこんなディレクトリ構成になっていて、基本的な実装部分はsrc以下にまとめた。 そして、今回submodule化したのはsrc以下の全て。 画像を見ればわかるように、redux関連の処理と各スクリーン・コンポーネント、ナビゲーション設定系も全てまとめている。

submoduleを使ったメリットは沢山あるしsubmoduleについて調べれば分かりやすいと思うので割愛するが、大きなデメリットが1つあるので紹介する。それは依存ライブラリのリンク(Linking Libraries)をプロジェクトごとにしなければならないことだ。(Linking Librariesについては「参考」のリンク参照)

NativeがいじれないからReactNaitveを選定したのに結局触らなアカンのかい!って気持ちになるのをプロジェクトごとにやるのか。。って気持ちになるが、意外と2回目の作業は詰まらずいけた。むしろ復習になってすごく理解が進んだ。なのでこれについては実はあまりデメリットに感じていない。 ただ、複数人でプロジェクトを回す場合はやはり大きなデメリットになるかも。

参考

qiita.com

facebook.github.io

【ReactNative】iOSアプリのリリースフロー

前提・背景

この記事は、「使用中のmacで1度以上iOSアプリをリリースしたことがある」前提です。 自分がその状態のため、思い出しながら備忘録として書いています。 リリース経験がない人は、この記事最下部におすすめ記事を書いているので、それがすごく参考になると思います。 この記事の対象読者にとっては、「この部分は1回だけで良いんだ」「この作業は毎回必要なんだ」という風に理解が進む狙いなので、お役に立てると思います。

そして、

使用中のmacで1度以上iOSアプリをリリースしたことがある というのは具体的に以下の3つの前提を指しています。

  • Appleの開発者登録(Apple Developer Program)が済んでいる前提です。 登録 - サポート - Apple Developer

  • iOS Certificateが作成されている前提です。 iOS CertificateはXcode上でアプリを実機用にビルドするのに必要なもの。逆に言えば、一度作成しているとそれ以降は必要ない。 以下の記事がすごく詳しい(少し古いが2018-12-04現在問題ない)のでおすすめです。 i-app-tec.com

  • Provisioning Profile Provisioning ProfileもXcode上でアプリを実機用にビルドするのに必要なもののようです。 作成手順に関してはやはりこちらの記事が詳しいです。 iOSアプリ Provisioning Profile を作ってみる

リリースの流れ

1. App IDを作成
2. AppStoreConnect上でアプリを作成
3. XcodeでArchive

以上になります。下記に1つ1つについて詳しく書きます。

1. App IDを作成

Apple Developer へ飛びます。画像のようにApp IDを新規作成します。f:id:yooska14:20181204163459p:plain

  • App ID Description(IDの説明)
  • Explicit App ID
  • App Services を入力します。Explicit App IDはつまりBundle IDです。(Xcodeで設定するやつ・アプリ固有のID) 基本的にはリバースドメイン(アプリ名を逆にしたやつ)が推奨されています。 例: news.oned.jp => jp.oned.news

App Servicesに関しては、使用している機能だけにcheckが基本です。checkしていない機能を使っていたら審査時にリジェクトされることは当たり前ですが、逆に使ってない機能にcheck入れすぎてると怒られるらしいです。(経験なし) が、実際、PushNotificationsとか「後々使うよなー」って機能は入れちゃってます。怒られた試しはありません。「とりあえず全部チェック入れとく」とか適当なことせずにいくつかだけチェック入れるなら良いんだと思います。(Appleも審査大変やろ)

以上3つを全て入力し、Continue => Submitすればdoneです。

2. AppStoreConnect上でアプリを作成

https://appstoreconnect.apple.com/ へアクセスし、「新規 App」を作成します。 f:id:yooska14:20181204165813p:plain

  • プラットフォーム(大体iOSでしょう)
  • 名前(アプリ名)
  • プライマリ言語(大体日本語でしょう)
  • バンドルID(「1. App IDを作成」を経ていたら選択肢として出るはずです)
  • SKU(Appleが内部で使うIDらしい。僕はbundleIDと一緒にしています。なんでも良さそう)
  • ユーザアクセス(「なし」で良いでしょう)

を入力します。 これで、Xcode上でArchiveができるようになります。

3. XcodeでArchive

f:id:yooska14:20181204171300p:plain 以上を入力して、メニューバーからProduct => Archiveすればアップロード完了です。

参考にした・関連おすすめ記事

i-app-tec.com qiita.com

【ReactNaitve】ReactNativeDebuggerが便利すぎた

開発し開発しながら書いてるので思いつき次第随時拡充していきます。

イメージ f:id:yooska14:20181021195617p:plain

これだけで大体分かる思うけど、 - stateの確認 - reduxアクション確認 - reduxアクション毎のstateの差分(diff)の確認 - 要素検証(Chromeの「検証」に近い) - console.logの確認 などが出来る。

これだけでも凄いなー便利だなーって思っていたけど、AsyncStorageの中身まで確認できちゃうと知って舞い上がった。 頑張ってconsole.logとかに中身をDumpしてた自分がかわいそう。。。

AsyncStoreの中身を確認する方法

consoleで

showAsyncStorageContentInDev()

をうつ。 すると中身がテーブル表記で綺麗に表示される。 f:id:yooska14:20181021200704p:plain 中身がない場合は No AsyncStorage content.が帰ってくる。 (ちゃんと確認せず「テーブル表示されないじゃねぇーかー!」ってなってしまった僕のような人へ。安心してください。ちゃんと確認すれば何かしら帰ってくる。)

f:id:yooska14:20181021200414p:plain

【ReactNative】git clone直後のBuild input file cannot be found: エラー解決方法

エラー

❌  error: Build input file cannot be found: 'path_to_project/node_modules/react-native/third-party/double-conversion-1.1.6/src/strtod.cc'


▸ Compiling fast-dtoa.cc

❌  error: Build input file cannot be found: 'path_to_project/node_modules/react-native/third-party/double-conversion-1.1.6/src/fast-dtoa.cc'


▸ Compiling fixed-dtoa.cc

❌  error: Build input file cannot be found: 'path_to_project/node_modules/react-native/third-party/double-conversion-1.1.6/src/fixed-dtoa.cc'

解決

Xcode 10: Build input file double-conversion cannot be found · Issue #21168 · facebook/react-native · GitHub

【ReactNative】Androidリリースをイチからメモ

流れ

  1. 鍵を生成・配置
  2. gradle.propertiesにgradle変数を追加
  3. build.gradleにsigningConfigsを追加
  4. APKファイルを生成
  5. APKファイルをplay storeへアップロード

1. 鍵を生成・配置

生成

keytool -genkey -v -keystore your_app_name.keystore -alias your_app_name -keyalg RSA -keysize 2048 -validity 10000

your_app_nameの部分を適宜変更する

姓名・組織単位名・組織名・都市名・州名・国番号などを聞かれるので適宜答える。 あまり重要じゃなさそう。

※国番号に関しては、日本の場合はJPとする。

配置

生成されたキー(your_app_name.keystore)をandroid/appへ配置。

2. gradle.propertiesにgradle変数を追加

android/gradle.propertiesに以下を記述

MYAPP_RELEASE_STORE_FILE=your_app_name.keystore
MYAPP_RELEASE_KEY_ALIAS=your_app_name
MYAPP_RELEASE_STORE_PASSWORD=your_password
MYAPP_RELEASE_KEY_PASSWORD=your_password

他のgradleファイルから参照される値なのでキーは独自に変えても良いと思うが、公式でもこのキーが例に上がっているので素直に従っておく。

3. build.gradleにsigningConfigsを追加

...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
...

siginigConfig {}とsigningConfig signingConfigs.releaseの部分を追加。

4. APKファイルを生成

cd android && ./gradlew assembleRelease を実行。 すると android/app/build/outputs/apk/release/app-release.apkファイルが生成される。これがアプリ本体らしい。

5. APKファイルをplay storeへアップロード

https://play.google.com/apps/publish へアクセスし、左サイドバーのリリース管理 > アプリのリリースからAPKファイルをアップロードする。「リリース名」や「このリリースの新機能」などを記入して公開する。 iOSと違い、Androidのリリースは審査なしですぐに反映される。

2回目以降のリリースについて

ここに書いた。 kang.hateblo.jp

参考

公式ドキュメント facebook.github.io

【ReactNative】コスパ良くクオリティ上げるならとりあえずLayoutAnimation入れとこ

苦労せずになんとなーくアプリのクオリティを上げたいあなたにおすすめです。 Animationを適用したいScreenのcomponentWillUpdateにでも書いておきます。

  import { LayoutAnimation } from 'react-native'

  ---------------------------------------------

  componentWillUpdate() {
    LayoutAnimation.easeInEaseOut()
  }

↓は開発中のアプリに適用してみた例。 「パッ」じゃなくて「フワッ」っとなってる感じ。。。わかるかな?w

f:id:yooska14:20181021003043g:plain

f:id:yooska14:20181021003043g:plain
開発してるアプリに導入してみた

アニメーションのオプションは

  • easeInEaseOut
  • linear
  • spring

があるみたいなので、全部試して好みを見つけると良さそうです。 個人的にはScreenに適用するならeaseInEaseOut一択です。 こんな感じでもっとかっちょいいアニメーションにもできる。

LayoutAnimation.configureNext({
  duration: 700,
  create: {
    type: 'linear',
    property: 'opacity',
  },
  update: {
    type: 'spring',
    springDamping: 0.4,
    property: 'scaleXY',
  },
  delete: {
    type: 'linear',
    property: 'opacity',
  },
})

※参考: https://blog.callstack.io/react-native-animations-revisited-part-i-783143d4884

僕はそんなに凝れないので極力やらないが、

onPress()なんかの最初に書いておいて発火されるたびにAnimationをかけることもできる。

※参考: rskull.hateblo.jp

特別な理由がなければ、ほぼ全てのScreenに入れて良いはず。 これ入れとくだけで「あ、ちゃんと凝ってんな」って思われそう。

【Shell】GithubのPullRequest作成ページを一発で開く関数(ワンライナー)

関数の定義

.zsh_alias.bash_aliasに書いておく。

# カレントブランチから$1ブランチへのPullRequestを開く
function opr() {
  parentBranch=$1
  currentBranch=`git branch | grep "*"`
  repoName=$(git remote show origin -n | ruby -ne 'puts /^\s*Fetch.*:(.*).git/.match($_)[1] rescue nil')

  open -a /Applications/Google\ Chrome.app  https://github.com/${repoName/* /}/compare/${parentBranch/* /}...${currentBranch/* /}
}

使用例

opr dev (currentブランチからdevブランチへ) opr master (currentブランチからmasterブランチへ) など。

簡単に解説

第一引数$1にターゲットブランチを渡す。 git branch | grep "*"で現在のブランチを取得出来るので、そこに対してPullRequestを出している。

補足

ChromeアプリへのPathは自分のPC内のに合わせてください。 もちろん、safarifirefoxでも可。