最低限困らない SwiftUI を指定した Xcode プロジェクトを XcodeGen で生成する

この記事は、[ゆめみ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/yumemi) の17日目の記事です。

# はじめに
XcodeGen に入門しようと SwiftUI を指定した Xcode プロジェクトを題材にしたら、ビルドするためにちょっとした設定が必要でした。
この記事ではその原因と対応策を紹介します。

# 環境
- macOS 12.0.1
- Xcode 13.1
- XcodeGen 2.25.0

# 成果物
[gist](https://gist.github.com/macneko-ayu/2621414f90189f76c49bfc404e637d1d) にあげていますが、Qiita では展開されないので貼っておきます。

```yml
# 任意のProduct Nameに置き換える（Targetも同様）
name: FooProduct

options:
  bundleIdPrefix: com.example
  deploymentTarget: 
    iOS: 15.0
  xcodeVersion: "13.1"

settings:
  base:
    # 任意のTeam IDに置き換える
    DEVELOPMENT_TEAM: (YOUR_TEAM)
    # すべてのTargetで共通にするためにここで指定する
    MARKETING_VERSION: 1.0.0
    # すべてのTargetで共通にするためにここで指定する
    CURRENT_PROJECT_VERSION: 1
    # Xcode 13.1のテンプレートから作成したものと差分があったので、そろえている
    CLANG_CXX_LANGUAGE_STANDARD: gnu++17
    # 任意でManualに置き換える
    CODE_SIGN_STYLE: Automatic

targets:
  # アプリのTarget定義を記載する
  FooProduct:
    type: application
    platform: iOS
    sources: 
      - FooProduct
    settings:
      base:
        ENABLE_PREVIEWS: YES
        DEVELOPMENT_ASSET_PATHS: "\"FooProduct/Preview Content\""
        ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
    # 生成するInfo.plistの設定を記載する
    info:
      # 生成先のPathを指定する
      path: FooProduct/Info.plist
      # 各種設定は基本的にXcode 13.1のテンプレートから作成したものとそろえている
      properties:
        # settingsで設定した環境変数を使用する
        CFBundleVersion: $(CURRENT_PROJECT_VERSION)
        # settingsで設定した環境変数を使用する
        CFBundleShortVersionString: $(MARKETING_VERSION)
        LSRequiresIPhoneOS: YES
        # UILaunchScreenをダミーでもいいので設定しないと、描画がおかしくなる
        UILaunchScreen: ""
        UIApplicationSupportsIndirectInputEvents: YES
        UIApplicationSceneManifest: 
          UIApplicationSupportsMultipleScenes: YES
        # Device OrientationはXcode 13.1のテンプレートとそろえている
        UISupportedInterfaceOrientations~iphone:
          [UIInterfaceOrientationPortrait, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight]
        UISupportedInterfaceOrientations~ipad:
          [UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight]

  FooProductTests:
    # UnitTestのTarget定義を指定する
    type: bundle.unit-test
    platform: iOS
    sources: 
      - FooProductTests
    dependencies:
      - target: FooProduct
    settings:
      base:
        ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: YES
    # 生成するInfo.plistの設定を記載する
    info:
      # 生成先のPathを指定する
      path: FooProductTests/Info.plist
      # 各種設定は基本的にXcode 13.1のテンプレートから作成したものとそろえている
      properties:
        CFBundleVersion: $(CURRENT_PROJECT_VERSION)
        CFBundleShortVersionString: $(MARKETING_VERSION)

  FooProductUITests:
    # UITestのTarget定義
    type: bundle.ui-testing
    platform: iOS
    sources: 
      - FooProductUITests
    dependencies:
      - target: FooProduct
    settings:
      base:
        ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: YES
    # 生成するInfo.plistの設定を記載する
    info:
      # 生成先のPathを指定する
      path: FooProductUITests/Info.plist
      # 各種設定は基本的にXcode 13.1のテンプレートから作成したものとそろえている
      properties:
        CFBundleVersion: $(CURRENT_PROJECT_VERSION)
        CFBundleShortVersionString: $(MARKETING_VERSION)
```

簡単にビルドしたい人向けにサンプルプロジェクトも用意しました。
`DEVELOPMENT_TEAM` だけご自身の環境にあわせて変えていただいたら試せると思います。

[SwiftUISampleWithXcodeGen v0.0.1](https://github.com/macneko-ayu/SwiftUISampleWithXcodeGen/releases/tag/0.0.1)
（今後手を入れていく予定なので、記事執筆時点のタグを指定しています）

# 原因
## `Info.plist` が生成されない
SwiftUI を指定した Xcode プロジェクトでは `Info.plist` が生成されないので、`project.yml` で既存のファイルを読み込むことができませんでした。
`Info.plist` の設定をなにもせずプロジェクトを生成すると、Targetの `Info` のタブに「Information from info.plist not available. File may not exist at specified path.」という、Info.plistの情報が読み込めない旨のメッセージが表示されます。

<img width="564" alt="スクリーンショット 2021-12-15 11 21 40" src="https://user-images.githubusercontent.com/5406126/146111243-d3026b80-1c96-410c-ae1a-e9c2fdf65bc8.png">

SwiftUI を指定した Xcode プロジェクトでは `Info.plist` が生成されないことも、いままで Info.plist に記載されていた設定が Xcode 13.0 からプロジェクトファイルの中に書き込まれているということも初めて知ったので、かなり面食らいました。

## Xcode の Release Notes に書いてあった

ちゃんとチェックしないとだめですね…。

> Projects created from several templates no longer require configuration files such as entitlements and Info.plist files. Configure common fields in the target’s Info tab, and build settings in the project editor. These files are added to the project when additional fields are used. (68254857)

https://developer.apple.com/documentation/xcode-release-notes/xcode-13-release-notes

# 対応
## 方針
原因は `Info.plist` が生成されないことだとわかったので、`Info.plist` を作成して `project.yml`  で読み込むようにすれば、ビルドできるようになりそうです。
[XcodeGen のドキュメント](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#plist) を読むと、指定したパスに `Info.plist` を生成できるとあったので、 `project.yml` に必要な設定を記述すればうまくいきそうです。

## やったこと
Xcode のテンプレートから SwiftUI を指定した Xcode プロジェクトを作成して、`Info` タブに記載されている内容を地道に転記しました。
この作業をするために、記述するべき key/value を調べようと `Info.plist` を開こうとして、「いや、それを作ろうとしてるんですよ…」ってなりました。
ずーっと不満なんですが、Xcode から見た `Info.plist` の key/value と実際に記述するべき key/value が統一されていないのは、なんでですかね。

そして、作業中にたまたま `Info` タブでコンテクストメニューを表示したら「Raw Keys & Values」という項目を見つけまして、この項目を選択すれば記述するべき key/value に切り替わることを知りました。
最初からそっちを表示してもいいんですよ。

項目選択前 | 項目選択後
--- | ---
<img width="1127" alt="スクリーンショット 2021-12-15 12 14 45" src="https://user-images.githubusercontent.com/5406126/146116595-b8c63e2d-a633-4adf-ae4a-61d46ae28c9d.png"> | <img width="1125" alt="スクリーンショット 2021-12-15 12 14 54" src="https://user-images.githubusercontent.com/5406126/146116583-18bbaf52-3f53-46a1-bf60-b7e7602b0482.png">

また詳細は割愛しますが、UnitTestやUITestもビルドできなかったので、 `Info.plist` を作成するようにしました。

## 面白いなって思ったこと
`Info.plist` に `UILaunchScreen` という設定があるのですが、これを設定しないと描画がおかしくなりました。

UILaunchScreen の設定がない | UILaunchScreen の設定がある
--- | ---
<img src="https://user-images.githubusercontent.com/5406126/146117189-51f690c3-a7b6-486b-8964-668653ab7105.png" width="320" /> | <img src="https://user-images.githubusercontent.com/5406126/146117195-26cd6786-0a55-40cb-a5af-5723fc5570db.png" width="320" />

ドキュメントによるとアプリの起動画面を指定するとありますが、いろいろ value を変えて試したところ、key が存在すれば描画はうまくいくみたいでした。

https://developer.apple.com/documentation/bundleresources/information_property_list/uilaunchscreen

Xcode のテンプレートでは以下のようになっていたのですが、`project.yml` でどう指定したらいいかわからず、今回は String として指定しています。
<img width="691" alt="スクリーンショット 2021-12-15 12 36 58" src="https://user-images.githubusercontent.com/5406126/146118686-83ff59c1-a15f-459d-840e-caa666686894.png">

# さいごに
今回作成したリポジトリは、今後ビルド時に SwiftLint を実行するための定義を追加したり、GitHub Actions と連携させたり、いろいろ手を入れていこうとおもいます。