⚙️

Xcode の Run Script Phase で .env を元に Swift コードを自動生成する

2022/01/31に公開

動機

サーバサイドの開発ではリポジトリにコミットしたくないような環境独自の値は .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