Flutterのdart-defineで設定した環境変数をSwiftのソースコードやAndroidManifest.xmlで使用する
Flutter の--dart-define で環境変数を設定して iOS ネイティブである Swift のソースコードや Android のネイティブ設定である AndroidManifest.xml で環境変数を使用する方法です。
Google の API キーなどを秘匿情報をソースコードにハードコーディングして Github などに上げてしまうと、その API キーが悪用される恐れがあります。
本人が気づかないうちに API キーを悪用され膨大な利用料金が請求される可能性がある訳です。
API キーなどの秘匿情報は環境変数で設定してソースコードから値を取得するようにしましょう。
前提として、macOS、IDE は Android Studio を利用します。
環境
- macOS Big Sur 11.2.3
- Android Studio 4.1.3
- Flutter 2.0.3
- Dart 2.12.2
コマンドラインで環境変数を定義する
環境変数である dart-define は以下のフォーマットで設定します。
--dart-define=ENVIRONMENT_NAME=value
- 通常の開発時にコマンドラインのデバッグビルドで dart-define を定義する例
$ flutter run --debug --dart-define=GOOGLE_API_KEY=dummy_key
- Circle CI や Github Actions など CI 環境からリリースビルドで dart-define を定義する例
$ flutter run --release --dart-define=GOOGLE_API_KEY=dummy_key
開発時は Android Studio で Run や Debug を実行する方は Android Studio の Configurations に --dart-define
を設定できます。
Android Studio の環境変数設定方法こちらの記事で紹介してますので参照ください。
AndroidManifest.xml から環境変数の値を取得する
次は AndroidManifest.xml から環境変数の値を取得する方法です。
dart のソースコードから環境変数の値を取得するには flutter run
の引数や、Android Studio の Configurations に --dart-define
を設定しました。
AndroidManifest.xml は Android ネイティブ固有の設定ファイルなので、--dart-define
は利用できません。
--dart-define
はあくまで Dart のソースコードで取得できる環境変数です。
私が知る限りの対応としては 3 つあります。
flutter run or build
コマンドに環境変数を渡すlaunchctl
コマンドで環境変数を設定する- Android/iOS ビルド時に
--dart-define
の環境変数を渡す
前者 2 つはライトに利用できます。
ただそれぞれ欠点があり、flutter run or build
コマンドに環境変数を渡す方法は Android Studio の IDE に環境変数が渡せず、 Run や Debug の実行時には利用できませんでした。
VSCode では launch.json で環境変数を IDE に渡す設定できるようです。
launchctl
の方法は IDE に環境変数を渡すことができますが、 iOS で環境変数が取得する事ができませんでした。
筆者が試した限り、Android Studio 上で Run や Debug を利用したいかつ、iOS でも共通の環境変数を利用する場合は後者の --dart-define
を Android/iOS のネイティブ側に渡す必要がありそうです。
まずは各方法を見ていきます。
flutter run/build コマンドに環境変数を渡す
flutter run or build コマンド実行時に環境変数を渡す方法です。
やり方は以下の 2 通りあります。
export GOOGLE_API_KEY=dummy_key
flutter run
GOOGLE_API_KEY=dummy_key flutter run
launchctl コマンドで環境変数を設定する
launchctl コマンドを利用する方法です。
以下のコマンドで GOOGLE_API_KEY
環境変数をセットします。
launchctl setenv GOOGLE_API_KEY dummy_key
launchctl getenv
で設定した値が表示されれば成功です。
launchctl getenv GOOGLE_API_KEY
Android のビルド時に --dart-define
環境変数を渡す
Android ビルド時に環境変数を渡す方法です。
まず android/app/build.gradle
に以下のスクリプトを追記します。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
:
:
// ----------------------- Add start ------------------------//
// 環境変数を格納する配列
def dartEnvironmentVariables = [
GOOGLE_API_KEY: ''
]
// --dart-defineの読み込み
dartEnvironmentVariables =
dartEnvironmentVariables + project.property('dart-defines')
.split(',')
.collectEntries { entry ->
def pair = URLDecoder.decode(entry, 'UTF-8').split('=' as char)
[(pair.first()): pair.last()]
}
// ----------------------- Add end ------------------------//
:
:
android {
compileSdkVersion 30
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
これは build.gradle で Android のビルド時に --dart-define
の環境変数を取得、パースし配列に格納するスクリプトです。
android/app/build.gradle で環境変数からビルド変数を定義する
次に AndroidManifest.xml から環境変数の値を呼び出せるように build.gradle に環境変数の値を読み込む設定をします。
android/app/build.gradle
ファイルを開いて、defaultConfig に manifestPlaceholders を追記します。
ここでは以下 3 つの環境変数設定方法のうち、前者 2 つは System.getenv
を使用して環境変数を取得します。
flutter run or build
コマンドに環境変数を渡すlaunchctl
コマンドで環境変数を設定する- Android ビルド時に
--dart-define
の環境変数を渡す
最後の Android ビルド時に --dart-define
の環境変数を渡す方法は先程設定した dartEnvironmentVariables
配列から環境変数を取得します。
manifestPlaceholders を利用して、環境変数から取得した値を任意のビルド変数で定義して AndroidManifest.xml から取得できるようにします。
ビルド変数名と環境変数名は同じになっていますが、異なっていても問題ないです。
manifestPlaceholders = [GOOGLE_API_KEY: System.getenv("GOOGLE_API_KEY")]
記述する箇所は以下です。
android {
compileSdkVersion 30
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.flutter_google_maps"
:
:
:
// 以下のケースでは System.getenv を使う
// - `flutter run or build` コマンドに環境変数を渡す
// - `launchctl` コマンドで環境変数を設定する
manifestPlaceholders = [GOOGLE_API_KEY: System.getenv("GOOGLE_API_KEY")] // added
// Android ビルド時に `--dart-define` の環境変数を渡すケースでは dartEnvironmentVariablesを利用する
manifestPlaceholders = [GOOGLE_API_KEY: dartEnvironmentVariables.GOOGLE_API_KEY] // added
}
buildTypes {
release {
:
:
:
}
}
}
AndroidManifest.xml でビルド変数を取得する
最後に AndroidManifest.xml に build.gradle に定義したビルド変数を取得します。
android/app/src/main/AndroidManifest.xml
を開いてビルド変数を使用する箇所で "${GOOGLE_API_KEY}"
を追記します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_google_maps">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:label="flutter_google_maps"
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GOOGLE_API_KEY}"/>
iOS のソースコードから環境変数の値を取得する
iOS のソースコードから環境変数の値を取得する方法です。
--dart-define
で設定した環境変数は Dart のソースコードでは使用できますが、そのままでは iOS の Swift で書かれたソースコードで利用できません。
なので iOS のビルド時に --dart-define
環境変数を Xcode に設定する必要があります。
iOS ビルド時に環境設定ファイルを出力する
まず Xcode を開きます。
プロジェクトのルートで以下コマンドを実行してください。
open ios/Runner.xcworkspace
今回 iOS ビルド時に環境設定ファイルを出力するスクリプトを実行します。
Xcode を開いたら Runner > Edit Scheme... をクリックします。
Build > Pre-actions をクリックします。
ウィンドウ左下の+ボタンをクリックしてプルダウンの中から New Run Script Action を選択します。
まず Provide build settings from のプルダウンから Runner を選択します。
次に以下のスクリプトを追記します。
function urldecode() {
: "${*//+/ }";
echo "${_//%/\\x}";
}
IFS=',' read -r -a define_items <<< "$DART_DEFINES"
for index in "${!define_items[@]}"
do
define_items[$index]=$(urldecode "${define_items[$index]}");
done
printf "%s\n" "${define_items[@]}" > ${SRCROOT}/Flutter/EnvironmentVariables.xcconfig
これは iOS の build 時に --dart-define
環境変数を取得し、環境設定ファイルである ios/Flutter/EnvironmentVariables.xcconfig
を自動生成するスクリプトです。
スクリプトを記述したらウィンドウを閉じて Android Studio からビルドしてみましょう。
Android Studio には以下の --dart-define
を定義しましたね。
--dart-define=GOOGLE_API_KEY=dummy_key
ios/Flutter
ディレクトリを Finder で開いてみると EnvironmentVariables.xcconfig
というファイルが生成されています。
中身を確認してみると以下環境変数が記述されています。
GOOGLE_API_KEY=dummy_key
flutter.inspector.structuredErrors=true
生成した環境設定ファイルを Xcode で利用できるようにする
ios/Flutter
ディレクトリにある Debug.xcconfig
を開きます。
以下 1 行を追記します。
#include "EnvironmentVariables.xcconfig"
また、同じく ios/Flutter
ディレクトリにある Release.xcconfig
を開いて同様 1 行追記します。
追記後のファイルは以下のようになります。
ios/Flutter/Debug.xcconfig
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "EnvironmentVariables.xcconfig"
ios/Flutter/Release.xcconfig
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include "EnvironmentVariables.xcconfig"
これで #include
で EnvironmentVariables.xcconfig を読み込んで Xcode から環境変数が利用できるようになりました。
.gitignore に EnvironmentVariables.xcconfig を追記する
ios/.gitignore
ファイルを開いて以下の1行を追記します。
Flutter/EnvironmentVariables.xcconfig
EnvironmentVariables.xcconfig を git に上げると人により環境変数が違う場合がある為チーム開発に支障がでます。
また、API Key など秘匿情報が含まれる可能性があるので ignore しておきます。
Info.plist に環境変数をビルド変数として定義する
まだ iOS のソースコードでは利用できません。
最後に Info.plist に先程、Debug.xcconfig
Release.xcconfig
で読み込んだ環境変数をビルド変数として定義する必要があります。
ios/Runner/Info.plist
を開いて Information Property List の +ボタンをクリックします。
入力項目が表示されますので、 Key に GOOGLE_API_KEY
Value に $(GOOGLE_API_KEY)
と入力します。
iOS のソースコードから環境変数の値を取得する
環境変数を取得するには Bundle.main.object
メソッドを利用します。
Bundle.main.object(forInfoDictionaryKey: "GOOGLE_API_KEY") as! String
例えば、iOS で GoogleMap を表示する時は API キーを ios/Runner/AppDelegate.swift
に設定します。
その場合は以下のように GMSServices.provideAPIKey
メソッドに取得したビルド変数を設定します。
import UIKit
import Flutter
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let googleApiKey = Bundle.main.object(forInfoDictionaryKey: "GOOGLE_API_KEY") as! String
GMSServices.provideAPIKey(googleApiKey)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
これで iOS でも --dart-define
環境変数が利用できました。
おわりに
ここで設定した環境変数は秘匿情報を隠蔽する目的の他、開発環境、ステージング環境、本番環境で環境を分けた場合の環境別に環境変数を利用したい時などにも利用します。
詳しくはこちらの記事を参照ください。
最後に今回環境変数を設定したサンプルは Github にありますので参照ください。