Xcode
iOS
Swift

iOSアプリでも環境変数が使いたい!

More than 1 year has passed since last update.

はじめに

GitHubにPublicなリポジトリでAPIキーとかを隠蔽したまま公開したい!っていうことありませんか。
.env ファイルとか作ってそれぞれの環境に合わせてビルドできるようになりたいーっていうやつ

ついでに Info.plist にAPIキー書くやつたまにあるけどそれもやりたいっていう

環境

  • Xcode9.1(8.3.3でも確認済み)
  • macOS 10.12.6
  • swift 4.0.2

ソースコード編

手順

  1. Xcodeプロジェクトの作成(省略)
  2. .env および .env.sample の作成

スクリーンショット 2017-11-16 10.45.59.png

.env.sample
TWITTER_CONSUMER_KEY=''
TWITTER_CONSUMER_SECRET=''

こんな感じに作って、どちらとも <PROJECT_NAME>.projectpbxproj が読める状態にする。
3. .gitignore 設定

$ echo "/path/to/.env" >> /path/to/.gitignore

この時点で git add .env などしてなかったらGit管理下になっていないのでいいかと思いますが、Git管理下になっていた場合、以下

$ git rm --cached /path/to/.env

スクリーンショット 2017-11-16 10.52.01.png

git clone とかした時点で上の画像みたいに File NotFound 状態になったらOK
4. 環境変数読み込み

今回は swift4.0.2 で確認してるのでそれぞれの環境に合わせて実装等お願いします。

AppDelegate.swiftfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { }

AppDelegate.swift
・・・
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        guard let path = Bundle.main.path(forResource: ".env", ofType: nil) else {
            fatalError("Not found: '/path/to/.env'.\nPlease create .env file reference from .env.sample")
        }
        let url = URL(fileURLWithPath: path)
        do {
            let data = try Data(contentsOf: url)
            let str = String(data: data, encoding: .utf8) ?? "Empty File"
            let clean = str.replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "'", with: "")
            let envVars = clean.components(separatedBy:"\n")
            for envVar in envVars {
                let keyVal = envVar.components(separatedBy:"=")
                if keyVal.count == 2 {
                    setenv(keyVal[0], keyVal[1], 1)
                }
            }
        } catch {
            fatalError(error.localizedDescription)
        }
        ・・・
    }
・・・

な感じに書く。 .env がなかったら fatalError が発生する。
メソッドぐちゃぐちゃになるので自分は別のメソッド作りました。

さっきの続きに。
今回は TwitterKit のAPIキーをサンプルに

AppDelegate.swift
・・・
        let env = ProcessInfo.processInfo.environment
        Twitter.sharedInstance().start(withConsumerKey: env["TWITTER_CONSUMER_KEY"]!, consumerSecret: env["TWITTER_CONSUMER_SECRET"]!)

.env の左辺をキーとして取得できるようになってたら完了!

Info.plist

手順

ソースコード編4 の手順までは同じ

  1. Run Script を作成。

スクリーンショット_2017-11-16_11_04_29.png

みなさんに説明するまでもなく...
この Run ScriptCopy Bundle Resources (○ items) よりも上に配置するようにしないと上手く動かないです
2. PlistBuddy を使うスクリプトの記述

PlistBuddy の説明はパスで、調べれば Build Number をなんとかーっていうのがたくさん出てくると思います!

スクリーンショット 2017-11-16 11.07.37.png

また、 TwitterKit の例ですが、URLSchemeを事前に用意しておいてそこを twitterkit-<API_KEY> に置き換えるといったような処理

RunScript
plistBuddy="/usr/libexec/PlistBuddy"
infoPlistFileSource="${SRCROOT}/${INFOPLIST_FILE}"
infoPlistFileDestination="${TEMP_DIR}/Preprocessed-Info.plist"

. /path/to/.env

$plistBuddy -c "Set :CFBundleURLTypes:0:CFBundleURLSchemes:0 twitterkit-${TWITTER_CONSUMER_KEY}" "${infoPlistFileDestination}"

Info.plist の特定のキーへのPATHは普通に辞書と配列でアクセスできるので説明はなしで

試しにビルドして、 twitterkit-<API_KEY>:// のURLSchemeをSafari等でも UIApplication.shared.canOpenURL(URL) で行ければできてると思います。

最後に

ArchiveしてExportした .ipa ファイルを unzip したら Info.plist とかは見られるのであくまでも外部リモートに公開してるときだけ隠蔽できるという感じでした。

Info_plist.png

今回は .env ファイル内の値を環境変数として読み込むだけでしたが、普通に母艦の環境変数も使えると思うので参考になればと思います。