iOSでホーム画面に表示されるアプリ名はどこで決まるのか？変更するには？

iOSでホーム画面に表示されるアプリ名はどこで決まるのか？変更するにはどうすればよいか？を整理してまとめました。

## 確認環境
- Xcode 11.3.1
- iOS 13.3

## 結論
1. アプリ名は Info.plist ファイルの **CFBundleDisplayName** で決まる
2. アプリ名を変更するには、その **CFBundleDisplayName** の値を変更すればよい
3. アプリ名をローカライズするには、対象言語毎に **InfoPlist.strings** を用意する
4. Build Configuration に応じてアプリ名を切り替えるには、 **User-Defined Setting** を使うと良い
5. Build Configuration に応じてアプリ名を切り替えつつローカライズするには、ビルド時にがんばるしかなさそう 

## ホーム画面のアプリ名はどこで決まるのか？
新規プロジェクトを作成して確認していく。

プロジェクト作成時に **Product Name** を入力する。
<img width="600" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/bf1a2d1c-d81c-fb6d-80b1-a841f36ac634.png">

以下が先ほど入力した **Product Name** になっていることがわかる。

- プロジェクトファイル名
- ターゲット名
- ターゲット設定の General タブにある **Display Name**

このうち、最後の **Display Name** がホーム画面に表示されるアプリ名となる。
<img width="600" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/b3372768-8675-f3b4-3141-aaba58f5c01d.png">

## アプリ名を変更するには？

この **Display Name** を変更してみると、プロジェクトファイルの `PRODUCT_NAME` が変わる。また、変更前の `PRODUCT_NAME` はターゲット名 `TARGET_NAME` であったこと、そして `PRODUCT_NAME` がアプリケーションファイル (.app) 名になることがわかる。
<img width="600" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/40c3a599-7c6c-8dd3-7e15-9c03062a880d.png">

アプリケーションファイル名が変わるのは気持ち悪いので `PRODUCT_NAME` を変更しないようにしたい。そんな時のために **Bundle display name** という設定がある。これはターゲット設定の Info タブで追加できるので、追加してアプリ名を入力してみる。
<img width="600" alt="4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/973c87f6-fc15-1bbd-56a7-c742214f8bd5.png">
<img width="600" alt="5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/16ccaf2a-bf0a-e1c5-569b-cd4073d2e764.png">

General タブの **Display Name** は入力した名前に変わった。ホーム画面上でも変わっている。
<img width="600" alt="6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/92b427d5-129f-1b7c-5419-f1450d25fdb2.png">

<img width="300" alt="11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/7419deb6-909e-ab56-783c-2abdf8c4be90.png">


その正体は Info.plist ファイルの [CFBundleDisplayName](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundledisplayname) キー。公式ドキュメントにも _ホーム画面で表示される名前_ という説明がある。

> The user-visible name for the bundle, used by Siri and visible on the iOS Home screen.

<img width="600" alt="7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/6cb6861a-d73b-95d5-185c-62bee1b552e1.png">

## アプリ名をローカライズするには？
アプリ名は Info.plist の CFBundleDisplayName で決まるので、これをローカライズしたい。その方法は公式ドキュメント [About Information Property List Files](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html) の Localizing Property List Values という章で説明されているとおり、**InfoPlist.strings** という Info.plist 国際化用の strings リソースファイルを用意する。

> Localized values are not stored in the **Info.plist** file itself. Instead, you store the values for a particular localization in a strings file with the name **InfoPlist.strings**. 

InfoPlist.strings ファイルの作成方法、記述方法、言語別に用意する方法は、通常の文字列リソースファイルをローカライズする場合と同様。プロジェクト設定でローカライズ対象言語を設定する。
<img width="600" alt="8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/c30c79a5-fac2-4b88-e03a-c7ae52572098.png">

InfoPlist.strings ファイルをプロジェクトに追加する。
<img width="600" alt="9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/99708254-aac8-6aa6-8d42-9c20cf57e5c3.png">

言語別に CFBundleDisplayName を設定する。
<img width="1000" alt="10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/cb9674f7-8deb-507d-e223-f1d93c1d7765.png">

これでデバイスの言語設定によってアプリ名が変わる。
<img width="300" alt="11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/6ccda996-80fa-2857-05e5-a5dea9115721.png"> <img width="300" alt="12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/7eb5fe13-2381-e025-8088-6dd5e892d1a8.png">

## Build Configuration に応じてアプリ名を切り替えるには？
ターゲット設定の Build Settings タブにある設定項目は Build Configulation (Debug, Releaseなど) ごとに値を変更できる。ここには **User-Defined** というユーザー定義の設定を追加することができる。
<img width="600" alt="14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/2a252dac-fd09-41b9-2e2e-7fb520e0598f.png">

ここに、例えば `BUNDLE_DISPLAY_NAME` のようなキーを追加し、Build Configuration ごとにアプリ名となる値を入力する。
<img width="600" alt="15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/32e457c9-d111-c2d4-2403-77d60f7ea490.png">

そして、Info.plist の Bundle display name の部分に、先ほどの `BUNDLE_DISPLAY_NAME` を参照させる。
<img width="600" alt="16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/b25d580f-2be8-3ec5-8adf-1d826ea41d19.png">

General タブの Display Name は、Run を実行する Build Configuration のものが表示されている。
<img width="600" alt="17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/cc5771c6-7b52-7c56-993e-a0ecb96fce96.png">

アプリ名にもちゃんと反映されている。
<img width="300" alt="18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16508/4a3ee6a1-c5a3-b8d9-90cb-d9b0007ce5c2.png">

## Build Configuration に応じてアプリ名を切り替えつつローカライズするには？
上記の手段では、最終的に InfoPlist.strings の方のアプリ名が優先されてしまい、両立することができない。Xcode に用意されている仕組みでは、これを両立する手段はなさそう。

ただ、開発の要件上、このような対応が必要になったため、次のようにして力技で対応してみた。

まず、全言語の InfoPlist.strings に対して、`CFBundleDisplayName_{Build Configuration}` というキーで、Build Configuration ごとにアプリ名を定義する。

```swift:en.lproj/InfoPlist.strings
"CFBundleDisplayName_Debug" = "HogeDebug"; 
"CFBundleDisplayName_Release" = "HogeRelease"; 
```
```swift:ja.lproj/InfoPlist.strings
"CFBundleDisplayName_Debug" = "ほげデバッグ"; 
"CFBundleDisplayName_Release" = "ほげリリース"; 
```

そして Build Phases の Run Script で全言語の InfoPlist.strings に対して、ビルド対象の Build Configuration に該当する `CFBundleDisplayName_{Build Configuration}` を `CFBundleDisplayName` に置換する。例えば Release の場合は ``CFBundleDisplayName_Release` が置換対象となる。

```shell:置換
sed -i "s/\"CFBundleDisplayName_$CONFIGURATION\"/\"CFBundleDisplayName\"/" path/to/InfoPlist.strings 
```

```swift:en.lproj/InfoPlist.strings
"CFBundleDisplayName_Debug" = "HogeDebug"; 
"CFBundleDisplayName" = "HogeRelease"; 
```
```swift:ja.lproj/InfoPlist.strings
"CFBundleDisplayName_Debug" = "ほげデバッグ"; 
"CFBundleDisplayName" = "ほげリリース"; 
```