Delphi で API キーを分離する

これは [Delphi Advent Calendar](http://qiita.com/advent-calendar/2016/delphi) の補欠記事です。

#API キー

近年のアプリは他のサービスと連携することが当たり前です。
アプリからあるサービス（API）を利用したいとなったら、そのサービスから「キー」（以下 API キー）を提供して貰うことが多いと思います。
  
**このキーはアプリや個人と結びつくもののため公開してはなりません。**

個人で開発しているだけなら良いのですが、ライブラリとして公開したり、サンプルとして公開する場合、[API キーを悪用されてしまうかもしれません](http://qiita.com/mochizukikotaro/items/a0e98ff0063a77e7b694)。

#API キーの分離
ということで、API キーは分離すべきなのですが、どうやって分離しようかと考えると、例えば

```pascal:KeyConsts.pas
unit KeyConsts;
interface
const
  API_KEY = 'AE58E0CC-9653-4363-B622-34544D0DE914'
implementation
end.
```

というユニットを作って、このユニット KeyConsts.pas を .gitignore に追加してアップを防ぐとかです。

まあ、これでも全然良いのですが…

個人的には $I を使っています。

#$I コンパイラ指令

[$I とは $INCLUDEコンパイラ指令のことです](http://docwiki.embarcadero.com/RADStudio/Berlin/ja/%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AB%E3%83%BC%E3%83%89_%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%EF%BC%88Delphi%EF%BC%89)。

\$I コンパイラ指令で指定されたファイルは、$I コンパイラ指令の直後に挿入されます。
これを使うとキーをキーだけで分離できます。

```pascal:KeyConsts.pas
unit KeyConsts;
interface
const
  API_KEY = {$I FooApi.Key};
implementation
end.
```

```text:FooApi.Key
'AE58E0CC-9653-4363-B622-34544D0DE914'
```

こんな風に分離して FooApi.Key を .gitignore に追加します。

もしくは、サンプルとして下記の様なダミーの FooApi.Key を入れておいても良いかもしれません。

```text:FooApi.Key
'YOUR_API_KEY'
```

この場合、もしも FooApi.Key の中身が変更されていない時にエラーで停止できます。


```pascal:KeyConsts.pas
unit KeyConsts;
interface
const
  API_KEY = {$I FooApi.Key};

{$IF API_KEY  = 'YOUR_API_KEY'}
  {$MESSAGE FATAL 'Foo API のキーを FooApi.Key に記述してください'}
{$ENDIF}

implementation
end.
```

ただ、これをやるとうっかり本当のキーが書かれた FooApi.Key をアップしてしまうかもしれない諸刃の剣です。
なので、アップしないのが一番でしょう（FooApi.Key が存在しなければコンパイルエラーになるので同じ事です）。

#AndroidManifest.xml
そんな感じで $I を使えば API キーを分離できますが、AndroidManifest.xml に記述するタイプのキーとかは中身を置換するのが良いと思います。

Delphi のビルドシステムは [MSBuild](http://docwiki.embarcadero.com/RADStudio/Berlin/ja/MSBuild_%E3%81%AE%E6%A6%82%E8%A6%81) なので[「ビルド前イベント」](http://docwiki.embarcadero.com/RADStudio/Berlin/ja/%E3%83%93%E3%83%AB%E3%83%89_%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)でバッチファイルや exe を実行できるので、次のようなコンソールアプリを作って 

```pascal:ReplaceAPIKey.dpr
program ReplaceAPIKey;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Classes, System.SysUtils, System.IOUtils;

const
  MANIFEST = 'AndroidManifest.template.xml';
  ORG_MANIFEST = '_' + MANIFEST;

  API_KEY = 'FooApi.Key'; // 読み込みたい API Key ファイル
  TARGET = '%API_KEY%';   // API Key を書き込む Place Holder

var
  APIKey: String;
begin
  if not TFile.Exists(ORG_MANIFEST) then
    TFile.Copy(MANIFEST, ORG_MANIFEST);

  TFile.Copy(ORG_MANIFEST, MANIFEST, True);

  with TStringList.Create do
    try
      LoadFromFile(API_KEY);
      APIKey := Text.Trim;

      LoadFromFile(MANIFEST, TEncoding.UTF8);
      Text := Text.Replace(Target, APIKEy);
      SaveToFile(MANIFEST, TEncoding.UTF8);
    finally
      DisposeOf;
    end;
end.
```

ビルド前イベントに追加します。

![qiita2.png](https://qiita-image-store.s3.amazonaws.com/0/12977/a7f323a1-973d-dee3-54be-89ff50f7f6d6.png)

もちろん、AndrodManifest.template.xml の方も置き換えるのを忘れないでください。

```xml:例
<meta-data android:name="foo.api.key" android:value="%API_KEY%" />
```

こんな風にしておくと、ビルド前に自動的に置き換わります。


**ただし**、注意したいのは [Google Map の API キーだけは特別扱いで IDE から設定できます](http://docwiki.embarcadero.com/RADStudio/Seattle/ja/Google_%E3%83%9E%E3%83%83%E3%83%97%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86_Android_%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E6%A7%8B%E6%88%90%E3%81%99%E3%82%8B)。
なので、IDE から設定したキーが記述されている .dproj をアップするとキーも漏洩してしまいます。
これについては悩ましい所ですが、上の仕組みを使って回避するのが良いでしょう。

#まとめ
あとは、適当に上手いことやってください！（酷い）