React Native 0.55.2->0.56.0にアップグレードした作業ログ

## この記事の内容

- 仕事で使っているアプリを0.55.2 -> 0.56.0にアップグレードしたので、その際試した方法、ハマった内容などを共有
- マイグレーションは大変と噂ですが、割と大変

## はじめに

- 公式ガイド: https://facebook.github.io/react-native/docs/upgrading.html
- リリース内容: https://github.com/facebook/react-native/releases
- 方法としては
    1. 公式の `react-native-git-upgrade` を使ってバージョン間の変更を適用する
    2. 指定バージョン間のdiffを見るツール https://github.com/ncuillery/rn-diff/compare/rn-0.55.2...rn-0.56.0 で変更をみつつ自力でマイグレーションする
    3. `package.json` 書き換えるなり `react-native init` して差分見るなりしてがんばる
- などがあります。今回は1をやろうとして3に切り替えましたが、2を見つつ3が一番楽そう
- 今回の対象プロジェクト は [lerna](https://github.com/lerna/lerna) のmonorepoで、multi packege構造だった
- やること
    - packege.json の `react` , `react-native` のバージョンアップ
    - interfaceの変更に伴い `build.gradle` や `xxx.xcodeproj` や `.flowconfig` なども必要なら書き換える
    - それにより他のライブラリやJSコード、ネイティブコードが動かなくなるので、一つ一つエラーを見て直していく
    - 後は気合

## 0.56の変更点

- https://github.com/facebook/react-native/releases から大きいところ
- 2018/4/4の0.55から816コミット分の変更
- Babel 7対応
- Android 26 SDK対応
- Node 8 がminimum required versionに
- iOS9 が minimum required versionに
- Xcode 9 が minimum required versionに
- 他にも100くらいの変更あるので公式を参照

## プロジェクトのディレクトリ構造

色々省略してますが

```
$ tree -L 2 -I node_modules
.
├── lerna.json
├── package.json
├── packages
│   ├── network
│   ├── domain
│   ├── i18n
│   └── main
├── tsconfig.json
├── tslint.json
├── vendor
│   └── bundle
└── yarn.lock
```

- `packages/main` がメインのアプリケーションのソースコードで、react-nativeに依存しているのはそこだけ
- `packages/main` 内にあるpackege.json 内の `dependencies` に `network` や　`domain` 等のパッケージへの依存が書かれてます

## プロジェクトの事情

- ReactNative触るのは全員モバイルエンジニア(Mac)
- React Nativeを一からTypeScriptで開発しているプロジェクトで、git cloneしてきたらとりあえず `init.sh` 叩く、みたいにしてる
- `init.sh` が行なっているのは `npm install` -> `bundle install` -> `pod install` -> `tsc build` の一連の環境設定の流れ(ほんとはデベロップメントサーバー起動とか色々ある)

### react-native-git-upgrade試した

```
$ yarn global add react-native-git-upgrade
$ cd packages/main
$ react-native-git-upgrade
```

### エラー xxxx(パッケージ名): Not found

lernaの独自パッケージが参照できずにエラー発生

```
error An unexpected error occurred: "https://registry.yarnpkg.com/xxxx: Not found".
git-upgrade ERR! An error occurred during upgrade:
git-upgrade ERR! Error: Command 'yarn add react-native@0.56.0 react@16.4.1' exited with code 1:
stderr: undefined+ cd ../xxxx
```

- `react-native-git-upgrade` を実行すると `react` と `react-native` パッケージをアップグレードして、同時に公式レポジトリに入っている変更を適用しようとする
- lernaには対応してないので `npm update` に失敗するので、mainパッケージのエラーになったdependenciesを消してみる

### 再度react-native-git-upgrade実行

普通にいくと下記みたいな出力が出る

```
git-upgrade info Check for updates 
git-upgrade info Using yarn 1.7.0 
git-upgrade info Read package.json files 
git-upgrade info Check declared version 
git-upgrade info Check matching versions 
git-upgrade info Check React peer dependency 
git-upgrade info Check that Git is installed 
git-upgrade info Get information from NPM registry 
git-upgrade info Upgrading to React Native 0.56.0, React 16.4.1 
git-upgrade info Setup temporary working directory 
git-upgrade info Configure Git environment 
git-upgrade info Init Git repository 
git-upgrade info Add all files to commit 
```

- conflictが発生

```
z1Vi_Zy=U>ksq-G_fM)m29;BR$>lFp~S*{+8W;-V<x(&S9j{qRW5YUQ=V&qVCbh(gE
z1LtGy9zHsxDTSaBmKCgK2%rMY$WK9|m-LzMgv@jUVUKukgw!~V#?v<}@9dn>zpOg_
z{-6B_%gK~tgzphw9K=
    at ChildProcess.child.on.code (/Users/username/.config/yarn/global/node_modules/react-native-git-upgrade/cliEntry.js:58:18)
    at emitTwo (events.js:126:13)
    at ChildProcess.emit (events.js:214:7)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)
git-upgrade ERR! Restore initial sources
error: Your local changes to the following files would be overwritten by checkout:
	node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj/xcuserdata/username.xcuserdatad/xcschemes/xcschememanagement.plist
	node_modules/react-native/React/React.xcodeproj/xcuserdata/username.xcuserdatad/xcschemes/xcschememanagement.plist
Please commit your changes or stash them before you switch branches.
Aborting
Error: Command 'git checkout project-snapshot' exited with code 1:
stderr: undefinederror: Your local changes to the following files would be overwritten by checkout:
	node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj/xcuserdata/username.xcuserdatad/xcschemes/xcschememanagement.plist
	node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj/xcuserdata/username.xcuserdatad/xcschemes/xcschememanagement.plist
	node_modules/react-native/React/React.xcodeproj/xcuserdata/username.xcuserdatad/xcschemes/xcschememanagement.plist
Please commit your changes or stash them before you switch branches.
Aborting

stdout:
    at ChildProcess.child.on.code (/Users/username/.config/yarn/global/node_modules/react-native-git-upgrade/cliEntry.js:58:18)
    at emitTwo (events.js:126:13)
    at ChildProcess.emit (events.js:214:7)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)
```

- 全部の出力だとすごい長いので省略してます
- この出力見てもよく分からないけど、react-nativeの本家の変更がgitのchangesに出てくる
- コマンドの実行結果はdiffみたいな内容が出力されることもある
- `git status` の結果に最新のReact Nativeとの差分(https://github.com/ncuillery/rn-diff/compare/rn-0.55.2...rn-0.56.0 と大体一緒)が出ている

```
$ git status
On branch feature/rn_migration
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   .babelrc
	modified:   .flowconfig
	modified:   android/app/build.gradle
	modified:   android/app/proguard-rules.pro
	modified:   android/app/src/main/AndroidManifest.xml
	modified:   android/app/src/main/res/mipmap-hdpi/ic_launcher.png
	modified:   android/app/src/main/res/mipmap-mdpi/ic_launcher.png
	modified:   android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
	modified:   android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
	modified:   android/app/src/main/res/values/strings.xml
	modified:   android/app/src/main/res/values/styles.xml
	modified:   android/build.gradle
	modified:   android/gradle.properties
	modified:   android/gradle/wrapper/gradle-wrapper.properties
	modified:   android/settings.gradle
	modified:   app.json
	modified:   package.json
	modified:   yarn.lock

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.gitattributes
	.gitignore
	.watchmanconfig
	android/app/BUCK
	android/app/src/main/java/com/
	android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
	android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
	android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
	android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
	android/app/src/main/res/mipmap-xxxhdpi/
	android/keystores/
	ios/main-tvOS/
	ios/main-tvOSTests/
	ios/main.xcodeproj/
	ios/main/
	ios/mainTests/

no changes added to commit (use "git add" and/or "git commit -a")
```

- 特に取り込むべき変更が見当たらなかったのでpackage.jsonだけ書き換える方針に切り替え(flow typeとか使ってたら `.flowconfig` など取り入れるべきと思います)
- git の変更を全て破棄してから下記を変える

```
-    "react": "16.3.1",
-    "react-native": "0.55.2",
+    "react": "16.4.1",
+    "react-native": "0.56.0",
```

## 起動確認

- init.shで環境整備(npm install, tsc buildなど諸々)し、 `react-native run-android` などで実機起動する
- 当然途中で諸々のエラーが発生する

## エラー CocoaPodsで"Folly"が見つからないエラー

- `pod install` の途中で下記のエラー

```
[!] CocoaPods could not find compatible versions for pod "Folly":
  In snapshot (Podfile.lock):
    Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)

  In Podfile:
    Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)

None of your spec sources contain a spec satisfying the dependencies: `Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`), Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)`.

You have either:
 * out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`.
 * mistyped the name or version.
 * not added the source repo that hosts the Podspec to your Podfile.

Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by default.
```

`ios/Podfile.lock` と `ios/Pods/` を消して再度 `pod install`

### エラー Could not find com.facebook.react:react-native:0.xx.x.

- `react-native run-android` 失敗した

```
FAILURE: Build failed with an exception.

* What went wrong:
Could not resolve all files for configuration ':app:debugCompileClasspath'.
> Could not find com.facebook.react:react-native:0.55.2.
  Searched in the following locations:
      file:/Users/username/Library/Android/sdk/extras/m2repository/com/facebook/react/react-native/0.55.2/react-native-0.55.2.pom
```

- build.gradle内で読み込むreact-nativeのバージョンが違う
- package.jsonのバージョンと合わせる

build.gradleの修正

```
-implementation "com.facebook.react:react-native:0.55.2"
-implementation "com.facebook.react:react-native:0.56.0"
```

### エラー Cannot find module 'babel-preset-react-native-dotenv'

- 環境変数の定義に [`react-native-dotenv`](https://github.com/zetachang/react-native-dotenv) を使っているが、実機でテストするとエラーが発生

```
Loading dependency graph, done.
error: bundling failed: Error: Cannot find module 'babel-preset-react-native-dotenv' from '/Users/username/Desktop/workspace/packages/main'
- If you want to resolve "react-native-dotenv", use "module:react-native-dotenv"
    at Function.module.exports [as sync] (/Users/username/Desktop/workspace/packages/main/node_modules/resolve/lib/sync.js:42:15)
```
`babel-preset-react-native-dotenv` moduleが見つからない
https://github.com/zetachang/react-native-dotenv/issues/44

#### 対応方法

- バージョン `0.2.0` を指定すればOK
- 当時 `react-native-dotenv` がv56に対応していなかったので暫定でforkされたrepositoryを参照するよう変更した
- .babelrcを下記に変更

```
{
  "presets": ["react-native", "module:react-native-dotenv"]
}
```

- package.json修正

```
-    "react-native-dotenv": "^0.1.1",
+    "react-native-dotenv": "^0.2.0",
```

### エラー babel-preset-react-native のバージョンが低い

```
Loading dependency graph, done.
error: bundling failed: TypeError: Cannot read property 'bindings' of null
    at Scope.moveBindingTo (/Users/username/Desktop/workspace/packages/main/node_modules/@babel/traverse/lib/scope/index.js:978:13)
    at BlockScoping.updateScopeInfo (/Users/username/Desktop/workspace/packages/main/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:364:17)
    at BlockScoping.run (/Users/username/Desktop/workspace/packages/main/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:330:12)
    at PluginPass.BlockStatementSwitchStatementProgram (/Users/username/Desktop/workspace/packages/main/node_modules/babel-plugin-transform-es2015-block-scoping/lib/index.js:70:24)
    at newFn (/Users/username/Desktop/workspace/packages/main/node_modules/@babel/traverse/lib/visitors.js:237:21)
    at NodePath._call (/Users/username/Desktop/workspace/packages/main/node_modules/@babel/traverse/lib/path/context.js:65:20)
    at NodePath.call (/Users/username/Desktop/workspace/packages/main/node_modules/@babel/traverse/lib/path/context.js:40:17)
    at NodePath.visit (/Users/username/Desktop/workspace/packages/main/node_modules/@babel/traverse/lib/path/context.js:100:12)
    at TraversalContext.visitQueue (/Users/username/Desktop/workspace/packages/main/node_modules/@babel/traverse/lib/context.js:142:16)
```

- https://stackoverflow.com/questions/51178388/react-native-crashes-after-upgrade-to-version-0-56-possible-babel-issue
- package.jsonのbabel-preset-react-nativeのバージョンを変更
- リリースノートにも書いてある https://github.com/facebook/react-native/releases/tag/v0.56.0
- 古いプロジェクトだと `babel-bridge` を使った方が下位互換が保てる？今回は使いませんでした

```
"babel-preset-react-native": "^5".
```

### エラー error: bundling failed: Error: Unable to resolve module react-native/Libraries/Renderer/shims/ReactNativePropRegistry

- 実機実行してJSのロード中に [`NativeBase`](https://github.com/GeekyAnts/NativeBase/) のエラー

```
error: bundling failed: Error: Unable to resolve module `react-native/Libraries/Renderer/shims/ReactNativePropRegistry` from `/Users/username/Desktop/workspace/packages/main/node_modules/native-base/dist/src/Utils/computeProps.js`: Module `react-native/Libraries/Renderer/shims/ReactNativePropRegistry` does not exist in the Haste module map

This might be related to https://github.com/facebook/react-native/issues/4968
To resolve try the following:
  1. Clear watchman watches: `watchman watch-del-all`.
  2. Delete the `node_modules` folder: `rm -rf node_modules && npm install`.
  3. Reset Metro Bundler cache: `rm -rf /tmp/metro-bundler-cache-*` or `npm start -- --reset-cache`.  4. Remove haste cache: `rm -rf /tmp/haste-map-react-native-packager-*`.
    at ModuleResolver.resolveDependency (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:167:1306)
    at ResolutionRequest.resolveDependency (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/node-haste/DependencyGraph/ResolutionRequest.js:80:16)
    at DependencyGraph.resolveDependency (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/node-haste/DependencyGraph.js:237:485)
    at Object.resolve (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/lib/transformHelpers.js:116:25)
    at dependencies.map.result (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/DeltaBundler/traverseDependencies.js:298:29)
    at Array.map (<anonymous>)
    at resolveDependencies (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/DeltaBundler/traverseDependencies.js:294:16)
    at /Users/username/Desktop/workspace/packages/main/node_modules/metro/src/DeltaBundler/traverseDependencies.js:159:33
    at Generator.next (<anonymous>)
    at step (/Users/username/Desktop/workspace/packages/main/node_modules/metro/src/DeltaBundler/traverseDependencies.js:239:307)
```

- `node_modules/native-base/dist/src/Utils/computeProps.js` あたりで発生
- native-baseの最新バージョンで対応済み
- https://github.com/GeekyAnts/NativeBase/issues/2028

```
-    "native-base": "^2.4.1",
+    "native-base": "^2.7.0",
```

### エラー 型定義が変わった

- TypeScriptを使っている場合、型定義ファイルも更新しなければいけない
- TextInputのonFocusに引数が増えた

```
main: src/components/FormItem.tsx(98,76): error TS2554: Expected 1 arguments, but got 0.
main: src/components/FormItem.tsx(117,22): error TS2326: Types of property 'display' are incompatible.
main:   Type 'boolean' is not assignable to type 'undefined'.
```

- 型定義に合わせてコードを修正

### エラー jestが実行できない

- テスト対象ファイル全部ビルド失敗

```
Plugin 0 specified in "project/node_modules/babel-preset-react-native/index.js" provided an invalid property of "default" (While processing preset: "project/node_modules/babel-preset-react-native/index.js")
```

- 関連issue
    - https://github.com/facebook/react-native/issues/20050
    - https://github.com/facebook/react-native/issues/19859
- jest系のパッケージをまとめてアップデート

```
-    "babel-core": "^6.26.0",
+    "babel-core": "^7.0.0-beta.54",
-    "babel-jest": "22.4.3",
+    "babel-jest": "23.2.0",
-    "jest": "22.4.3",
+    "jest": "23.2.0",
-    "ts-jest": "^22.4.4",
+    "ts-jest": "^23.0.0",

  "jest": {
    "transform": {
      "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
    }
  }
```

### エラー jest で setTimeoutが動かない？

```
TypeError: Cannot read property 'debug' of undefined
    at /Users/username/Desktop/workspace/packages/main/dist/data/HogeLib.js:26:44
    at Timeout._onTimeout (/Users/username/Desktop/workspace/packages/main/dist/data/__test__/HogeLib.test.js:27:13)
    at ontimeout (timers.js:498:11)
    at tryOnTimeout (timers.js:323:5)
    at Timer.listOnTimeout (timers.js:290:5)
Jest did not exit one second after the test run has completed.
```

- 上記からtestが動かない現象、もしくはこれも出ないで永遠にyarn testが終わらない現象が起きました
- テストコードから不要なsetTimeoutを除去したら起こらなくなりました

### エラー Androidのlintが通らない

```
/Users/username/Desktop/workspace/packages/main/android/app/build/intermediates/res/merged/release/drawable-xhdpi/assets_img_ic_ic.png: error: uncompiled PNG file passed as argument. Must be compiled first into .flat file..
```

> uncompiled PNG file passed as argument. Must be compiled first into .flat file

- 関連issue
- https://github.com/facebook/react-native/issues/16906
- https://github.com/react-navigation/react-navigation/issues/3097
- aapt通してcompileすると `.flat` ファイルを想定してるのでエラーになる？
- `android/gradle.properties` に `android.enableAapt2=false` を追加すると直る、が2018年末にこの設定は削除される予定らしいので暫定対応。うちのプロジェクトだとこれ以外の修正方法がうまくいかなかったけど、どこかのタイミングで修正したい

### エラー AndroidのReleaseビルドが通らない

```
verifyReleaseResources/home/circleci/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/0af13b712ba9e9b567dee09bc2b2f546/res/values-v26/values-v26.xml:13:5-16:13:AAPT: No resource found that matches the given name: attr 'android:keyboardNavigationCluster'
```

- 使っているネイティブライブラリがAndroid SDK 26に対応していない
- react-native-navigation など四つぐらいあって、各レポジトリにPRやissueが上がっていた
- うちのテックリードがライブラリプロジェクトのSDKバージョンを書き換えるgradle task書いてくれた。超感謝

```
+ext {
+    compileSdkVersion = 27
+    targetSdkVersion = 27
+}
+
+subprojects {
+    afterEvaluate { project ->
+        if (project.hasProperty("android")) {
+            android {
+                compileSdkVersion rootProject.ext.compileSdkVersion
+            }
+        }
+    }
+}
```

## CircleCI落ちた

ipa作成時

```
[23:53:47]: ▸ ❌  ld: could not reparse object file in bitcode bundle: 'Invalid bitcode version (Producer: '902.0.39.2_0' Reader: '900.0.39.2_0')', using libLTO version 'LLVM version 9.0.0, (clang-900.0.39.2)' for architecture arm64
[23:53:47]: ▸ ❌  clang: error: linker command failed with exit code 1 (use -v to see invocation)
[23:53:47]: ▸ ** ARCHIVE FAILED **
```

- https://stackoverflow.com/questions/51128462/vsts-build-generation-not-working
- Bitcodeのバージョン違いでエラーが発生するケースがあるらしいので、Xcode(が中で使っているBitcode生成のLLVMバージョン？)のバージョンを上げたらうまく行った

## ビルド後、JSロード時に失敗する

```
lerna ERR! Unable to resolve module `../utils/mapPropsToStyleNames` from `/Users/username/project/packages/main/node_modules/native-base/dist/src/basic/Text.js`: The module `../utils/mapPropsToStyleNames` could not be found from `/Users/username/project/packages/main/node_modules/native-base/dist/src/basic/Text.js`. Indeed, none of these files exist:
```

- キャッシュが原因だった
- よく分からないとこでmodule読み込めないよって言われる
- node_modules と TypeScriptビルドしたコードを削除したら動く

## iOSがリロードでクラッシュ

- react-native-navigation のバージョンが古い

```
Fatal Exception: NSInvalidArgumentException
0  CoreFoundation                 0x1124771ce (Missing)
1  libobjc.A.dylib                0x1108ec031 objc_exception_throw
2  CoreFoundation                 0x1124f8784 (Missing)
3  UIKit                          0x10e97f6db (Missing)
4  CoreFoundation                 0x1123f9898 (Missing)
5  CoreFoundation                 0x1123f9278 (Missing)
6  app                       0x10b247421 -[RCCViewController sendGlobalScreenEvent:endTimestampString:shouldReset:] (RCCViewController.m:225)
7  app                       0x10b2480f9 -[RCCViewController viewDidDisappear:] (RCCViewController.m:293)
```

- https://github.com/wix/react-native-navigation/issues/3558
- 1.1.477 へ更新

## 参考リンク

ここら辺読んでからやりました。感謝
https://qiita.com/ponnjinnka/items/b1fdd200b1bc094c6797
https://qiita.com/hotchpotch/items/5617caccd8749f410631