2017/10/08 追記:@motoshima1150さんよりご指摘いただいたplistの注意事項について追記しました。重要なご指摘ありがとうございます。
やりたいこと
- 外部サービスのAPIキーなどの設定値を
debug
とrelease
のschemeごとに分けたい - APIキーなどの設定値を使用するプロジェクトはパブリックなリポジトリなので、APIキーは公開されないようにしたい
この2点を満たせるように設定していきたいと思います。
まずはそれぞれの方法を個別に見ていき、最終的にそれらの方法を組み合わせる形で説明していきます。
APIキーをパブリックリポジトリに公開させない
APIキーなどの設定値をプロジェクトから切り離す方法として、ググるとcocoapods-keys
の利用がよく見つかりました。
早速試してみましたが、この方法ではなぜかうまくいかず、、
とりあえずパブリックリポジトリに設定値が公開されなければOKなので、結局のところ以下の手順で実装しました。
-
Keys.plist
を作成し、そこにプロジェクトから切り離す設定値を書く -
Keys.plist
をgit管理下から外す - プロジェクト内では
Keys.plist
から値を取得する
1. Keys.plist
を作成し、そこにプロジェクトから切り離す設定値を書く
下記例のようにKeys.plist
を作成後、任意の文字列でKeyを設定し(myAPIKey
の部分)、Valueにはプロジェクトで使用する設定値を記入します。(myapikeymyapikeymyapikey
の部分)
2.Keys.plist
をgit管理下から外す
.gitignore
ファイルにKeys.plist
を追記しましょう。
Keys.plist
これを忘れると結局公開されます。
3.プロジェクト内ではKeys.plist
から値を取得する
最後に、1で設定した設定値をプロジェクトから取得して使用する方法について説明します。
まず、簡単にKeys.plist
から値を取得するためにKeyManager.swift
というstructを定義しておきます。
import Foundation
struct KeyManager {
private let keyFilePath = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist")
func getKeys() -> NSDictionary? {
guard let keyFilePath = keyFilePath else {
return nil
}
return NSDictionary(contentsOfFile: keyFilePath)
}
func getValue(key: String) -> AnyObject? {
guard let keys = getKeys() else {
return nil
}
return keys[key]
}
}
これで、プロジェクト内では以下のように使用できます。
if let myAPIKey = KeyManager().getValue("myAPIKey") as? String { // "myAPIKey"は自分で設定したKeys.plistのキー
print(myAPIKey)
}
現状、1人で一台のMacで開発をしているので、このやり方で特に不自由ないですが、他の人と作業をするような場合はgit管理下にないKeys.plist
を別の形で共有しないといけないという手間は出てきます。
※2017/10/08追記
こちらのコメントにてご指摘いただいたように、plistはリソースなので公開後のアプリから中身を見ることができてしまいます。
なので、基本的には外部から見られてはいけない内容を直接書いてしまうことは避けたほうが良いです。
こちらも併せてご参考ください。
- https://qiita.com/mafmoff/items/8635d5b0c82b6af60b29
- https://qiita.com/Saayaman/items/c50000e6103358c8c4d6#comment-d518cbe570693cab32bb
外部サービスのAPIキーをdebug
とrelease
のschemeごとに分けたい
次に、外部サービスのAPIキーのような設定値をschemeごとに分ける方法を見ていきます。
私の場合は、Flurryのアプリケーション登録をrelease
とdebug
で分けたかったので、それぞれのAPIキーを取得し、schemeによってAPIキーを使い分けられるようにしています。
こちらは下記のクラスメソッドさんの記事を参考にさせていただき、User-Defined
を使用した方法で実現しました。
[iOS] User-Definedを利用しDebugとReleaseで定義を分ける
やり方は参考記事そのままなので、割愛します。
ちなみに、Objective-Cのマクロ?のような書き方も検討したのですが、個人的にあまりコードがきれいに見えないのでこちらにしました。
前述の2点を組み合わせる
最後に、前述のKeys.plist
を使った方法とUser-Defined
を使った方法を組み合わせて、表題の要望を実現させます。
概要としては、以下のような流れになります。
【設定方法】
-
Keys.plist
にdebug
時に使用する設定値とrelease
時に使用する設定値を定義
-
User-Defined
のdebug
,release
それぞれにKeys.plist
で取得する**Key
の文字列**を定義(設定値のAPIキーなどではなく、plistのKey) -
Info.plist
にUser-Defined
の値を取得するためのKey-Valueを定義
【使用方法】
-
Info.plist
からUser-Defined
の値を取得 -
User-Defined
で取得した値をKeys.plist
のKey
として、設定値を取得
それでは、それぞれについて見ていきます。
設定方法
1. Keys.plist
にdebug
時に使用する設定値とrelease
時に使用する設定値を定義
下記画像では、例として
-
release
scheme で使用するMyAPIKeyRelease
-
debug
scheme で使用するMyAPIKeyDebug
の2つのKeyとそれに対応するValueを定義しました。
また、前述の通り、このKeys.plist
はgit管理下から外しておきます。
2. User-Defined
のdebug
,release
それぞれにKeys.plist
で取得するKeyの文字列を定義
User-Defined
に任意の名前(例ではMyAPIKey
)で定義を加えます。
そして、Debug
とRelease
のそれぞれのvalueに、前工程でKeys.plist
に設定したKeyを定義します。
3. Info.plist
にUser-Defined
の値を取得するためのKey-Valueを定義
最後に、Info.plist
に任意のKey(下の例ではMyAPIKey
)をつけ、
そのValueとして$(User-Definedでつけた名前)
(例では$(MyAPIKey)
)を定義します。
ここまでで、設定は完了です。
使用方法
使用方法はまずコードで示してみます。
記事前半で作成したKeyManager
のstructを使用しています。
if let keyString = NSBundle.mainBundle().objectForInfoDictionaryKey("MyAPIKey") as? String, myAPIKey = KeyManager().getValue(keyString) as? String {
print(myAPIKey)
// release scheme の時は myapikeyreleasemyapikeyrelease
// debug scheme の時は myapikeydebugmyapikeydebug
}
細かく見てみると、
let keyString = NSBundle.mainBundle().objectForInfoDictionaryKey("MyAPIKey") as? String
の部分でInfo.plist
からschemeに応じたUser-Defined
のMyAPIKey
の値が取得されます。
仮にschemeがdebug
だとしたら、この時点でMyAPIKeyDebug
という文字列が取得できます。
次に、
myAPIKey = KeyManager().getValue(keyString) as? String
の部分で、先ほど取得した値(schemeがdebug
の場合、keyString
にMyAPIKeyDebug
が代入されている)をKeyとしてKeys.plist
から目的のAPIキーを取得しています。
以上の方法で、表題の目的は解決できました。
最後に
正直なところ、少し回りくどいのでこれがベストなのかは分かりません、、
また、「Info.plist
に書いた定義はKeys.plist
に書く方がいいかなぁ」とか、「KeyManagerのgetValue、staticでいいかも」とか、
いろいろと思うところはありますが、大筋はこんな感じでやりたいことが実現できたかと思います。
もしいいやり方をご存知の方いらっしゃいましたらご教授ください