XcodeGen+SwiftPMを入れてみた

# はじめに
[前回の記事](https://qiita.com/doi_daihei/items/594c15282e70c7d9235b)でXcodeGenを導入したプロジェクトの話をしました。
その記事で紹介したプロジェクトではCocoaPodsとCarthageを用いてライブラリを管理していました。
そろそろXcode9で導入された[Swift Package Manager](https://swift.org/package-manager/)(以下、SwiftPM)に移行してみてもいいかなと考え、XcodeGenはそのままでライブラリ管理をSwiftPMにしてみた際の知見を話します。

XcodeGenの解説は[前回の記事](https://qiita.com/doi_daihei/items/594c15282e70c7d9235b)を参考にしていただき、本稿では割愛します。

# SwiftPMとは
CocoaPodsやCarthage同様にSwiftで構成されているパッケージを管理するツールです。
他にない強みとしては以下です。

- **Apple製**
- XcodeのIDEサポートがある

Apple製なので、Xcodeを含めたツール周りで親和性が高いので今後はSwiftPM使っといた方がいいかなと考えています。
Xcode11以前では`Package.swift`というソースコードで管理し、ツールアプリやサーバー側ソースコードしか扱えませんでしたが、Xcode11よりIDEサポートが導入され、Xcodeで完結できるようになりました。

SwiftPM対応しているライブラリはプロジェクトルートに `Package.swift` が置いてあるプロジェクトは基本できます。

# XcodeGen+SwiftPM
やり始める前は `Package.swift` が必要になるかなと思いました。
ですが、終わってみるとXcodeGenのプロジェクト構成ファイル`project.yml`のみで完結できました。

<details><summary>完成したものでSwiftPM関係ある部分を抜粋しました。クリックで開きます。</summary><div>

```yml:project.yml
name: test
packages: # SwiftPMで扱うPackageを最初に定義
  Reusable:
    url: https://github.com/AliSoftware/Reusable
    exactVersion: 4.1.1 # バージョン指定 versionでもOK
  RxSwift:
    url: https://github.com/ReactiveX/RxSwift
    minorVersion: 5.1.1 #指定マイナーバージョン ex: 5.1系ならどれでもOK
  Action:
    url: https://github.com/RxSwiftCommunity/Action
    branch: master # ブランチ指定
  RxSwiftExt:
    url: https://github.com/RxSwiftCommunity/RxSwiftExt
    minVersion: 5.1.0 # 指定バージョン以上かつ
    maxVersion: 5.2.0 # 指定バージョンより小さい
  APIKit:
    url: https://github.com/ishkawa/APIKit
    revision: b95a941 # コミット指定
  Rswift:
    url: https://github.com/mac-cain13/R.swift.Library
    majorVersion: 5.2.0 #指定メジャーバージョン fromでもOK ex: 5系ならどれでもOK
  SwiftyUserDefaults:
    url: https://github.com/sunshinejr/SwiftyUserDefaults
    from: 5.0.0 # majorVersionと同義
# 後は同様の書き方、細かい設定なので、割愛
targets:
  test:
    type: application
    platform: iOS
    scheme: {}
    deploymentTarget: "13.0"
    sources:
      - test
      - path: "test/Resources/R.generated.swift"
        optional: true
        type: file
    dependencies:
      - target: TestFramework # Embedded Framework
      - package: Action
      - package: Reusable
      - package: Rswift
      - package: RxSwift 
        product: RxCocoa # RxSwift内部のRxCocoaを指定する
      - package: RxSwift
        product: RxRelay # RxSwift内部のRxRelayを指定する
      - package: RxSwift
      - package: RxSwiftExt
    # 後は設定周りなので、本稿では割愛
  TestFramework:
    type: framework
    platform: iOS
    scheme: {}
    deploymentTarget: "13.0"
    sources:
      - TestFramework
    dependencies:
      - package: APIKit
      - package: SwiftyUserDefaults
    # 後は設定周りなので、本稿では割愛
```

</div></details>

全体の設定を確認した場合は[こちら](https://github.com/doihei/rx-practice/blob/feature/using-article/project.yml)をご覧ください。

後はプロジェクトルートで`xcodegen`を叩くとSwiftPMのライブラリ依存関係が解決したプロジェクトが生成されます。
生成プロジェクトをXcodeで開くとそれぞれのfetchが始まり、しばらくすると以下のように Project Navigator上で確認ができます。

![xcode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/6a28ce3f-2369-14df-afc7-8e0addb284fc.png)

次に細かい定義に関して説明していきます。

## 1. プロジェクト全体で扱うPackageを定義
まずは[XcodeGenのドキュメント](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#swift-package)を参考にプロジェクト全体で扱うパッケージをそれぞれ定義します。
`url` にて、リモート先を指定し、その後のパラメータでバージョンなどの細かい指定ができます。

今回は特にローカルで扱うものがなかったのですが、先述のドキュメントにも記載の通り導入ができます。

```yml:project.yml
name: test
packages: # SwiftPMで扱うPackageを最初に定義
  Reusable:
    url: https://github.com/AliSoftware/Reusable
    exactVersion: 4.1.1 # バージョン指定 versionでもOK
  RxSwift:
    url: https://github.com/ReactiveX/RxSwift
    minorVersion: 5.1.1 #指定マイナーバージョン ex: 5.1系ならどれでもOK
  Action:
    url: https://github.com/RxSwiftCommunity/Action
    branch: master # ブランチ指定
  RxSwiftExt:
    url: https://github.com/RxSwiftCommunity/RxSwiftExt
    minVersion: 5.1.0 # 指定バージョン以上かつ
    maxVersion: 5.2.0 # 指定バージョンより小さい
  APIKit:
    url: https://github.com/ishkawa/APIKit
    revision: b95a941 # コミット指定
  Rswift:
    url: https://github.com/mac-cain13/R.swift.Library
    majorVersion: 5.2.0 #指定メジャーバージョン fromでもOK ex: 5系ならどれでもOK
  SwiftyUserDefaults:
    url: https://github.com/sunshinejr/SwiftyUserDefaults
    from: 5.0.0 # majorVersionと同義
  # 後は同様の書き方、細かい設定なので、割愛
```

### バージョン記法

 記法 | 指定内容 
:-----------:|:------------:
 `majorVersion: n.m.i` or `from: n.m.i`      | 指定メジャーバージョン n系ならどれでもOK 
 `minorVersion: n.m.i`      | 指定マイナーバージョン n.m系ならどれでもOK 
 `exactVersion: n.m.i` or `version: n.m.i`      | バージョン指定 versionでもOK 
 `branch: main`      | 指定ブランチ 
 `revision: xxxxxx`      | コミット指定 
※ XcodeGen2.18.0時点

## 2. 依存関係をTargetに記述
次にそれぞれのTargetにて[XcodeGenのドキュメント](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/Usage.md#swift-package)を参考に依存関係記述していきます。

ポイントとしては、`RxSwift`みたいなプロジェクトの中にあるライブラリの場合は、さらに指定が必要になります。

```yml:project.yml
targets:
  test:
    type: application
    platform: iOS
    scheme: {}
    deploymentTarget: "13.0"
    sources:
      - test
      - path: "test/Resources/R.generated.swift"
        optional: true
        type: file
    dependencies:
      - target: TestFramework # Embedded Framework
      - package: Action
      - package: Reusable
      - package: Rswift
      - package: RxSwift 
        product: RxCocoa # RxSwift内部のRxCocoaを指定する
      - package: RxSwift
        product: RxRelay # RxSwift内部のRxRelayを指定する
      - package: RxSwift
      - package: RxSwiftExt
    # 後は設定周りなので、本稿では割愛
  TestFramework:
    type: framework
    platform: iOS
    scheme: {}
    deploymentTarget: "13.0"
    sources:
      - TestFramework
    dependencies:
      - package: APIKit
      - package: SwiftyUserDefaults
    # 後は設定周りなので、本稿では割愛
```

## 注意点
[こちら](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/Usage.md#swift-package)でも記載がありますが、Xcodeより[File] -> [Swift Packages] -> [Add Package Dependency...]から行った場合は、`ProjectName.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved`に生成されます。

Xcodeから新たにライブラリを導入した場合、上記の更新対象ファイルを`project.yml`に落とし込む必要があり、めんどくさいです。

※ `Package.resolved`は実際の取り扱いバージョン情報の定義がされています。`CocoaPods`でいう`Podfile.lock`のようなものです。

# まとめ
導入自体は[XcodeGen](https://github.com/yonaskolb/XcodeGen)のドキュメント見ながら行えば、簡単にできます。
Xcode11よりIDE上で管理できるので効率化や管理コスト削減などは見込めると思います。

ただ注意点でも述べたように`project.yml`に落とす作業が他と比べてめんどくさいです。
後、やっぱりCocoaPodsやCarthageに比べると対応しているものが少ないかなと思います。

なので、ライブラリ依存が少なく済む小規模のプロジェクトとかだと導入してみてもいいんじゃないでしょうか。