Edited at

サーバーサイドSwiftを学ぶ(基礎の基礎の基礎編)

More than 1 year has passed since last update.

サーバーサイドとかいいつつ、今回はサーバーらしいことは何も書きません。単純にswiftで、SPM(パッケージマネージャ)を使って実行可能バイナリを作って実行してみるところまでです。

ちなみにswiftはXCodeを入れた時に入ったものを使いますが、コーディイングにXCode自体は使いません。私はAtomを使いました。Atomの場合は、 Preferences -> Installswift で検索して、 language-swiftautocomplete-swift を入れておくと幸せになれると思います。


インストール


Ubuntu

$ sudo apt-get install clang


Mac OSX

XCodeを入れて入ればすぐに使えます。


コマンドラインでSwiftを学ぶ


こんにちは世界!

とりあえずはお約束のHello World。

こんなソースを書く。


hello_world.swift

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


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 を書きます。


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 を作成します。


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 にもともと書いてあったソースを全て消して、以下のように書き換えます。


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 というファイルが存在していると思います。この時点での内容はたぶんこんな感じ。


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 はこんな感じになります。


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.swift

.package(url: "https://github.com/IBM-Swift/BlueSocket.git", from: "1.0.0")


それと忘れてはいけないのが、 targets: [ .target( ... ) ] のところで、このライブラリを使うターゲットの dependencies 内に、今回使用するライブラリの名称を文字列で指定しておくことです。私はこれに気づかなくて小一時間ハマりました...


Package.swift

    targets: [

.target(
name: "HelloPackage",
dependencies: ["Socket"]),
]

BlueSocketはプロジェクト名で、ライブラリ名としては Socket となるようですので、これをdependenciesに追加します。

続いて、このライブラリを使用するソースファイルの先頭にimport文を記述します。今回は main.swift にimport文を記述します。


main.swift

import Socket


main.cf 全体としてはこんな感じになっていると思います。


main.swift

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

ライブラリは使ってませんが、ビルドは成功しました。