【Swift】環境に応じてバックエンドのホストを切り替える

ソースコード：https://github.com/ShionKoga/configure-values-differently-per-environment

# やりたいこと

開発中は`http://localhost:8080`
TestFlight配信時は`https://stage.hoge.com`
AppStore配信時は`https://hoge.com`
のように、環境に応じて何かしら値を切り替えたい場面があるかと思います。
今回は、バックエンドのホストを例に説明していきます。

# プロジェクトを作成する
適当にiOSプロジェクトをテストなしで作成してください。

# Schemeを理解する
Xcodeがビルド時、アプリケーション実行時、テスト時などの設定値をひとまとめにしたものがSchemeです。
Xcode上部でSchemeを切り替えたり、編集する画面を開くことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/396d2d99-c21e-9594-b9aa-fd682381a0e3.png)

デフォルトで1つのスキームが作成されています。
「edit Scheme」をクリックすると、Schemeを編集することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/fd0db0b7-f6ca-c97b-b795-562ee311089e.png)

各アクションに対して「Build Configuration」の選択項目があり、それぞれ`Debug`もしくは`Release`が選択できます。
環境に応じてバックエンドの参照先のような値を変更するために、このBuild Configurationの値を使用します。


# Build Configurationで選択できる値を増やす
プロジェクトのInfoタブから設定することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/54564d34-dfd9-77db-dd23-2163111d64af.png)
Configurationsの下にある「＋」マークを押して、新しい`Staging`のConfigurationを作成しましょう。
「Duplicate "Debug" Configuration」と「Duplicate "Release" Configuration」のどちらを選択しても構いません。
下の画像の状態になっていればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/aecf28ef-56e0-5b35-ef1b-03aee58f507a.png)
先ほどのScheme編集画面で選択できるBuild Configurationの項目に`Staging`が増えていることが確認できると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/22dcc9c7-415f-3226-a074-ff4a19f1af33.png)

# 環境ごとのSchemeを用意する
開発PCのシュミレーターでの実行時、TestFlight配信時、AppStore配信時のSchemeをそれぞれ用意していきます。
すでにRunアクション（アプリケーション実行時）のBuild Configurationが`Debug`になっているSchemeは存在しているので、
`Staging`, `Release`の合計2つのSchemeを追加します。

「New Scheme」を選択し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/12aabf15-a8fb-aea2-77ef-5b7e767d27fe.png)

Scheme名を入力します。（分かればなんでもいい）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/e337aaca-217b-d4d9-7d9c-193962144d2b.png)

作成されたSchemeの編集画面を開き、RunアクションのBuild Configurationを`Staging`に変更します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/1ab0ed74-5ca5-4c25-cc4f-2b5380bd821c.png)

同様の手順でRunアクションのBuild Configurationが`Release`になっているSchemeも作成してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/20ad1f29-d9e2-13d5-c5c1-55d71f00657d.png)
実行時のSchemeが3つ選べる状態になっているはずです。

作ったこの3つのSchemeをTestFlight配信時、AppStore配信時などに応じて切り替えてビルドすることで、
環境に応じて実行時のBuild Configurationの値を変えることが可能になります。

| 環境 | Scheme | Build Configuration | 
| ---- | ---- | ---- |
| 開発PCのシュミレーターでの実行時 | configure-values-differently-per-environment | Debug |
| TestFlight配信時 | configure-values-differently-per-environment Staging | Stage |
| AppStore配信時 | configure-values-differently-per-environment Release | Release |

# Build Configurationに応じて値を切り替える
`Info.plist`に新たに値を追加します。
| Key | Type | Value | 
| ---- | ---- | ---- | 
| APIConfiguraiton | String | $(CONFIGURATION) |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/e2a47169-14f0-3946-2971-7dbc56358613.png)

`$()`の中に環境変数名を指定することで、環境変数をplistの値として指定することができます。
この値を、Swiftファイルから読み取ってみましょう。
`ContentView.swift`を以下のように編集します。
```swift
struct ContentView: View {
    var body: some View {
        VStack {
            Text(Bundle.main.object(forInfoDictionaryKey: "APIConfiguration") as! String)
        }
        .padding()
    }
}
```
`configure-values-differently-per-environment`選択時：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/c5ac34e1-ab07-1f07-7773-e5d3b8ed0bca.png)

`configure-values-differently-per-environment Staging`選択時：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/dff8840b-31d9-fc71-eb7e-cb9528cb5bac.png)

`configure-values-differently-per-environment Release`選択時：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/f3013f72-8a8b-8d26-79c3-4d1f667e3f89.png)

`Info.plist`の値を参照することができたので、この値を使ってバックエンドのホストを切り替えていきます。

`Configuration.swift`と`Environment.swift`をそれぞれ以下のように作成します。

```swift
import Foundation

struct Configuration {
    static var environment: Environment {
        get {
            let configuration = Bundle.main.object(forInfoDictionaryKey: "APIConfiguration") as! String
            return Environment(rawValue: configuration)!
        }
    }
}

```

```swift

enum Environment: String {
    case Debug
    case Staging
    case Release
    
    var baseUrl: String {
        switch self {
        case .Debug: return "http://localhost:8080"
        case .Staging: return "https://stage.yourapp.com"
        case .Release: return "https://yourapp.com"
        }
    }
}

```

struct`Configuration`のstatic var `environment`は、
`Info.plist`の`APIConfiguration`の値に応じて、enum `Environment`を初期化して返します。

enum `Environment`の `baseUrl` は自身のrowValueに応じて、適切なバックエンドのホストを返します。


これを試しにContentView で表示してみます。
```swift
struct ContentView: View {
    var body: some View {
        VStack {
            Text(Configuration.environment.baseUrl)
        }
        .padding()
    }
}
```
`configure-values-differently-per-environment`選択時：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/afa6d1b4-c931-8f38-cd23-0a712cee4ffd.png)

`configure-values-differently-per-environment Staging`選択時：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648522/ed0ee036-ebac-5d8b-13fc-4ecf238f2d7b.png)

このように`Configuration.environment.baseUrl`を参照することで、環境ごとに異なるバックエンドのホストを切り替えることが可能になります。