Swiftにcpan相当のツールはあるのか?
モダンプログラミング言語は、単にプログラムを
だけではなく、
機能をライブラリ(library)にまとめ、
モジュール(module)として公開し
それを援用する
機能まで備えているものです。Perlのcpanを嚆矢( こうし ) に、Pythonならpip、Rubyならgem、ECMAScriptならnpm……ではSwiftは?
あります! Swift 3以降は。その名もSwiftPackage Manager。それ以前から同様の機能を提供するサードパーティー製のツールとしてはCarthage やCocoaPods が存在していましたが、Swift Package Managerはその名のとおりSwiftに標準装備され、すべての作業がSwiftで完結するという点で画期的です。まだまだ「老舗」ツールに比べると機能面で見劣りはしますが、標準装備というのはそれを上回る利点ですし、MacだけでなくLinuxでも同じように使えることで、とくにServer Side Swiftではこれが標準となっていくものと断言できるところまで来ました。
ライブラリを作る
ではさっそく、書いて覚えていきましょう。ここではFizzBuzzモジュールを作ってみます。Terminal.appから次のコマンドを実行します。
$ mkdir FizzBuzz
$ cd FizzBuzz
$ swift package init # --type=library
……(中略)……
Creating library package: FizzBuzz
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/FizzBuzz/FizzBuzz.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/FizzBuzzTests/
Creating Tests/FizzBuzzTests/FizzBuzzTests.swift
Creating Tests/FizzBuzzTests/XCTestManifests.swift
実はこの時点でビルドしてテストできる状態になっています。
$ swift build
で、
Compile Swift Module 'FizzBuzz' (1 sources)
……(中略)……
$ swift test
となり、図1 のようになります。
図1 ライブラリの作成
Compile Swift Module 'FizzBuzzTests' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/FizzBuzzPackageTests.xctest/Contents/MacOS/
FizzBuzzPackageTests
Test Suite 'All tests' started at 2018-05-21 07:23:10.304
Test Suite 'FizzBuzzPackageTests.xctest' started at 2018-05-21 07:23:10.305
Test Suite 'FizzBuzzTests' started at 2018-05-21 07:23:10.305
Test Case '-[FizzBuzzTests.FizzBuzzTests testExample]' started.
Test Case '-[FizzBuzzTests.FizzBuzzTests testExample]' passed (0.085 seconds).
Test Suite 'FizzBuzzTests' passed at 2018-05-21 07:23:10.390.
Executed 1 test, with 0 failures (0 unexpected) in 0.085 (0.085) seconds
Test Suite 'FizzBuzzPackageTests.xctest' passed at 2018-05-21 07:23:10.390.
Executed 1 test, with 0 failures (0 unexpected) in 0.085 (0.085) seconds
Test Suite 'All tests' passed at 2018-05-21 07:23:10.390.
Executed 1 test, with 0 failures (0 unexpected) in 0.085 (0.085) seconds
しかしこの状態ではとくに何もしていません。実際にFizzBuzzしてみましょう。
$ swift package generate-xcodeproj
とすると Xcode Project が生成されるので、
$ open FizzBuzz.xcodeproj
で開きます(図2 ) 。
図2 FizzBuzz.xcodeprojの実行
まずはモジュールを書き直しましょう。リスト1 のとおり上書きします。Int
にfizzbuzz
プロパティを追加するだけの簡単なモジュールです。
リスト1 Sources/FizzBuzz/FizzBuzz.swift
extension Int {
public var fizzbuzz:String {
let fb = ("Fizz", "Buzz")
switch (self % 3, self % 5) {
case (0, 0) : return fb.0 + fb.1
case (0, _) : return fb.0
case (_, 0) : return fb.1
default : return "\(self)"
}
}
}
テストも書き直しましょう(リスト2 ) 。
リスト2 Tests/FizzBuzzTests/FizzBuzzTests.swift
final class FizzBuzzTests: XCTestCase {
func test0() { XCTAssertEqual(0.fizzbuzz, "FizzBuzz") }
func test1() { XCTAssertEqual(1.fizzbuzz, "1") }
func test2() { XCTAssertEqual(2.fizzbuzz, "2") }
func test3() { XCTAssertEqual(3.fizzbuzz, "Fizz") }
func test4() { XCTAssertEqual(4.fizzbuzz, "4") }
func test5() { XCTAssertEqual(5.fizzbuzz, "Buzz") }
}
今度は、Xcodeからテストしなおしてみましょう。[ Command] -[ U]を押して、図3 のとおりに全テスト項目が緑になれば成功です。
図3 Testの実行結果
とりあえず最低限文化的なFizzBuzzモジュールができたので、Terminalに戻ってgitリポジトリを作成します。
% git init
% git add .
% git commit -am 'version 0.0.0'
% git tag 0.0.0
これで、公開可能 なモジュールが1つできあがりました。
ライブラリを使う――ローカル編
今度はそれをほかから使ってみましょう。
$ cd ../
でFizzBuzzディレクトリを抜けたら、今度はFizzBuzzRunというディレクトリを作成しそこにパッケージを生成します。
$ mkdir FizzBuzzRun
$ cd FizzBuzzRun
$ swift package init --type=executable
--type=executable
に注目。デフォルトだとlibrary
で、上記のとおりライブラリモジュールが作成されたのですが、今度はそのライブラリを使った実行ファイルを作成するというわけです。
ここで、生成されたPackage.swift
ファイルを編集します。これこそがSwift Package Managerの心臓部。空([]
)だったdependencies:
に、
dependencies: [
.package(url: "../FizzBuzz", from:"0.0.0")
]
を追加し、.target
の中に"FizzBuzz"
を追加します。
.target(
name: "FizzBuzzRun",
dependencies: ["FizzBuzz"]),
これで、main.swift
でimport FizzBuzz
する準備が整いました。
Sources/FizzBuzzRun/main.swiftをこう書き換えます。
import FizzBuzz
_ = (1...30).map{ print($0.fizzbuzz) }
あとはswift run
するだけです(図4 ) 。
図4 FizzBuzzの実行
% swift run
Fetching /Users/dankogai/Desktop/swift-38/FizzBuzz
Cloning /Users/dankogai/Desktop/swift-38/FizzBuzz
Resolving /Users/dankogai/Desktop/swift-38/FizzBuzz at 0.0.0
Compile Swift Module 'FizzBuzz' (1 sources)
Compile Swift Module 'FizzBuzzRun' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/FizzBuzzRun
1
2
Fizz
4
Buzz
……(中略)……
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
ライブラリを使う――リモート編
簡単ですね。しかし簡単とは言っても、単にFizzBuzzする程度であればswiftファイル1つあれば事足りますし、必要なことを、すべてそこに書いてもTerminalの画面1つに余裕で収まります。
ではこれをもとにFizzBuzzするWebサーバを作りたいとします。http://localhost:8181/0
ならFizzBuzz
、http://localhost:8181/1
なら1
……程度の簡単なサーバですが、FizzBuzzは簡単でもサーバはそうは行きません。どうしましょう?
Swift Package Managerはまさにそういうときのためにあるのです。まずはFizzBuzzRun
同様にFizzBuzzServer
という実行ファイルパッケージを生成します。
$ cd ..
$ mkdir FizzBuzzServer
$ cd FizzBuzzServer
$ swift package init --type=executable
そしてPackage.swift
を編集します。
.package(url: "../FizzBuzz", from: "0.0.0"),
.package(url: "https://github.com/
PerfectlySoft/Perfect-HTTPServer.git", from:"3.0.0"),
このhttps://github.com/PerfectlySoft/Perfect-HTTPServer.git
に、Webサーバを実装するのに必要な機能がすべてそろっているわけです。.target
のdependencies:
にもその旨を追加しておきましょう。
.target(
name: "FizzBuzzServer",
dependencies: ["FizzBuzz","PerfectHTTPServer"]),
あとはmain.swift
を適宜書き換えるだけです。あえて説明抜きで全体をリスト3 に示します。
リスト3 Sources/FizzBuzzServer/main.swift
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
import FizzBuzz
var routes = Routes()
routes.add(method: .get, uri: "/**") { q, r in
if let n = Int(q.path.dropFirst()) {
r.setBody(string: n.fizzbuzz)
} else {
r.status = .notFound
r.setBody(string: "\(q.path) : not found")
}
r.completed()
}
try! HTTPServer.launch(name: "localhost", port: 8181, routes: routes)
Ruby on RailsやPHPのLaravelを使っている人であれば、なんとなくやっていることはおわかりいただけるのではないでしょうか。では実行してみましょう。
swift run
[INFO] Starting HTTP server localhoston :::8181
Port 8181が待ち受け状態になりました。この状態で適当なブラウザでhttp://localhost:8181/0
にアクセスしてみましょう。FizzBuzz
と表示されましたか?
Swiftからもアクセスしてみましょう。URLにアクセスしてその中身を表示する程度であれば、Swift Package Managerに頼らずともリスト4 のような感じで書けます。
リスト4 zzbuzzclient.swift
import Foundation
for n in (1...30) {
let url = URL(string:"http://localhost:8181/\(n)")!
let str = try! String(contentsOf:url)
print(str)
}
そうそう。Webサーバを止めなければいけませんね。通常のシェルプログラム同様、[ Ctrl] -[ C」で止まります。
次回予告
というわけで駆け足でSwift Package Managerを使ってみました。{cpan,pip,gem,npm}のユーザであれば見よう見まねで使える気がしてきたのではないでしょうか? 次回のWWDC特集をはさんで、次々回以降ふただびSwift Package Managerを取り上げます。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)