サーバーサイドとかいいつつ、今回はサーバーらしいことは何も書きません。単純にswiftで、SPM(パッケージマネージャ)を使って実行可能バイナリを作って実行してみるところまでです。
ちなみにswiftはXCodeを入れた時に入ったものを使いますが、コーディイングにXCode自体は使いません。私はAtomを使いました。Atomの場合は、 Preferences
-> Install
で swift
で検索して、 language-swift
と autocomplete-swift
を入れておくと幸せになれると思います。
インストール
Ubuntu
$ sudo apt-get install clang
Mac OSX
XCodeを入れて入ればすぐに使えます。
コマンドラインでSwiftを学ぶ
こんにちは世界!
とりあえずはお約束のHello World。
こんなソースを書く。
print("Hello World!")
実行する。
$ swift hello_world.swift
hello world!
ふむ。RubyやPythonみたいにインタプリタとして実行できました。
続いてコンパイルしてみます。
$ swiftc hello_world.swift
$ ls
hello_world
バイナリファイルが生成されました。実行してみます。
$ ./hello_world
hello world!
あたり前ですがインタプリタもバイナリも実行結果は同じ。
クラスを書いてみる
続いてクラスを書いて、そのクラスを別ファイルから使ってみます。
まずは、猫の名前を操作するための Neko
クラスを書きます。ファイル名は neko.swift
。
class Neko {
// 猫の名前
var myName = "Goroneko"
/**
* 猫の名前を返す.
*/
public func getName() -> String {
return myName
}
/**
* 猫の名前を登録する
*/
public func setName(_ name: String) {
myName = name
}
}
Swiftの単純なクラスです。 setName
メソッドで名前を登録し、 getName
メソッドで名前を取得します。
また、同じディレクトリ内にエントリポイントとなる main.swift
を書きます。
let neko = Neko()
print(neko.getName())
neko.setName("Tama")
print(neko.getName())
main.swift
内では、特にimportなどしなくてもいいようです。
コンパイルします。
$ swiftc *.swift
main
というファイルができますので、実行します。
$ ./main
Goroneko
Tama
期待通りの結果ですね。しかし、swiftcコマンドを使ってのビルドでは、なにかと面倒そうなので、パッケージマネージャを使ってみます。
SPM(Swift Package Manager)によるパッケージ化
HelloPackage
という名前のパッケージを作ります。
$ mkdir HelloPackage
$ cd HelloPackage/
$ swift package init --type executable
Creating executable package: HelloPackage
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/HelloPackage/main.swift
Creating Tests/
自動的に色々とファイルができました。 $ swift package init --type executable
で、 --type executable
を指定しているのは、こうすると実行可能ファイルを作るプロジェクトになるからです。逆にモジュールなど作る場合は何も指定しません。
まずは、なにも編集せずにこのまま実行します。
$ swift run
Compile Swift Module 'HelloPackage' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloPackage
Hello, world!
実行できました。
では、ここに新たにDogクラスを追加します。
まずは、プロジェクトのルートディレクトリから見て、 Sources/HelloPacakge/dog.swift
を作成します。
class Dog {
private var name = "Pochi"
func getName() -> String {
return name
}
func setName(_ newName: String) {
name = newName
}
}
単純なゲッターとセッターだけのクラスです。
さらに、 Sources/HelloPackage/main.swift
にもともと書いてあったソースを全て消して、以下のように書き換えます。
let dog = Dog()
let name01: String = dog.getName()
print(name01)
dog.setName("Tarou")
let name02: String = dog.getName()
print(name02)
では実行してみます。
$ swift run
Compile Swift Module 'HelloPackage' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloPackage
Pochi
Tarou
やりました。まったく期待通りの動きです。
先ほどはパッケージで直接実行しましたが、今度はビルドして実行可能バイナリを生成してみます。
$ swift build
Compile Swift Module 'HelloPackage' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloPackage
swift run
を実行した直後だとこのメッセージは表示されないかもしれません。
では生成されたバイナリを実行してみます。
$ ./.build/x86_64-apple-macosx10.10/debug/HelloPackage
Pochi
Tarou
SPMによる外部パッケージの取り込み
どんな言語を使ってもそうですが、全てを自分で実装するのではなく、共通の問題を解決してくれる他のプロジェクトの成果物を使いたくなる時があります。
たとえば、Rubyには bundler
というツールがあり、 Gemfile
に依存するライブラリを記述しておけばそれを自動的にダウンロードしてプロジェクトに組み込んでくれます。
同様の仕組みがSwiftにもあり、それが Swift Package Manager
です( ドキュメントはここ )。
では、先ほどの HelloPackage
に外部ライブラリを組み込んでみましょう。
HelloPackage
内にはすでに 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: "HelloPackage",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.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: "HelloPackage",
dependencies: []),
]
)
読みやすさのためにちょっと改行入れましたがこんな感じかと。
ここで、まあなんでも良いんですが、ためしにSwift向けの通信ソケットライブラリ BlueSocketを組み込んでみます。
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: "HelloPackage",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/IBM-Swift/BlueSocket.git", from: "1.0.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: "HelloPackage",
dependencies: ["Socket"]),
]
)
最初のポイントは、 dependencies: [ ... ]
の中に依存するパッケージを追加で記述しているところです。これ、Webで検索すると結構古い書き方がヒットしますが、ここでは自動生成された Package.swift
にご丁寧に書き方がコメントで書いてあるのでこれに従います。
.package(url: "https://github.com/IBM-Swift/BlueSocket.git", from: "1.0.0")
それと忘れてはいけないのが、 targets: [ .target( ... ) ]
のところで、このライブラリを使うターゲットの dependencies
内に、今回使用するライブラリの名称を文字列で指定しておくことです。私はこれに気づかなくて小一時間ハマりました...
targets: [
.target(
name: "HelloPackage",
dependencies: ["Socket"]),
]
BlueSocket
はプロジェクト名で、ライブラリ名としては Socket
となるようですので、これをdependenciesに追加します。
続いて、このライブラリを使用するソースファイルの先頭にimport文を記述します。今回は main.swift
にimport文を記述します。
import Socket
main.cf
全体としてはこんな感じになっていると思います。
import Socket
let dog = Dog()
let name01: String = dog.getName()
print(name01)
dog.setName("Tarou")
let name02: String = dog.getName()
print(name02)
折角インポートしたライブラリを全く使っていませんが、試しにこの状態でビルドしてみましょう。
$ swift build
すると自動的に先ほどの BlueSocket
がダウンロードされてビルドが実施されます。
では出来上がった実行ファイルを実行してみます。
$ ./.build/x86_64-apple-macosx10.10/debug/HelloPackage
Pochi
Tarou
ライブラリは使ってませんが、ビルドは成功しました。