LoginSignup
16
16

More than 5 years have passed since last update.

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

Posted at

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

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

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


まずはプロジェクト作成

xcode.png

xcode1.png

xcode2.png

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

xcode6.png

新規ファイル作成

xcode3.png

xcode4.png

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

xcode5.png

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

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

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

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

$(PROJECT_DIR)/scripts/current_domain.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

xcode8.png

xcode9.png

xcode10.png

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

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

こうなる!!

xcode12.png

ブランチを変更してみる

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

再度ビルドしてみる

これが

xcode12.png

こうなる!!!

xcode13.png

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

まとめ

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

READMEなど何も書いてないですがサンプルを ここ にアップしておきましたので手軽に試してみたい方はこちらからCloneしてビルドして頂ければと思います。

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

16
16
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16