raywenderlich.comブログ:「Swift Package Managerの紹介」(私家版・和訳版)
raywenderlich.comブログの「An Introduction to Swift Package Manager」(2019-04-01)の私家版和訳です。
先日ポストしたSwiftコマンド「swift package」の使い方みたいな記事ですね。
元記事の上部/下部にあるDownload Materialsボタンで、完成版ソースコード(Swift 4.2 / macOS 10.14 / Xcode 10)をダウンロードできるみたいです(ダウンロードには、フリー・アカウントでの登録が必要)。
以下、本文です。
この記事では、慣用句をランダムに表示するwebサイトを構築します。プロジェクトや依存関係の管理方法、Package.swiftファイルの読み方や変更方法を学びます。
サーバーサイドのSwiftプロジェクトでは、AppleのSwift Package Manager(SPM)を使用してプロジェクトの依存関係を管理でき、アプリケーションにライブラリを簡単にインポートできます。
このチュートリアルでは、慣用句をランダムに表示するwebサイトを構築します。プロジェクトと依存関係の管理方法、Package.swiftマニフェストファイルの読み方と変更方法を学びます。
SPMでdive into logisticsする準備はできました? 幸運を!
1. Packageを作成する
Packageは、1つ以上のライブラリと実行ファイルを含む単純なリポジトリです。あなたのアプリケーションには、依存関係を管理するためのPackage.swiftマニフェストファイルがあり、すべてのパッケージには、その依存関係を処理する独自のPackage.swiftマニフェストがあります。
マニフェストファイルも、ファイル拡張子が示すようにSwiftで書かれています!
マニフェストファイルはパッケージを記述します;依存関係とターゲットのリストが含まれています。
targetは、単一のモジュールとその依存関係の指定です。main.swiftを含む各ターゲットを実行ファイルにコンパイルできます。
Xcodeでmoduleを見たことがあるかもしれません。アプリ自体が実行可能なモジュールであり、依存関係があり、モジュールでもあります。
SPMを使用すると、Terminalから直接パッケージを作成できます。SPMは、アプリケーションを設定する際に、ディレクトリ名をプロジェクト名として使用します。
2. 始めに
まず、希望するプロジェクト名のディレクトリを作成し、そのディレクトリに移動します:
mkdir Website
cd Website
実行可能なプロジェクトを作成するには、以下のコマンドを実行します:
swift package init --type=executable
注:代わりにライブラリを作成したい場合は、typeパラメータを省略し、代わりに「swift package init」を使用します。
システムがSPMプロジェクトのすべてのボイラープレートを作成すると、以下のような出力が表示されます:
これでおしまい;あなただけのパッケージが完成しました!
3. 依存関係を追加する
プロジェクト作成の次ステップは、その依存関係を追加することです。これを行うには、プロジェクトを作成したディレクトリ内のPackage.swiftマニフェストファイルを開きます。
ファイルの先頭は、Swiftツールのバージョンです。生成されたツールのバージョンコメントは削除してはいけません;これはマニフェストの解釈方法を理解する為に、SPMによって読み取られます。
各パッケージには、名前とターゲットのリストがあります。実行ファイルには、通常のtargetと、test targetがあります。通常のターゲットは実行ファイルやライブラリで、テストターゲットはテストスイートです。
あなたのPackage.swiftファイルは、以下のようになっている筈です:
// swift-tools-version:4.2
import PackageDescription
let package = Package(
name: "Website",
dependencies: [],
targets: [
.target(
name: "Website",
dependencies: []
),
.testTarget(
name: "WebsiteTests",
dependencies: ["Website"]
),
]
)
このチュートリアルでは、アプリケーションの依存関係として、「WebsiteBuilder」ライブラリを追加します。このライブラリは、このチュートリアルのために特別に作成されたものです。
3.1. 依存関係URLを探す
依存関係を追加するには、3つの情報が必要です:URL、バージョン番号、名前です。
URLを取得するには、WebsiteBuilderライブラリへのGitHubリンクを開き、Clone or downloadという緑色のボタンを探します。そのボタンをクリックして、Clone with HTTPSを探します。これが表示されていない場合は、代わりにUse HTTPSというテキストが表示されている筈です。Use HTTPSをクリックすると、HTTPSリンクが表示され、クリップボードボタンを使ってコピーできます。
パッケージのバージョンは通常、プロジェクトのREADMEに記載されています。しかし、古いバージョンのパッケージが欲しい場合や、自分でバージョンを見つける必要がある場合は、プロジェクトのreleasesタブで見つけることができます。このプロジェクトでは、バージョン1.0.0から始めます。
パッケージ名はREADMEにあるかもしれませんが、依存関係のPackage.swiftファイルを見れば、いつでも見つけることができます。この依存関係の名前は「WebsiteBuilder」です。
アプリのPackage.swiftファイルには、依存関係の配列があります。そこには依存関係の例が1つありますが、参考の為だけのものなので、コメントアウトされています。
依存関係の配列内のマニフェストに、次の行を追加します。
.package(url: "https://github.com/raywenderlich/spm-tutorial.git", from: "1.0.0")
これは、GitHubリポジトリのURLとバージョン番号を参照します。
次に、「Website」ターゲットの名前付き依存関係リストに、パッケージ名…「WebsiteBuilder」…を追加します:
.target(name: "Website",dependencies: ["WebsiteBuilder"]),
ファイルを保存してTerminalに戻ります。これで依存関係が処理されます。しかし、依存関係にはプロパティもあり、次で説明します。
4. 依存関係プロパティを編集する
最初のSPM依存関係を最もシンプルな形で追加しましたが、アプリが必要とする依存関係のバージョンをもう少し具体的にしたい場合は、どうなるでしょう?
SPMでは、バージョン要件を非常に細かく指定でき、必要な依存関係の正確なバージョン、またはブランチさえも適切に制御できます。
注:これらは、様々なバージョンを指定する方法を示すための例です。これらの例では、切り捨てられたURLと架空のバージョン番号を使用しています。
以下のコードは、1.1.3以降、2.0.0以前のバージョンの依存関係を指定しています:
.package(url: "https://github.com/...git", from: "1.1.3")
一つの依存関係に対して最低と最大のバージョンを指定したい場合は、範囲を使用できます:
.package(url: "https://github.com/...git", "1.1.0"..."1.2.1")
依存関係の厳密バージョンに関心がある場合は、以下を使用します:
.package(url: "https://github.com/...git", .exact("1.2.3"))
これらのバージョン固有のバリエーションは、ベータ版もサポートしています。例えば:
.package(url: "https://github.com/...git", from: "1.1.3-beta.4")
依存関係をgitの特定のブランチにロックすることもできます。これは、機能や修正がまだリリースされていない場合に便利です:
.package(url: "https://github.com/...git", .branch("bugfix/issue-121"))
最後に、コミットをそのハッシュ値で、指定できます:
.package(url: "https://github.com/...git",
.revision("04136e97a73b826528dd077c3ebab07d9f8f48e2"))
5. Xcodeと統合する
これでプロジェクトの設定が完了したので、Xcodeプロジェクトを生成する準備ができました。以下のコマンドをTerminalで実行してください:
swift package generate-xcodeproj
これにより、依存関係がダウンロードされ、Xcodeプロジェクト-Website.xcodeproj-が作成されます。
注:以前にSwift Package Managerを使ったことがある人は、「swift build」の使用を要求していないことに気づくことでしょう。これは、Xcodeプロジェクト生成コマンドが、プロジェクトをビルドする前に依存関係ツリーを解決するだけでなく、これを行ってくれるからです。後ほど、「swift build」と「swift package update」が、どのように動作するかのヒントを得ることができます!
以下のような出力になる筈です:
最後に、プロジェクトを開きます。
Protip:以下のコマンドを実行すれば、Terminalからすぐに開くことができます:
open ./Website.xcodeproj
または、「xed」を使うと、現在のフォルダでプロジェクトを開くことができます:
xed .
XcodeはiOSデバイスやシミュレータを、ターゲットデバイスとして選択する場合があるので、必ずMy Macに切り替えてください。
Xcodeでプロジェクトをビルドして実行します。これでプロジェクトがコンパイルされ、プロジェクトが起動し、コンソールにメッセージが表示されて終了します。
Xcodeでmain.swiftを開き、その内容を以下のコードに置き換えます:
import NIO
import WebsiteBuilder
/// アプリが応答できるすべての慣用句
let idioms = [
"A blessing in disguise",
"Better late than never",
"Bite the bullet",
"Break a leg",
"Cutting corners",
"Get your act together",
"That's the last straw",
"You can say that again"
]
/// 上記のリストから選択されたランダムな慣用句で、リクエストに応答します
struct IdiomResponder: HTTPResponder {
func respond(to request: HTTPRequest) -> EventLoopFuture<HTTPResponse> {
guard let randomIdiom = idioms.randomElement() else {
return request.eventLoop.newFailedFuture(error: Error.noIdiomsAvailable)
}
let response = HTTPResponse(status: .ok, body: HTTPBody(text: randomIdiom))
return request.eventLoop.newSucceededFuture(result: response)
}
enum Error: Swift.Error {
/// 慣用句リストが空の場合、このエラーが発生します
case noIdiomsAvailable
}
}
/// IdiomResponderで応答する、新しいwebサイトを作成します
let website = Website(responder: IdiomResponder())
/// デフォルトのポートでwebサイトを起動します
try website.run()
このコードでは、SwiftNIOを使って、指定されたリストからランダムな慣用句で着信ネットワークに応答します。SwiftNIO自体は、このチュートリアルの対象外ですが、SwiftNIO:サーバーでの非同期の簡単なガイドで詳細を学べます。
XcodeのRunボタンを使って、アプリケーションをビルドして再度実行してみてください。しまった-うまくいきません:
Fatal error: Error raised at top level: bind(descriptor:ptr:bytes:) failed: Permission denied (errno: 13)
うおぉぉぉぉぉぉぉぉぉぉ! これは一体どんなバグやねん?
これは、このチュートリアルでの意図的なエラーであり、偽装の祝福である可能性もあります。これは、アプリケーションが、既に使用されているポート、または使用権限を持っていないポートを使用しようとしたことを意味します。この場合は、パーミッションの問題です。このライブラリはポート80を使用しますが、これはsuper userのみが使用可能です。
6. 依存関係を管理する
今回のプロジェクトでは、super userに切り替えないとアプリケーションを実行できなくなってしまいます。開発目的では、80以外のポートを使うのが一般的です。通常は1024番以上のポートで、まだ使用されていないものであれば、自由に使用できます。
通常、SPMがパッケージの新しいバージョンのアップデートをダウンロードできるように、「from」を使って最小バージョンを設定し依存関係を指定します。しかし、このシナリオでは、バージョン1.0.1にはポート・パーミッションの問題があり、最新の既知の動作バージョンは1.0.0です。1.0.0に戻す必要がありそうです。
Package.swiftを編集し、依存関係を以下のように置き換えて、依存関係を正確にバージョン1.0.0に固定します:
.package(url: "https://github.com/raywenderlich/spm-tutorial.git", .exact("1.0.0"))
マニフェストを変更した後、依存関係は自動的に更新されません。
依存関係を更新するには、Xcodeプロジェクトを再度更新「する」だけですが、今回はプロジェクトのディレクトリで、Terminalから以下のコマンドを実行してください:
swift package update
このコマンドは、単独で、依存関係の新しいバージョンや異なるバージョンを探し、必要に応じてダウンロードします。
Xcodeでこのプロジェクトを更新している為、このコマンドを実行した後にXcodeプロジェクトを再生成する必要があります。依存関係がファイルを追加したり削除したりできる為、これが必要です。
以下のコマンドを実行して、プロジェクトを再生成します:
swift package generate-xcodeproj
6.1. 依存関係の問題を解決する
Xcodeのバージョンによっては、以下のようなプロンプトが表示されます:
Revertをクリックして、ディスクからプロジェクトをリロードします。この後、Xcodeがアプリケーションをコンパイルしない場合は、ウィンドウを閉じてプロジェクトを開き直してください。
今回はXcodeでビルドして実行すると、コンソールに「Server started and listening」と表示されます。webブラウザを開き、「localhost:8080」にアクセスして、何度か更新しながら作ったものを確認してください。
これで、Xcodeを使ってアプリケーションを実行する方法がわかりました。ここまでは順調ですね!
次は、SPMと、Terminalから「swift」を実行する方法について説明します。実行中の場合はXcodeでアプリケーションを停止し、Terminalに戻します。
7. Terminalから実行する
これで、macOS上でアプリケーションをビルドして実行する方法がわかったので、開発を始める準備は万端です。しかし、サーバーサイドのSwiftは一般的にLinuxサーバー上で実行されます。その為、Linux上でアプリケーションをコンパイルして実行する方法も知っておく必要があります。
macOSでは、Xcode 10.1以降がインストールされていれば問題ありません。Linux環境で作業する場合は、Swiftダウンロード・ページに記載されてる様に、Swiftを設定する必要があります。
扱うコマンドは主に3つあります;最も一般的なコマンドは「swift run」です。これはアプリケーションをビルドし、その結果の実行ファイルを実行します。
実行可能なターゲットが複数ある場合は、コマンドにターゲット名を追加するだけです:
swift run Website
このコマンドを初めて実行する時、SPMは最初にすべてをコンパイルします。これには少し余分な時間がかかるかもしれませんが、その後のコンパイルではかなり速くコンパイルされます。「Server started and listening」というメッセージが表示されれば、サーバが起動していることがわかります。サーバーを停止するには、Control + Cを押します。
もう一つの一般的なタスクは単体テストを実行することです。今はこれを実行しないでください。次のコマンドは、パッケージの単体テストを実行するときに使うものです:
swift test
注:「swift test」は、サーバーをバックグラウンドで実行し、決して停止しない為、実行しないでください。サーバーサイドのSwiftプロジェクトをテストすることは、このチュートリアルの範囲外なので、このコマンドを知ることは重要ですが、プロジェクトはテスト用に設定されていません。
最後に、以下のコマンドを使用して、実行ファイルを実行せずにビルドします:
swift build
7.1. ビルド構成
上記のコマンドはデフォルトで「debug」設定を使用します。利用可能な設定は、他にもあります;最も一般的なものは「release」です。
「debug」設定は「release」よりもはるかに高速にコンパイルできますが、「release」設定でバイナリをコンパイルすると、最適化の度合いが高まります。
コマンドに「--configuration release」を追加して、設定を「release」に変更し、実行します:
swift run --configuration release
このコマンドは、サーバーを起動します。ブラウザでhttp://localhost:8080に移動します。慣用句が表示されれば、アプリケーションが動作していることになります!
とはいえ、まだ「今日はこれでアガリ」と言ってはいけません。まだまだ解決すべき問題があります!
8. ライブラリを編集する
多くの開発シナリオでは、別のアプリケーションがポート8080を使用しています。未使用の別ポートを割り当てることで、アプリケーションの1つを別のポートに移動させる必要があります。インポートされたライブラリはこれをサポートしていませんが、自分で実装できます!
まずはパッケージを編集可能なモードにします。Control + Cを押してサーバーを停止させます。次に以下のコマンドを実行します:
swift package edit WebsiteBuilder
これにより、WebsiteBuilderが、プロジェクトのDependenciesフォルダに移動します。SPMは依存関係を更新したり変更したりしません。パッケージを編集する前に、Xcodeプロジェクトを再生成してください。
Tip:Terminalでコマンドを素早く呼び出すには、Terminalで上矢印キーを何度もプレスします。「swift package generate-xcodeproj」の行に戻るまで行い、Returnを押して再実行してプロジェクトを再生成します。
XcodeのProjectナビゲータのDependenciesグループ内に、すべての依存関係ソースのリストがあります。WebsiteBuilderの依存関係フォルダ内のWebsite.swiftを開きます。
「run()」メソッドから、以下の行を削除します:
let port = 8080
また、「run()」メソッドのシグネチャを、以下のように変更します:
public func run(port: Int) throws {
今度はポートを指定する必要があります。自分のアプリケーションのmain.swiftファイルを開きます。そのファイルの最後の行(実際にwebサイトを起動している行)を変更し、ポート番号を変更します:
try website.run(port: 8123)
このチュートリアルを完成させるには、「release」設定を使って、Terminalで「swift run --configuration release」コマンドで、アプリケーションを実行します。 起動したら、ブラウザでhttp://localhost:8123のwebサイトを開きます。
慣用句が出てきましたか? だとしたら、もうおしまいです! 楽しんでいると時間があっという間に過ぎていきますよね?
9. ここからどこに行くのか?
- サーバーサイドのSwiftで最も重要なライブラリの1つであるSwiftNIOについては、このチュートリアルの頭でも触れたSwiftNIO tutorialで解説しています。
- API開発について学びたい方は、KituraとVaporを調べてみてください。Getting Started with Vapor及びGetting Started with Kituraは、基本を学べるチュートリアルです。
- Vapor vs. Kituraでは、2つのサーバーサイドのSwiftフレームワークが対決します。どちらを使うか迷っている方は、こちらを読んで判断してください!