こんにちは、皆さん!

今回は兼ねてからずっと作っていたモバイルアプリ、「ツイートスケジュラー」をExpoベースからReact Nativeベースに変更した時の話をしたいと思います。ツイートスケジュラーについて詳しくはこちらからお願いします↓


トピック


経緯

Expoを使っていると、やはりReact Native用のライブラリが使えない場合が出てきます。

今回はツイートのための写真を選択する時にExpoのままだと、expo-image-pickerしか選択肢がなく、これでは画像の同時複数選択もできないし、動画を選択することもできません。

ということでそろそろ潮時かなということでExpoから離れることにしました。

他にもreact-native-fast-imagereact-native-chart-kitなどのライブラリも使用できるようになるので、楽しみでした。


環境

今回使用した環境は以下の通りです。

  • Expo SDK: 36.0.0
  • React Native: 0.61.4 (0.60.0以上はreact-native linkコマンドを自動でやってくれます)
  • React: 16.9.0
  • Typescriptベース

実際にやったこと

実際に僕の場合どうだったのかに入っていく前に、ピュアのReact Nativeアプリを走らせる手順について一応説明しておきます。

  1. React NativeのMetroサーバー立ち上げ
  2. iOSとAndroidそれぞれのコマンドでそれぞれのシミュレーターを使ってアプリを操作(Androidのシミュレーターは予め経っている必要あり)
  3. 発生したエラーの処理(Ejectした際には何かしらのエラーが発生する可能性が高い)

Ejectコマンドを使う

最初はまず、ExpoアプリからReact Nativeアプリへ移行するためのコマンドを走らせるところからでした。

expo eject

このコマンドを実行すると、二つの選択肢を与えられます。一つはBare React Nativeと言って、もう完全にピュアなReact Nativeに移行するパターンで、もう一つはExpoKitと言って、React Nativeのようにネイティブコードをいじることができる傍ら、Expoも残しているというものです。僕は前者を選んだので、後者の方は詳しくないのですが、後者の場合、React NativeとExpoの両方を必要とするため、サイズが大きくなったりする欠点があるそうです。

前者を選んでからいろいろ必要なものがあちこちに足されます。大きな部分としてはiosフォルダーとandroidフォルダー、そして、package.jsonファイルです。

二つのフォルダーが追加され、新たなコマンドなどがpackage.jsonファイルに追加されます。

コマンドの処理が終わった際に何か変更を要するパッケージがあった場合、そこで教えてくれます。

僕の場合は下記の二つのパッケージにおいて、インストール以外にいくつかやることがありました。

  • expo-image-picker
  • expo-ads-admob

それぞれのGithub上にちゃんと何をすればいいのか載っていました。

expo-ads-admobに関してはiOS側でinfo.plistファイル内にadmobのIDを入れる必要がありました。

https://github.com/expo/expo/tree/master/packages/expo-ads-admob#configure-for-ios

expo-image-pickerに関してはiOSとAndroidのそれぞれに画像アクセスの権限を付随してあげないといけないというものでした。

https://github.com/expo/expo/tree/master/packages/expo-image-picker#configure-for-ios


ポートの処理

そしたら、早速React NativeのMetroサーバーを立てて、それぞれのシミュレーターからアクセスできるようにしていきます。

サーバーを起動するために下記のコマンドを使います。

npx react-native start

最初はここではまりました。このサーバーはデフォルトではポート8081にマッピングするのだが、自分のコンピュータ内では既にそのポートが使われているようでしたので、下記のようなエラーメッセージが出てきました。ただ、localhost:8081みたいな感じでアクセスしても何も出てこないので、全然わかりませんでした。

Error: listen EADDRINUSE :::8081
    at Server.setupListenHandle [as _listen2] (net.js:1360:14)
    at listenInCluster (net.js:1401:12)
    at Server.listen (net.js:1485:7)
    at D:\rnprojects\testproject\node_modules\metro\src\index.js:156:18
    at new Promise (<anonymous>)
    at Object.<anonymous> 
(D:\rnprojects\testproject\node_modules\metro\src\index.js:155:12)
    at Generator.next (<anonymous>)

グーグル検索してたら、このGithubでヒントを得られました。

https://github.com/facebook/react-native/issues/705

この中の下記のコマンドを使い、自分のポート8081で何が使われているのか確認したところ、Mcafeeっていうウィルス対策のソフトウェアがそこを使用していたようでした。

sudo lsof -nP -iTCP:8081 -sTCP:LISTEN

というわけで、そこのポートを使用しているソフトウェアを止めるために下記のコマンドを試しました。(僕の場合はこのソフトウェアがPID:2263を使用していました。)

sudo kill 2263

これやった後に、先ほどのコマンドでポート8081に何かいるかどうか確認して、何もなければ問題ないのですが、このMcafeeは結構めんどくさいことに、ストップするたびに新しいのをどんどん作り出してしまいます。よって、結果的にMcafeeをアンインストールすることにしました。(後から考えましたが、Mcafeeのマッピングするポートを変更するでも大丈夫でした。)

アンインストールに関しては下記のサイトを参考にしました。


Podfileをいじる

iOSに関するパッケージをインストールする際にはRubyで書かれたCocoaPodsがとても便利です。

React Nativeはデフォルトでこちらを使用しているのですが、僕の場合、使用した際にいくつか問題がありました。

まず、一つ目がIPHONEOS_DEPOLYMENT_TARGETが適合しないという問題でした。

こちらはiOSのシミュレーターを使用する際にどのバージョンのOSシステムを使用するか指定できるところです。基本的には8から13あたりまでしか適合しないように設定されています。しかし、CocoaPodsで必要なパッケージをインストールした際に、ここでは4.3バージョンが登録されました。

おかしいなと思い、.xcodeprojファイルを確認したのですが、そこでは問題なく9を指定していたので、混乱しました。

結局、これはPodfile内で、必要パッケージをインストールした後に、この部分を無理やり変える方法で解決しました。下記のような1行をPodfile内に追加しました。

post_install do |pi|
  pi.pods_project.targets.each do |t|
    t.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
    end
  end
end

これによって、IPHONEOS_DEPLOYMENT_TARGETを見かけ次第、それを9に変更してくれるようになります。

参考にしたサイトはこちらです。

https://stackoverflow.com/questions/54704207/the-ios-simulator-deployment-targets-is-set-to-7-0-but-the-range-of-supported-d


次に二つ目はReact Native Configの問題でした。これは僕もあまり深く理解していないのですが、どうもReact Native Configの設定に何かしらの問題があるみたいです。

iOSをスタートさせようとした際に下記のようなメッセージが出ました。

'GeneratedDotEnv.m' file not found

下記のサイトを参考にしました。

https://github.com/luggit/react-native-config/issues/187#issuecomment-514857871

これを踏まえて、僕のPodfileで追加した部分は最終的に下記のようになりました。

post_install do |pi|
  pi.pods_project.targets.each do |t|
    t.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
    end
    if t.name == 'react-native-config'
      phase = t.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
      phase.shell_script = "cd ../../"\
                            " && RNC_ROOT=./node_modules/react-native-config/"\
                            " && export SYMROOT=$RNC_ROOT/ios/ReactNativeConfig"\
                            " && export BUILD_DIR=$RNC_ROOT/ios/ReactNativeConfig"\
                            " && ruby $RNC_ROOT/ios/ReactNativeConfig/BuildDotenvConfig.ruby"

      t.build_phases << phase
      t.build_phases.move(phase,0)
    end
  end
end

これをやった後に、iosフォルダー内に入り、そこで下記のようにコマンドを走らせて、作成されたPodsフォルダーの内容を変更しました。

pod deintegrate
pod clean
pod install

PodfileはPodfile.lockもついていたりと、RailsのGemfileそっくりでしたので、とてもやりやすかったです。


Expoパッケージを削除する

EjectしたあとはもうExpoアプリではなくなるので、Expo SDKを使用しているExpoパッケージがもう使えなくなります。(expoがダメなだけで、expo-ads-admobexpo-image-pickerなどのexpoが一部分についたパッケージには使えるものもあります。)

僕の場合は下記の二つを使用していました。

  • AuthSession
  • ScreenOrientation

Expoをインポートしている箇所があると、画面上では下記のようなエラー画面になりました。

Expoがあるときのエラー画面

AuthSessionに関しては下記のライブラリを使うことで問題を解決できました。こちらは関数などの内容も元々のExpoのパッケージにそっくりでしたので、インポート先を変える程度の修正で済みました。

getRedirectedUrl関数をお使いの場合はhttps://expo.auth.io/がもう使えなくなるため、アプリに戻ってきてもらうためにもDeep Linkの設定をネイティブコード内でする必要がありますので、その場合は少し気をつけてください。

https://www.npmjs.com/package/expo-auth-session

ScreenOrientationに関しては僕がスクリーンをロックしたくて使っていたものでしたが、下記の記事を参考にしたところ、ネイティブコードを少しいじる必要があるみたいです。

https://medium.com/building-with-react-native/how-to-lock-device-orientation-for-react-native-apps-android-ios-2×02-952c42cb51b1


この次にはexpo-constantsの方で少し問題が起きました。

expo-constantsのエラー

こちらのRelease Channelで元々アプリがどの環境にいるのかを判定していたのですが、ここからはapp.jsonに入っているものしか指定できなくなりますので、.envファイルを使用するように変更をすることにしました。

ここで使ったパッケージが下記のものです。

https://www.npmjs.com/package/react-native-dotenv

この際、使っていたアプリがTypescriptで書かれていたため、react-native-dotenvからインポートする際にタイプのエラーが発生しました。それを直すために下記の記事を参考にしました。

https://theisomorphic.dev/adding-dotenv-to-your-typed-react-native-project

僕の場合、.envファイルがとりあえずは下記のようになっていました。

ENV=dev

それで、ENVという変数が文字列のタイプで存在するので、先ほどの記事で追加したvendor-typings.d.tsファイルを下記のように変更しました。

declare module 'react-native-dotenv' {
    export var ENV: string
}

その後、node_modules/react-native-dotenvへのパスがなかなかうまくいかなかったのですが、こちらを参考にしてやったら、うまく行きました。ここではExpoの時からあった、babel.config.jsファイルに下記のものを入れました。

https://github.com/zetachang/react-native-dotenv/issues/30#issuecomment-549752231


Deep Linkの修正

次にDeep Linkがうまく設定されていなくて、ツイッターでログインした後に、アプリに帰ってこれないという問題が発生しました。こちらに関しては下記の記事を参考にしながら、下記のコードをiOSとAndroidそれぞれに追加しました。

https://medium.com/react-native-training/deep-linking-your-react-native-app-d87c39a1ad5e

	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>tweet-scheduler</string>
			</array>
		</dict>
	</array>

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
  return [RCTLinkingManager application:app openURL:url options:options];
}

        <intent-filter android:label="filter_react_native">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="tweet-scheduler" android:host="" />
        </intent-filter>

これらを追加した後はちゃんとアプリの方に戻ってくることができました。僕の場合はtweet-schedulerっていうSchemeを使っていますが、皆さんはご自身のをお使いください。

ここまでやった地点で、シミュレーター内のブラウザでtweet-scheduler://と打つと、このアプリが開かれるようになりました。


Splashスクリーンの設定

これが正直少しめんどくさかったのですが、Ejectした際に、元々あった、アプリが起動した時の背景写真が無効になったため、下記のサイトにしたがって、一つずつ必要なものを追加していき、なんとかアプリ起動時の背景写真が元の状態と同じものになるように変更できました。

https://medium.com/@appstud/add-a-splash-screen-to-a-react-native-app-810492e773f9


まとめ

そんななんだかんだで最終的には完全にピュアのReact Nativeに移行することができました。

そこからは自分の付けたかったライブラリをどんどん足していって改良を重ねました。

Expoについて振り返ってみると、モバイル開発に対して疎かった自分自身に第一歩を簡単に踏み出す勇気をもらうことができましたので、とても感謝しています。Expoでのモバイル開発で少しずつだが、iOSとAndroidのアプリを公開する方法などを学んで成長して、こうしてExpoのサポートを借りずにピュアなReact Nativeでの開発に着手することができました。

モバイル開発をこれから始めるみなさんも是非一度、Expoの恩恵を一度受けてからネイティブ要素が強いピュアなReact Nativeに移行しましょう!!


皆さんの役に立つことを願います!
では、また次回まで✌
記事更新はツイッターで告知するので、ツイッターの方でもフォローお願いします!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です