⚙️
Xcode の Run Script Phase で .env を元に Swift コードを自動生成する
動機
サーバサイドの開発ではリポジトリにコミットしたくないような環境独自の値は .env ファイルなどを使って環境変数として定義する方法がよく使われます。
API_BASE_URL=http://localhost:3000
クライアントサイドの開発の際にも同様に各環境の環境変数を .env で定義し、ビルド時にその中身に応じて swift ファイルが生成できればいいなと思ってやってみました。
方法
事前に生成される swift コードのテンプレートを作成しておき、Run Script Phase でそのテンプレート内に .env に定義した環境変数を埋め込む方法で実現します。
環境変数の埋め込みには envsubst を使用します。
envsubst はもともと gettext 付属のシンプルなテンプレートエンジンなのですが、今回は CI でも手軽に使えるよう go 移植版の a8m/envsubst を使用しています。
テンプレート
シンプルにこんな感じです。
templates/Env.swift
import Foundation
enum Env {
static let ApiBaseUrl: String = "${API_BASE_URL}"
}
Run Script Phase の定義
スクリプト本体
if [ ! -e $SRCROOT/vendor/envsubst ]; then
curl -L https://github.com/a8m/envsubst/releases/download/v1.2.0/envsubst-`uname -s`-`uname -m` -o envsubst
chmod +x envsubst
mkdir -p $SRCROOT/vendor/
mv envsubst $SRCROOT/vendor/
fi
# .env の中身を読み込む
set -a; source $SCRIPT_INPUT_FILE_0; set +a;
$SRCROOT/vendor/envsubst < $SRCROOT/templates/ENV.swift > $SCRIPT_OUTPUT_FILE_0
if [ -e $SCRIPT_OUTPUT_FILE_1 ]; then
# ファイル内容が変更になった場合のみ置き換える
cmp $SCRIPT_OUTPUT_FILE_0 $SCRIPT_OUTPUT_FILE_1
if [ $? -eq 1 ]; then
cp $SCRIPT_OUTPUT_FILE_0 $SCRIPT_OUTPUT_FILE_1
fi
else
cp $SCRIPT_OUTPUT_FILE_0 $SCRIPT_OUTPUT_FILE_1
fi
Input Files
$(SRCROOT)/.env
$(SRCROOT)/templates/Env.swift
Output Files
$(DERIVED_FILE_DIR)/Env.generated.swift
$(SRCROOT)/$(PROJECT)/Env.generated.swift
Discussion