iOSアプリ開発にSwift Package Managerを使おう

Xcode 11からSwift Package Manager（以下、SwiftPM）がiOSアプリ開発にも使えるようになりました :tada:

#  SwiftPMとは

[SwiftPM](https://swift.org/package-manager/)は、Swiftのソースコードで構成されたパッケージを、依存関係を解決しつつ利用できるようにしてくれる、Apple製のパッケージ管理ツールです。オープンソースで公開されています。iOS開発者の人なら、CocoaPodsやCarthageのようなものだと言った方が伝わるかもしれません。

- GitHubリポジトリ  
https://github.com/apple/swift-package-manager

Xcode 11が登場するまでは、SwiftPMを使ったアプリというのはコマンドラインからのビルド＆実行しかありませんでした。ですから、macOSまたはLinuxのターミナル上で実行するCUIアプリやサーバーサイドのプログラムなどに利用範囲が限られていました。
Xcode 11からはiOSアプリのプロジェクトでもSwiftPMのパッケージが使えるようになりました。また、Xcodeでパッケージ開発もサポートされるようになりました。標準のIDEのサポートがあるというのは強みになると思うので、今後はiOSアプリ開発でもSwiftPMが注目されていくものと思います。

この記事ではXcode 11でのSwiftPM対応を、iOSアプリ開発に焦点を当てて簡単に紹介します。

# パッケージを使うには

## 利用するパッケージの追加

Xcodeで開発しているiOSアプリのプロジェクトに利用するパッケージを追加することができます。なお、追加したパッケージの情報は、プロジェクトファイル（.xcodeproj）の中に記録されます。つまり、Package.swiftで依存先を記述するわけではありません。

実際の手順ですが、まず、パッケージを使いたいiOSプロジェクトを開きます。
そして、Xcodeのメニューから［File］－［Swift Packages］－［Add Package Dependency］を実行しましょう。

<img width="618" alt="AddPackage Dependency" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/b940da28-80a6-41bd-12e3-662de9e5c72b.jpeg">

続いて表示されるダイアログで使いたいパッケージを選択します。

<img width="722" alt="Choose Package Repository" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/8903ffb9-133b-bab6-dfe5-aa175cc659dd.jpeg">

このダイアログ、なんとなくGitHubからしか選択できないような印象を受けますが、上の検索ボックスにリポジトリのURLを直接入力することもできるので、GitHub以外にあるgitリポジトリも指定できます。

次に、どのバージョンを使いたいかを指定します。

<img width="722" alt="Choose Package Options" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/ed1f39d2-7033-2590-8f83-9ec80dd85a56.jpeg">

大抵の場合は［Version］を選んで［Up to Next Major］でバージョン番号を指定することが多いと思います。例えば、 `Up to Next Major 4.0.0` だと（画面にズバリ表示されている通り）4.0.0 < 5.0.0、つまり、4.0.0以降の4系ならどれでもOKと指定したことになります。パッケージのバージョンは [Semantic Versioning](https://semver.org/) に従っています。大雑把に言ってしまえば、互換性に影響を与えないバグ修正なら3つ目を上げる、互換性に影響を与えない機能追加なら2つ目を上げる、互換性に影響を与えるものなら1つ目を上げる、というものなので、1つ目が同じなら互換性は保たれているはずで、これで十分なことが多いのです。他に、特定のバージョン範囲を指定したり、特定のバージョンで固定したり、特定のブランチやコミットを指定することもできます。

次に、プロジェクト内のどのターゲットで、そのパッケージをどのプロダクトを使いたいかを指定します。パッケージによっては複数のプロダクトが公開されているものがあります。ここで使いたいプロダクトを選びます。

<img width="722" alt="Choose package products and targets" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/86662b41-2742-6dee-aaa4-3045f40b7776.jpeg">

［Finish］でパッケージの追加は終わりです。左側のProject NavigatorにSwift Package Dependenciesというセクションが生まれ、実際に取得されたパッケージとバージョンが表示されます。パッケージが別のパッケージに依存していれば、さらに依存先のパッケージも取得されます。

また、プロジェクト設定のところにある［Swift Packages］タブに追加されたパッケージが表示されるので、ここでそれらを編集・削除することができます（ここからパッケージを追加することもできます）。

<img width="988" alt="Package Dependencies" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/b7018b62-2f9a-c51d-bdb1-aca6734c6c53.jpeg">

あとは使いたいところでimportすれば、追加したパッケージを使うことができます。

<img width="503" alt="import" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/a5d69bff-e949-f70b-b854-b0d493d1c076.jpeg">

## 取得されたパッケージ

Project Navigatorで、Swift Package Dependenciesセクションにあるパッケージを選択すると、パッケージの内容を見ることができます。
また、パッケージを右クリックして［Show in Finder］をするとパッケージの実体をFinderで開くことができます。どうやらパッケージは、DerivedDataフォルダの下に取得されているようですね。

<img width="1095" alt="SwiftPackages Folder.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/56bf2945-587b-3fff-b6a2-24614e15cf23.jpeg">


なお、解決した各パッケージのバージョンをチームメンバー間で合わせるためのファイル Package.resolved は `プロジェクト名.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved` に作られています。
CocoaPodsで言うところのPodfile.lock、Carthageで言うところのCartfile.resolvedですね。

## パッケージに関する操作

［File］－［Swift Packages］の下にあるメニューから、パッケージに関する操作を行うことができます。

<img width="566" alt="SwiftPackages" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/993b9807-6008-5f9e-381f-fc9894055a91.jpeg">

### Reset Package Caches

取ってきたパッケージをいったん全部削除して、再び取得しなおします。
コマンドラインでSwiftPMを使うときの `swift package reset` に相当するものです。

### Resolve Package Versions

解決されたバージョンのパッケージを取ってきます。
コマンドラインでSwiftPMを使うときの `swift package resolve` に相当するものです。
CocoaPodsで言うところの `pod install` 、Carthageで言うところの `carthage bootstrap` のようなものだと思うのですが、Xcodeでプロジェクトを開くと自動的に行われているような気がするので、これを手動で実行する必要がいつあるのかはよくわかりませんでした:thinking_face:
Xcodeのプロジェクトを開いたまま、外部のgitツールを使ってcheckoutしたりpullした結果、Package.resolvedが更新されたときなどに強制的に再解決させるために使うんでしょうかね。

### Update to Latest Package Versions

各パッケージの最新のバージョンを使うように更新します。
コマンドラインでSwiftPMを使うときの `swift package update` に相当するものです。
CocoaPodsで言うところの `pod update` 、Carthageで言うところの `carthage update` のようなものだと考えていいでしょう。


# パッケージを作るには

Xcode 11ではメニューからパッケージを作ることもできるようになりました。［File］－［New］－［Swift Package］です。

<img width="566" alt="New Swift Package" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33397/d2285582-b9b2-c5c7-45dd-ad97f3d17167.jpeg">

実行すると、保存先のフォルダを聞かれます。ここで指定したフォルダ名がパッケージ名になって、パッケージの雛形が作られます。
Xcodeで作られるのは `swift package init` で作ったものと変わらない普通のパッケージです。プロジェクトファイル（.xcodeproj）も生成されません。それから、Package.swiftの内容をGUIで編集するような支援機能も特になさそうです。Package.swiftはSwiftで書かれたコードになっていて柔軟性が高いので、GUIで編集できたとしてもごく一部のみしか対応できないと思います。直接Package.swiftを編集するのがベストだと個人的には思っているので、特に不満はありません。

Xcode 11ではパッケージのフォルダを直接開いて開発することができるようになりました。
従来でも `swift package generate-xcodeproj` とすることで、ダミーのプロジェクトファイルを生成し、Xcodeを使ってパッケージを開発することができましたが、ソースファイルの増減があったときはプロジェクトファイルを再生成する必要があって面倒でした。
Xcode 11では直接パッケージのフォルダを開くことができるので、これからはダミーのプロジェクトファイルを生成する必要はありません。むしろ、あるとプロジェクトファイルの方を読みに行ってしまうので、すでに生成してしまっているものは削除しておいた方がいいでしょう。

パッケージに依存する別のパッケージがある場合は、Xcodeでパッケージフォルダを開いたときに自動的に依存先パッケージが取得されます。Package.swiftを編集したときも同様です。


# 注意：パッケージはリソースを持てない

このように今後の注目が期待されるSwiftPMですが、現時点のSwiftPMはコードだけが対象であることに注意が必要です。それ以外の、画像やStoryboardといったリソースをパッケージに含めることはできません。

ただ、リソースを持たせられるようにしようという議論がSwiftのforumで進行中です。将来は持てるようになるでしょう。
（と書きつつ、ぼくはこれらのスレッドをちゃんと追えていないのですが…）

- forumのスレッド
  - [Swift PM, Bundles and Resources](https://forums.swift.org/t/swift-pm-bundles-and-resources/13981)  (→ [proposal](https://github.com/abertelrud/swift-evolution/blob/package-manager-resources/proposals/NNNN-package-manager-resources.md))（2018/07/26〜）
  - [Draft Proposal: Package Resources](https://forums.swift.org/t/draft-proposal-package-resources/29941) (→ [proposal](https://github.com/aciidb0mb3r/swift-evolution/blob/package-resources/proposals/NNNN-package-manager-resources.md))（2019/10/19〜）
  - [SE-0271: Package Manager Resources](https://forums.swift.org/t/se-0271-package-manager-resources/30730) (→ [proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md))（2019/11/13〜）
  - [[Accepted with Modifications] SE-0271: Package Manager Resources](https://forums.swift.org/t/accepted-with-modifications-se-0271-package-manager-resources/31021)