プロジェクトファイルからXcodeGenのproject.ymlを生成する

# はじめに

数年経つプロジェクトに途中からXcodeGenを入れるのは骨折れる作業が、、、

そんな中、気になる内容を見つけた

<img width="594" alt="スクリーンショット 2020-02-03 2.16.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/ccbb8198-8967-7fb0-c68d-12cfc9260617.png">


お、やってみよ！

# 導入

## 0. 前提

[#735](https://github.com/yonaskolb/XcodeGen/pull/735)は、まだマージされていないので未完成です。
PRの説明でもそれは記載されています。

<img width="674" alt="スクリーンショット 2020-02-01 16.19.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/4e838d5b-d418-0eb5-2d9f-1eafb5151541.png">

いくつか対応がなされていないようです。
なので、使用してみてメリデメの調査を含めてやっていきます。

## 1. インストール

PRのブランチを指定してXcodeGenを落としてきます。
その後、インストールしましょう。

```
$ git clone -b spec-generation https://github.com/yonaskolb/XcodeGen.git

$ cd XcodeGen

$ make install
```

※ make install で少し時間がかかるので注意
※ ブランチ指定するために普通にcloneしていますが、他の方法でも指定できるならそれでも問題ありません

## 2. project.ymlの生成

プロジェクトのルートディレクトリまで移動し、コマンドを実行します。

```
$ xcodegen migrate --project xxx.xcodeproj --spec project.yml
```
（``xxx.xcodeproj``は各自のプロジェクト名に変更してください）

すると、ルートディレクトリに``project.yml``が生成されているはずです。

と言うわけで簡単に行えました！


# 問題点と解決策

実際に運用するとなるといくつかの問題点が見えてきました。
（まだPRが未完成なので、当たり前っちゃ当たり前ですが、、笑）

## 1. 設定したConfigurationが生成されない

以下のように、３つを設定していましたが

<img width="562" alt="スクリーンショット 2020-02-01 19.00.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/f203d588-bedc-3283-a3ca-40371c7b6da2.png">

実際にymlに生成されたものは以下でした。

<img width="196" alt="スクリーンショット 2020-02-01 18.56.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/473c13d5-b9d4-9308-ae95-ff00eb00e615.png">

これは他のプロジェクトでも試しましたが、**debug/releaseしか生成されない**ようです。

なので自前で書く必要があります。が、**configsの部分**だけで問題ありません。
なぜなら、設定やアプリへの紐付け部分は吐き出されています。

<img width="1000" alt="スクリーンショット 2020-02-01 19.11.35 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/a386ad8f-ae41-1dd1-5822-412aa6f402ec.png">

（長いので間を省略しています）

このように``debug/develop/release``の３つ分の設定が、吐き出されているのがわかります。
なので、configsで定義し直しあげることで、ちゃんと動くようになります。

設定してあげないと、``Xcodegen generate``コマンドでこんなエラーが出ます。

<img width="472" alt="スクリーンショット 2020-02-01 19.19.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/93df11c5-d42d-0917-4183-a49ba055754e.png">


設定に関しては、この記事を参考にすると良いでしょう。
[Xcodeプロジェクトの生成ツール「XcodeGen」のセットアップ&操作方法](https://qiita.com/uhooi/items/16a870eaae24b46103fb)


## 2. 設定したschemeが生成されない

以下のように、３つを設定していましたが

<img width="215" alt="スクリーンショット 2020-02-01 19.02.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175242/e55fd1cf-a11f-d2a2-aac1-89a19168c5d8.png">

実際にymlには何も生成されていませんでした。
こちらもConfiguration同様に追加する必要があります。

最低限、動くようになる例を置いておきます。

```.yml
schemes:
  MailerTestAppDebug:
    build:
      targets:
        MailerTestApp: all
        MailerTestAppTests: [test]
        MailerTestAppUITests: [test]
  MailerTestAppDevelop:
    build:
      targets:
        MailerTestApp: all
  MailerTestAppRelease:
    build:
      targets:
        MailerTestApp: all
```

アプリ名は各自のでお願いします。
実際はもっと細かく設定してください。

## 3. xcconfigの紐付けが生成されない

こちらも自前で追加しましょう

```.yml
configFiles:
  Debug: configs/Debug.xcconfig
  Develop: configs/Develop.xcconfig
  Release: configs/Release.xcconfig
```

そもそもymlに全て移行してしまうのも手ではありますが、、

## 4. 生成されたymlが肥大化

**余計なコード**が入ってしまい肥大化がみられました。

**では、何が余計なのでしょうか、、？**

以下、いくつかの例を紹介します。

### ① Clang（コンパイラ）の設定

Xcodeにデフォルトで設定されている、コンパイラの部分が吐き出されることで肥大化します。
**SchemeやTestのTargetごと**に吐かれるため、以下のようなコード群が**いくつも生成**されます。

```.yml
    CLANG_ANALYZER_NONNULL: YES
    CLANG_CXX_LANGUAGE_STANDARD: gnu++0x
    CLANG_CXX_LIBRARY: libc++
    CLANG_ENABLE_MODULES: YES
    CLANG_ENABLE_OBJC_ARC: YES
    CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING: YES
    CLANG_WARN_BOOL_CONVERSION: YES
    CLANG_WARN_COMMA: YES
    CLANG_WARN_CONSTANT_CONVERSION: YES
    CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS: YES
    CLANG_WARN_DIRECT_OBJC_ISA_USAGE: YES_ERROR
    CLANG_WARN_EMPTY_BODY: YES
    CLANG_WARN_ENUM_CONVERSION: YES
    CLANG_WARN_INFINITE_RECURSION: YES
    CLANG_WARN_INT_CONVERSION: YES
    CLANG_WARN_NON_LITERAL_NULL_CONVERSION: YES
    CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF: YES
    CLANG_WARN_OBJC_LITERAL_CONVERSION: YES
    CLANG_WARN_OBJC_ROOT_CLASS: YES_ERROR
    CLANG_WARN_RANGE_LOOP_ANALYSIS: YES
    CLANG_WARN_STRICT_PROTOTYPES: YES
    CLANG_WARN_SUSPICIOUS_MOVE: YES
    CLANG_WARN_UNREACHABLE_CODE: YES
    CLANG_WARN__DUPLICATE_METHOD_MATCH: YES
```

特定の意図がない限りはいじることがないので、ymlからごっそり消して良いかと思います。
（ymlに記載しなくても、デフォルトで設定されるので）

### ② GCC（コンパイラ）の設定

①と同じですが、以下のようなコード群がいくつも生成されます。

```.yml
    GCC_WARN_64_TO_32_BIT_CONVERSION: YES
    GCC_WARN_ABOUT_RETURN_TYPE: YES_ERROR
    GCC_WARN_UNDECLARED_SELECTOR: YES
    GCC_WARN_UNINITIALIZED_AUTOS: YES_AGGRESSIVE
    GCC_WARN_UNUSED_FUNCTION: YES
    GCC_WARN_UNUSED_VARIABLE: YES
```

こちらもymlからごっそり消して良いかと思います。

### ③ Carthege(framework)の設定が入ってしまう

このように列挙されてしまいます。

```.yml
    - inputFiles:
      - $(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
      - $(SRCROOT)/Carthage/Build/iOS/Realm.framework
      - $(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework
      - $(SRCROOT)/Carthage/Build/iOS/SwiftyStoreKit.framework
      - $(SRCROOT)/Carthage/Build/iOS/Reachability.framework
      - $(SRCROOT)/Carthage/Build/iOS/KeychainAccess.framework
      - $(SRCROOT)/Carthage/Build/iOS/RxSwift.framework
      - $(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework
      - $(SRCROOT)/Carthage/Build/iOS/Nuke.framework
      - $(SRCROOT)/Carthage/Build/iOS/NukeWebP.framework
```

dependenciesにライブラリ(framework)を記載しておけば、XcodeGenが勝手に紐づけてくれるのでごっそり消してしまいましょう。

### ④ Cocoapod(framework)の設定が入ってしまう

```.yml
    - inputFiles:
      - ${PODS_ROOT}/Target Support Files/Pods-delish/Pods-delish-frameworks.sh
      - ${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework
      - ${BUILT_PRODUCTS_DIR}/FBSDKLoginKit/FBSDKLoginKit.framework
      - ${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework
     name: '[CP] Embed Pods Frameworks'
      outputFiles:
      - ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework
      - ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework
      - ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKLoginKit.framework
      - ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework
      runOnlyWhenInstalling: false
      script: '"${PODS_ROOT}/Target Support Files/Pods-delish/Pods-delish-frameworks.sh"'
      shell: /bin/sh
      showEnvVars: false
```

```.yml
    OTHER_LDFLAGS:
    - $(inherited)
    - -ObjC
    - -l"c++"
    - -l"sqlite3"
    - -l"stdc++"
    - -l"z"
    - -framework
    - '"AVFoundation"'
    - -framework
    - '"Accounts"'
    - -framework
    - '"AddressBook"'
           ・
           ・
           ・
```

``pod install``で勝手に追加されるので、ごっそり消してしまいましょう。

### ⑤ １ファイルごとに登録されてしまう

以下のように列挙された形で出力されます。

```.yml
    sources:
    - app/AppDelegate.swift
    - app/Extensions/UIImageView+Extensions.swift
    - app/Extensions/UIView+Extensions.swift
    - app/Extensions/UITableView+Extensions.swift
    - app/Extensions/UICollectionView+Extensions.swift
                ・
                ・
                ・
```

このように一覧で出るため、**グループ化**はされていないのです。

なので、以下のようにしてまとめてあげると良いでしょう。

```.yml
    sources:
    - path: app
    - path: Extensions
      type: directory
```

``XcodeGen``の``type directory``で簡潔になりました。

## 4. すべてのファイルがルートディレクトリにきてしまう（Xcode上で）

これは「③ １ファイルごとに登録されてしまう」に付随しますが、１ファイルごとに登録されてしまうために起こります。

こちらは、sourcesで読み込みたいファイルを指定することで解決できます。
（なので、同様に不要なpathの参照をまとめるなり・消すなりする必要があります）

# 考察

## メリット
「**かゆいオプションを描き出さなくて済む**」
これに尽きるかと思います。

buildのsettingsもそうですが、たとえば、

```
- Run script
- Notification/today などのextension
```

など、見比べてymlに書いていくのは以外に面倒だったりします。笑

## デメリット
問題点に基本的に集約されますが、

```
PRがまだ未完成
  - 一部の設定が反映されない
  - ymlの肥大化
```

「**一部**」の設定と書いてますが、まあまああるのでなんとも言えないラインではあります、、

## 現状

**既存プロジェクトにおいては、こちらを使ってみるのはアリ**だと思います。

というのも、基本的には吐き出されないScheme/Configurationの設定だけ、自分で追記すればビルドできるので、ymlが肥大化しようが関係ないです。
（sourcesでディレクトリ構成も設定する必要あり）

既存でこちらを導入する場合は、こちら（PRのXocdeGen）でつくったymlを叩き台にして、ymlファイルを整理していくのが良いかと思います。

ただ、**新規アプリでXcodeGenを導入するなら、自前でymlを書いた方良い**かと思います。
行数も少なくてすみ、なにより読みやすいかと思います。
（作ったばかりのプロジェクトも含む）

#終わりに

XcodeGenのメリットは、よく「コンフリクトしなくなること」が例に挙げられますが、そこはそんなにメリットではなく
（普通に開発ルールを守っていればそんなに大きくおきないし、解消するのも難しくない）

こちらの記事にもありますが
[（株式会社キュリオシティソフトウェア - 2019年上半期のiOSアプリ開発振り返り）](http://curiosity.co.jp/2019ios/)
> わざわざSchemeで設定を変えるみたいな変なことやっててもConfigurationに差し替え可能
> Scheme間やConfiguration間の設定値の差分がコードなのでわかりやすい

つまり、**XcodeのGUIでしか閲覧・編集できなかった**ことが、一覧で見れることの方が大きいです。

既存でこちらを導入する場合は、その一覧で見れるメリットが少し薄れてしまいます。
（行数多いし、無駄なコードが設定が増えてしまうため比較しづらい）

まだまだ、開発途中なので本体にmergeされることを期待しつつ、、と言う感じでしょうか笑