ほとんどのアプリは以下の方法で分けていると思います。
一つは、User-Definedを使う方法。
一つは、プリプロセッサマクロを使う方法。
これらよりも、自分がベストだと思う方法があるよ。という記事です。
MavenのprofileやGradleのAndroid Pluginのように環境別のファイルを用意して、ビルドにどっちを使うかを判断させる方法です。
やり方
まずは、Environment-Info-develop.plist
、 Environment-Info-production.plist
のように環境ごとのファイルを作成します。
中身は以下のように環境ごとの違いを記述します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>apiUrl</key>
<string>https://develop.hoge.jp</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>apiUrl</key>
<string>https://hoge.jp</string>
</dict>
</plist>
次に、Build Phasesで、Run Scriptを作成します。
内容は、以下です。つまり、選択したBuild Configurationに対応するファイルをEnvironment-Info.plist
としてアプリにバンドルさせています。
BUILD_PRODUCT="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app"
PROJECT_PATH="${PROJECT_DIR}/${PROJECT_NAME}"
INFO_BUILD_PATH="${BUILD_PRODUCT}/Environment-Info.plist"
rm -rf "${INFO_BUILD_PATH}"
if [ "${CONFIGURATION}" = "Debug" ]; then
cp "${PROJECT_PATH}/Configuration/Environment-Info-develop.plist" "${INFO_BUILD_PATH}"
echo "Development Environment-Info copied into ${BUILD_PRODUCT}"
else
cp "${PROJECT_PATH}/Configuration/Environment-Info-production.plist" "${INFO_BUILD_PATH}"
echo "Production Environment-Info copied into ${BUILD_PRODUCT}"
fi
最後にこの内容を扱うクラスを作成して利用するだけです。
取得できなかった場合は単純なバグなので、すぐに落として気づけるように、Forced unwrappingさせます。
public class EnvironmentHolder {
static let sharedInstance = EnvironmentHolder()
let apiUrl: String
private init() {
let bundle = Bundle.main
let environmentPath = bundle.path(forResource: "Environment-Info", ofType: "plist")!
let environmentDict = NSDictionary(contentsOfFile: environmentPath)!
self.apiUrl = environmentDict.object(forKey: "apiUrl") as! String
}
}
User-Definedとの比較
User-Definedは、Target > Build Settings で「+」を押下して 「add User-Defined Setting」 でConfigurationごとに設定していく方法です。
そして、Info.plistに書いて
<key>apiUrl</key>
<string>${apiUrl}</string>
コードで以下のように呼び出します。
let apiUrl = Bundle.main.object(forInfoDictionaryKey: "apiUrl")
あまり、良くないところは、User-Definedが、*.xcodeproj/project.pbxproj
に保存されることです。
開発中は、Debugの場合だけ書いておいて、後でコピペして本番用のを用意するということができません。
一方、最初に提案した方法は、ただのplistなので、簡単にコピペができますし、diffに気づきやすいです。
プリプロセッサマクロとの比較
Objective-cなら Preprocessor Macrosに、例えば、DEBUG=1
と書き、
Swiftなら Other Swift Flags に -D DEBUG
と書き、
コードで以下のように呼び出します。
Objective-Cの場合
#ifdef DEBUG
// ...
#elif RELEASE
// ...
#endif
Swiftの場合
#if DEBUG
// ...
#elseif RELEASE
// ...
#endif
あまり良くないところは、本質的ではない処理がコード上に現れることです。値が少なければ良いですが、多くなると可読性が損なわれ、バグに気付きにくくなります。
この方法では、EnvironmentHolder.swiftのようなクラスを用意して、一つのクラスだけに閉じる戦略がいいでしょう。
いずれにしろ、最初に提案した方法は余計な分岐がないことがメリットになります。