【SwiftPM】Swift Package Manager 4.0でCLIアプリケーションを作る

# Swift Package Managerとは

Swift製のパッケージマネージャでSwiftPMと呼ばれたりします。
CocoaPodsやCarthageとは違ってCLIアプリケーションやサーバサイドSwiftで利用されることが多い状況です。
バージョン3の情報はある程度増えてきてはいますが、現在の最新バージョンである**4.0.0**の情報はまだまだ少ないので、今回はSwiftPM 4を使ってCLIをツールを作ってみたいと思います。

# 何を作るか

こんな感じであいさつするやつを作りたいと思います。

```
$ hello-swift-pm
Hello, SwiftPM!
$ hello-swift-pm --name Tom
Hello, Tom!
$ hello-swift-pm --name Tom --time morning
Good morning, Tom!
$ hello-swift-pm --time evening
Good evening, SwiftPM!
```

# やっていき

## バージョンの確認

SwiftとSwiftPMのバージョンは次のような感じです。

```
$ swift --version
Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
$ swift package --version
Apple Swift Package Manager - Swift 4.0.0-dev (swiftpm-13752)
```

## プロジェクトの新規作成

今回は`hello-swift-pm`というディレクトリに作っていきます。

```
$ mkdir hello-swift-pm
$ cd hello-swift-pm
$ swift package init --type executable
$ tree
.
├── Package.swift
├── README.md
├── Sources
│   └── hello-swift-pm
│       └── main.swift
└── Tests

3 directories, 3 files
```

## ビルドしてみる

デフォルトで生成されている`Hello, world!`アプリをビルドしてみましょう。

```
$ swift build
$ ./.build/debug/hello-swift-pm 
Hello, world!
```

## Xcodeプロジェクトの生成

コードの編集はXcodeでやりたいってことでXcodeプロジェクトを生成します。
しかし、これはコードからいい感じにXcodeプロジェクトを生成しているだけなので、Xcodeからはコードの編集のみを行うようにしてください。
ファイルやディレクトリを追加する場合はコマンドラインから作成し、Xcodeプロジェクトを再度生成したほうが良いです。

```
$ swift package generate-xcodeproj
$ open hello-swift-pm.xcodeproj/
```

## 外部依存ライブラリの指定

今回は[`Commander`](https://github.com/kylef/Commander)というライブラリを用いてCLIアプリケーションを作りたいと思います。
次のような変更を加えて依存パッケージをビルドします。

```diff:Package.swift
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "hello-swift-pm",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
+       .package(url: "https://github.com/kylef/Commander.git", from: "0.8.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "hello-swift-pm",
-           dependencies: []),
+           dependencies: ["Commander"]), // ここはimportするときのパッケージ名を書く
    ]
)

```

```
$ swift package update
Fetching https://github.com/kylef/Commander
Fetching https://github.com/kylef/Spectre.git
Cloning https://github.com/kylef/Spectre.git
Resolving https://github.com/kylef/Spectre.git at 0.8.0
Cloning https://github.com/kylef/Commander
Resolving https://github.com/kylef/Commander at 0.8.0
```

**ちなみに、`targets`の中の`dependencies`に書く名前は、リポジトリ名ではなく、`import`するときのパッケージ名を書く必要があることに注意してください。
ライブラリ名とパッケージ名は同じことが多いのでハマりポイントだと思います。
(例: `SwiftYaml` -> `Yaml` ( https://github.com/behrang/YamlSwift ))**

## `main.swift`の実装

CLIアプリケーションのメイン部分を作っていきましょう。
`Commander`が`import`できないときはXcodeプロジェクトを再生成してみると直るかも。

```swift:main.swift
import Commander

enum Greeting {
    enum ParseError: Error, CustomStringConvertible {
        case invalidTime

        var description: String {
            switch self {
            case .invalidTime:
                return "Invalid time option"
            }
        }
    }

    static func validator(time: String) throws -> String {
        switch time {
        case "morning": return "Good morning, "
        case "evening": return "Good evening, "
        default: throw ParseError.invalidTime
        }
    }
}

let main = command(
    Option("name", default: "SwiftPM"),
    Option("time", default: "daytime", description: "morning or evening", validator: Greeting.validator)
) { name, time in
    let greeting = time == "daytime" ? "Hello, " : time
    print(greeting + name + "!")
}

main.run()
```

## 最後にリリースビルドして完了

```
$ swift build -c release
$ ./.build/release/hello-swift-pm --name Tom --time morning
Good morning, Tom!
```

## 完成品

完成品のコードは[GitHub](https://github.com/snowman-mh/hello-swift-pm)に置いています。

# おわりに

今回はSwift Package Manager 4.0を使ってCLIアプリケーションを作ってみました。
最後まで読んでいただきありがとうございました。
今回はあまり難しいことはしていませんが、`Package.swift`の書き方がバージョン3から少し変更されていてしっかり最新情報を追っていないとハマりそうだと思いました。
間違いや追加情報などあればコメントしていただけると嬉しいです。