iOSアプリ開発でGitのブランチ毎に設定を変更してみる

弊社の開発環境ではサーバーサイドも人が増え、プロジェクトも複数走る事が多いので、プロジェクト毎に開発サーバーが必要になっています。
まずはアプリからサーバーが選択出来れば良いだろうとアプリを立ち上げた状態でiPhoneをシェイクすれば、ドメインを選択する為の画面が立ち上がってドメインが選択出来る機能を用意していました。
ただ、ドメインの変更を忘れてしまう事が頻発し、サーバー側で大きな変更が入るとアプリが落ちたりする事が頻発してしまいました。。

今回この問題を解決する為に1つ案を思いついたので実装してみました。

もうすぐ `iOS 10` 、 `Swift 3` がリリース予定ですが、環境は `Xcode 7.3.1` 、 `Swift 2.2` です。

---

## まずはプロジェクト作成

![xcode.png](https://qiita-image-store.s3.amazonaws.com/0/2846/87060572-0a7d-48f5-6180-6ef6103403ae.png)

![xcode1.png](https://qiita-image-store.s3.amazonaws.com/0/2846/2d4c1a4b-2d8b-c3df-7d55-ede5110191db.png)

![xcode2.png](https://qiita-image-store.s3.amazonaws.com/0/2846/4cc10974-a849-7c0d-d5ee-60cdd423199f.png)

※Gitのブランチをベースにするので必ずこのチェックは入れて下さい

![xcode6.png](https://qiita-image-store.s3.amazonaws.com/0/2846/e949f3a6-d3e8-6b31-f630-0c632ca72d0a.png)

## 新規ファイル作成

![xcode3.png](https://qiita-image-store.s3.amazonaws.com/0/2846/a35fe9c1-0044-97b8-4a83-ba7a2e307105.png)

![xcode4.png](https://qiita-image-store.s3.amazonaws.com/0/2846/c9465eaa-c089-aae7-8a15-d1185464123f.png)

※今回は **Config.swift** という名前で作成します

![xcode5.png](https://qiita-image-store.s3.amazonaws.com/0/2846/655953b8-7bc4-9b25-7df8-ccfcc49d061d.png)

## 設定のベースとなるJSONファイルを作成

※Xcodeのプロジェクトに入れるファイルではないのでXcodeの操作から離れて下さい

`$(PROJECT_DIR)/scripts/API.json` というファイルを作成

```json
{
    "master": "http://fromkk.me",
    "develop": "http://www.google.com/",
    "feature/branch/.*": "http://yahoo.co.jp"
}
```

`$(PROJECT_DIR)/scripts/current_domain.swift` というファイルを作成

```swift
#!/usr/bin/swift

import Foundation

/// 正規表現のマッチ機能をStringに拡張
extension String {
    private func match(value: String?) -> Bool {
        guard let value: String = value else {
            return false
        }

        do {
            let regexp: NSRegularExpression = try NSRegularExpression(pattern: self, options: [.CaseInsensitive])
            return 0 < regexp.numberOfMatchesInString(value, options: [], range: NSRange(location: 0, length: value.characters.count))
        } catch {
            return false
        }
    }
}

struct System {
    /// このファイルのフルパス
    lazy var fullPath: String? = {
        guard let args: [String] = NSProcessInfo.processInfo().arguments else {
            return nil
        }

        guard let index: Int = args.indexOf("-interpret") else {
            return nil
        }

        return args[index + 1]
    }()

    /// このファイルのディレクトリ
    lazy var currentDir: String? = {
        guard let fullPath: String = self.fullPath else {
            return nil
        }
        var paths: [String] = fullPath.componentsSeparatedByString("/")
        paths.removeLast()
        return paths.joinWithSeparator("/")
    }()

    /// このファイルのファイル名
    lazy var fileName: String? = {
        guard let fullPath: String = self.fullPath else {
            return nil
        }
        var paths: [String] = fullPath.componentsSeparatedByString("/")
        return paths.popLast()
    }()

    /// 現在のGitのブランチ名
    lazy var branch: String? = {
        guard let args: [String] = NSProcessInfo.processInfo().arguments else {
            return nil
        }

        guard let index: Int = args.indexOf("-branch") else {
            return nil
        }

        return args[index + 1]
    }()

    /// JSONのファイル名
    let jsonFileName: String = "API.json"
    /// API.jsonの内容をパース
    lazy var api: [String: String]? = {
        guard let currentDir: String = self.currentDir else {
            return nil
        }

        let path: String = "\(currentDir)/\(self.jsonFileName)"
        let fileManager: NSFileManager = NSFileManager()
        if !fileManager.fileExistsAtPath(path) {
            return nil
        }

        guard let data: NSData = fileManager.contentsAtPath(path) else {
            return nil
        }
        do {
            return try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as? [String: String]
        } catch {
            return nil
        }
    }()
    /// 初期ドメイン（masterだけは絶対存在する前提）
    lazy var defaultDomain: String = {
        guard let json: [String: String] = self.api else {
            return ""
        }

        return json["master"] ?? ""
    }()
    /// ブランチ名に合ったドメイン
    lazy var currentDomain: String? = {
        guard let json: [String: String] = self.api, let currentBranch: String = self.branch else {
            return nil
        }

        guard let key: String = json.keys.filter({ (key: String) -> Bool in
            return ("^"+key+"$").match(currentBranch)
        }).first else {
            return nil
        }

        return json[key]
    }()
}

/// 実際に実行される処理
/// ブランチ名に合ったドメイン名を出力しているだけ
var system: System = System()
print(system.currentDomain ?? system.defaultDomain)

```

## XcodeのSchemeを編集

![xcode7.png](https://qiita-image-store.s3.amazonaws.com/0/2846/23af3d18-9bca-8034-45fb-fd352d3b6c24.png)

![xcode8.png](https://qiita-image-store.s3.amazonaws.com/0/2846/7c5f6b7c-93d4-df50-726f-8c89986ecbd4.png)

![xcode9.png](https://qiita-image-store.s3.amazonaws.com/0/2846/0265b554-a84e-da71-e460-1297869bdbb0.png)

![xcode10.png](https://qiita-image-store.s3.amazonaws.com/0/2846/36dc8692-4bb4-7cae-2775-d9c57f75f8ca.png)

**Provide build settings from** を現在のSchemeを選択して **Type a script or drag a script file from your workspace to insert its path** に下記のコードをコピペ

```bash
branch=$(/usr/bin/git --git-dir=$PROJECT_DIR/.git symbolic-ref --short HEAD)
output=$(swift $PROJECT_DIR/scripts/current_domain.swift -branch $branch)
sw=$PROJECT_DIR/Config.swift
if [ ! -e $sw ]; then
touch $sw
fi

: > $sw
echo "import Foundation\n\nlet _gitBranch: String? = \"$branch\"\nlet _apiCurrentDomain: String? = \"$output\"" > $sw
```

※Gitでこの設定を共有するには **Shared** にチェックを入れておく必要があるかもです

## ビルドしてみる

必要な設定は済みましたのでビルドしてみましょう。
**Config.swift** を選択した状態でビルドすると変化が分かりやすいと思います。

これが

![xcode11.png](https://qiita-image-store.s3.amazonaws.com/0/2846/4e06f3ac-e51d-64dc-e712-4800e0d48050.png)

# こうなる！！

![xcode12.png](https://qiita-image-store.s3.amazonaws.com/0/2846/cb3d5ca0-33dc-fb7f-a114-e71448da34c3.png)

## ブランチを変更してみる

```bash
# Terminalで下記のコマンドを実行
git checkout -b develop
```

## 再度ビルドしてみる

これが

![xcode12.png](https://qiita-image-store.s3.amazonaws.com/0/2846/cb3d5ca0-33dc-fb7f-a114-e71448da34c3.png)

# こうなる！！！

![xcode13.png](https://qiita-image-store.s3.amazonaws.com/0/2846/01a865c8-24f1-582b-fddf-8a5912924d44.png)

ここまでくれば後は **Config.swift** に記述された定数を利用してアプリを作成していけばGitのブランチ毎に設定を変更しながら開発を進めていくことが出来ると思います。

# まとめ

やっている事はSchemeのビルド時のPre-actionsで **current_domain.swift** を実行し、 **Config.swift** ファイルを書き換えているだけです。
この仕組みを応用すればブランチ毎に **.plist** ファイルを書き換える等色々な事が出来そうです。

READMEなど何も書いてないですがサンプルを [ここ](https://github.com/fromkk/PreActionSample) にアップしておきましたので手軽に試してみたい方はこちらからCloneしてビルドして頂ければと思います。

更に良い案があればコメントやPRを頂ければと思います。