本番・ステージング・開発などの環境設定を美しく切り替える方法
Swiftの環境変数を設定するベストプラクティス
はじめに
ソフトウェア開発において、環境ごとに動作を変えることはつきものです。
モバイルアプリ開発においても、通信を用いた外部連携があれば、接続先の変更が伴います。
その際に、開発環境、テスト環境、ステージング環境、本番環境など、様々な環境に、接続先URLを変更するなどの必要が出てきます。そして、開発者はなるべくその変更を意識することなくビルドしてプロダクトを生成したいものです。
多くの人が、この環境毎の設定を自動的に切り替えるためのよい方法を考え、様々な対策案を出しています。
以下の記事もObjective-C時代の方法を踏襲したひとつの案です。
しかしながら、Swiftに相応しいベストプラクティスが何なのかというのは定まっていません。
よくある対策
例えばよくあるのが、Configurationsに環境名を追加する方法です。
開発環境と本番環境だけでよければ、
もともとあるDebugを開発環境、Releaseを本番環境とする人もいます。
更にステージング環境などが必要であれば、Stagingなどと追加し、以下のように環境変数用のファイル内で切り替えられるようにします。
enum API {
#if DEBUG
static let baseUrl = "https://naturalmindo.com/develop"
#elseif STAGING
static let baseUrl = "https://naturalmindo.com/staging"
#elseif RELEASE
static let baseUrl = "https://naturalmindo.com/production"
#endif
このifdefの分岐が気持ち悪いとして、更に様々な方法が考えられています。
- User-Definedやplistファイルを利用した方法
https://dev.classmethod.jp/articles/ios-user-defined/
- 環境ごとの値をYAMLファイルに外出しした独自のツールを使用した例
https://speakerdeck.com/417_72ki/management-of-environment-variables-with-yamls-ver-dot-2
しかしながら、もうひとつ気持ち悪さが残ります。
ConfigurationsのDebugとReleaseの並列にStagingなどの環境があることです。
環境とビルドモード(Debug、Release)は別の考え方です。
Androidの例ですと、
buildTypesとproductFlavorsでその考えは分かれています。
buildTypesでrelease/debugの設定を行い、productFlavorsでdevelop、productionなどの環境ごとの設定を行います。
この設定値を元に、アプリ実行時には、Build Variantsの選択ができるようになっています。
上の例ではそれぞれの組み合わせで、4種類のBuild Variantsができることが分かります。
- developDebug
- developRelease
- productionDebug
- productionRelease
このように百歩譲って、iOSのConfigurationsでも、それぞれの組み合わせごとに用意するのであればまだよい気がしますが、それでも設定が個別ででき過ぎてしまい管理しずらくなってしまいます。
やはり、環境はTARGETSで切り替えたいものです。
そもそもデバイスには環境毎にインストールして別アプリとして管理したいはずなので、TARGETSの切り分けは必須で、いかにそれだけで済ますかというところです。
結局のところこれでいい
外部ツールを活用したり、独自にツールを作ったりしている例は素晴らしく、応用すれば今回のことも解決できそうな気がしています。
差分管理などメンテナンス性など目線によってメリット・デメリットはあります。
しかしながら身の回りの小規模開発に目を向けた場合には、ほとんど手を施すことなく簡単に変えられる方法があればそれで良いと思っています。
そこでターゲット別に簡単に切り替えられるものに着目したときに、ターゲットで参照するファイルが違うことや、コンパイル対象が違うということに目を向けます。そこでプロパティリスト(plist)が思いつきます。
そういえば、Firebase連携をした際に使用する設定ファイルも「GoogleService-Info.plist」とplistですね。
ただし、元々あるInfo.plistに手を加えることは相応しくありません。
既にターゲットに紐付いてあり、以下コードだけでkey-valueの値が取り出せることは魅力ですが、管理上分けたいところです。
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName")
オリジナルのplistファイルを作成して管理したほうがよさそうです。
しかし、それでも少し欠点があり、plistから取り出すためのコードが、設定値を取得するたびに呼ぶには冗長のため、クラス化するなど検討事項が残ります。
let path = Bundle.main.path(forResource: "env", ofType: "plist")
let plist = NSDictionary(contentsOfFile: path!)
let feedUrl = plist!["FEED_URL"]
ということで、もうswiftファイルでよいのではとなりました。
同一ファイル名、同一構成のswiftファイルをターゲットごとに用意して、「Target Membership」で対象のターゲットだけリソースに含めるようにするということです。
同一ファイル名が複数あること、チェックが間違って付いたり外れたりした場合に気づきにくいなど、管理面での課題はありますが、設定項目があまり複雑でない小規模開発の場合には、これでも良いように思います。
さいごに
swiftファイルの一番シンプルな方法で、productionとdevelopで取得する記事一覧のfeedを変えて試すことができました。
きちんとしたベストプラクティスを見逃している可能性もなくはないですが、とにかくシンプルに解決させたいところです。