2020-08-02

Expoの設定(app.json)をtypescriptで動的に記述する

Expoの設定ファイルについて

Expoではアプリのメタ情報や設定値をapp.jsonに記載します。ただapp.jsonはstaticなJSONファイルのためそのままだと環境別(例えばdevelopment, staging, productionのように)に値を指定できません。

そこで環境別に設定値を記述するにはどうしたらよいか説明します。

--configオプションを指定する方式はdeprecated

以前のExpoでは以下のように起動時に--configオプションで設定ファイルを指定することができましたが、その方式についてはdeprecatedになっているため、利用しないでください。

expo start --config app.dev.json
expo start --config app.production.json

fyi/config-flag-migration.md at master · expo/fyi

設定を動的に切り替えるには

設定を動的に切り替える場合、app.config.jsまたはapp.config.tsをエントリファイルとして利用します。環境別にエントリファイルを分けるのではなく、同一のエントリファイルで中身を動的にスイッチする仕組みです。

以下のように環境変数を指定して起動し

APP_ENV=dev expo start

app.config.tsでその環境変数に応じて設定の中身を動的に切り替えます。

import { ConfigContext } from "@expo/config"

export default ({ config }: ConfigContext) => {
  const appEnvName = process.env.APP_ENV || "dev"

  return {
    ...config,
    // 動的な記述を追加
  }
}

共通(デフォルト)の設定項目

環境によって変わらない部分(またはデフォルト値)についてはapp.json(またはapp.config.json)に記載しておくとよいです。Expoの起動時、app.jsonが存在する場合はまずそれを読み込んだ上でapp.config.tsが読み込まれるためです。

// app.jsonにデフォルト設定を記載
{
  "expo": {
    "name": "xxxxxx",
    "slug": "xxxxxx",
    "orientation": "portrait"
    ...
  }
}

app.jsonの中身はfunctionの引数として参照できます。

環境別の設定項目をファイルに分ける

app.config.tsから更に環境別に用意した設定ファイルをインポートしたいケースがあると思います。その場合、app.config.dev.ts等、tsファイルで用意しておきたいところですが、app.config.tsからはtsファイルをimportすることはサポートされていません。

expo/configuration.md at main · expo/expo

Importing/requiring other JavaScript files. Using import/export syntax in external files is not supported. All imported files must be transpiled to support your current version of Node.js.

そのためjsファイルまたはjsonファイルで用意しておく必要があります。

tsファイルにしておきtranspileするという手もありますが、管理の手間が増えてしまうため辞めたほうがよいと思います。また、設定ファイルを分けずにすべての環境の設定をapp.config.tsに記載してしまうというのも一つの手段です。

以下のどちらかでファイルを用意しておき、

// app.config.<env>.json
{
  name: "yyyyy",
  ...
}
// app.config.<env>.js
module.exports = {
  name: "yyyyy",
  ...
}

app.config.tsからimportして利用します。個別の設定はapp.jsonとは異なりルートに{ "expo": {}} は記述する必要が無い点に注意してください。

import { ConfigContext } from "@expo/config"
import merge from "deepmerge"
import devConfig from "./app.config.dev"
import productionConfig from "./app.config.production"

type APP_ENV_NAME = "dev" | "production"

const envMapping: { [key in APP_ENV_NAME]: Record<string, any> } = {
  dev: devConfig,
  production: productionConfig,
}

export default ({ config }: ConfigContext) => {
  const appEnvName = process.env.APP_ENV || "dev"
  if (!["dev", "production"].includes(appEnvName)) {
    throw new Error(`APP_ENV: ${appEnvName} is not supported.`)
  }

  const envConfig = envMapping[appEnvName as APP_ENV_NAME]
  const o = merge(config, envConfig)
  return o
}

アプリから設定値を読み込む

アプリのコードからexpo-constantsを使って設定値にアクセスさせたい場合、以下のようにextraフィールドに環境変数の値を設定しておきます。

{
  extra: ["APP_NAME", "API_ENDPOINT"].reduce((envs, key) => ({
    ...envs,
    [key]: process.env[key],
  }), {})
}

コードからは以下のようにアクセスできます(Expo SDKが46以降の場合)。

import Constants from 'expo-constants'

console.log("API_ENDPOINT:", Constants.expoConfig.extra.API_ENDPOINT)

Expo SDKが45以前の場合、Constants.manifestを使います。

import Constants from 'expo-constants'

console.log("API_ENDPOINT:", Constants.manifest.extra.API_ENDPOINT)

アプリビルド時の設定

開発環境ではExpoサーバの起動時にAPP_ENV=dev expo startのように環境変数を指定できますが、EAS Buildでアプリバンドルをビルドする場合はサーバを起動するわけではないため、別途APP_ENVを渡す設定が必要です。

EAS Buildで環境変数を渡すにはeas.jsonで各プロファイルの"env"フィールドに項目を追加します。最低限APP_ENVだけ渡してあげれば、あとはapp.config.tsでその値に応じて設定を切り分けることができます。

{
  "build": {
    "dev": {
      "env": {
        "APP_ENV": "dev"
      }
    },
    "production": {
      "env": {
        "APP_ENV": "production"
      }
    }
  }
}

eas.jsonに個別の環境変数をすべて設定してしまうこともできますが、eas.jsonの環境変数は開発時のExpoサーバ起動時に読み込まれないため、結局重複した設定を更に別の箇所に記載しなくならなくなってしまうため辞めたほうがよいです。

また、もしも値をGit上で管理せず秘密にしたい場合、EAS BuildのSecretsを利用するのがよいと思います。この値は開発時(Expoサーバ起動時)には参照できないため、ビルドされたアプリバンドルからのみ使う想定になります。

Environment variables and secrets - Expo Documentation

参考

最終更新: 2022-10-15 02:00
筆者: @gaishimo 主にReact Nativeでのアプリ開発を行っています。
© 2021 Omoidasu, Inc. All rights reserved.