Swift Advent Calendar 2015 で勉強してみる(6日目)

Swift プログラミング

Swift Advent Calendar 2015 が暖かい感じに盛り上がっていて好きだったので、自分もこれを眺めながら Swift をあれこれ勉強してみることにしました。

その6日目です。

Swift Advent Calendar 2015 が暖かい感じに盛り上がっていて好きだったのと、話題自体も広かったり深かったりして興味深くて、眺めていたら勉強したい気分が高まってきました。

そこで、開催されている4つの Swift Advent Calendar の内容を拝見して、そこからいろいろ浮かんだことを綴ってみようと思います。

Swift Advent Calendar

Swift Advent Calendar の6日目は takebayashiさんSwift で Sinatra っぽく Web アプリケーションを書いて Linux で動かす というお話でした。

アプローチとしては OS X でも Linux でも動く簡単な Web アプリケーションを Swift で作ってみた という内容みたいです。

Sinatra

そもそも Sinatra ってなんだろう?と思ったんですけど、どうやら関数を書くだけで、簡易 Web サーバーが立ち上がってリクエストを捌けるようになる仕組みのようでした。

そう聞いて Linux の xinetd みたいなものを想像したんですけど、どうやらもっと簡単で、担当する URI を引数で受け取って文字列を返すコードを作って実行するだけで簡易 Web サーバーが待機状態になるところまで行ってくれる様子でした。

Swiftra

そして、その Sinatra をモチーフにして takebayashi/swiftra というライブラリを作成されたそうです。

この README を見ると実感できるのですけど、とてもシンプルにコードが書けるようになっていて、これだけで自由な Web サーバーが動かせるというのが素敵です。


これが OS X でも Linux でも動かせるとあれば、ちょっとした Web サーバーが何かと必要になる人にとってはとても嬉しいものになりそうですね。

願わくば Apple TV とかの On-Demand Resources テストとかにも使えるように ATS に対応してくれたりすると嬉しさがいっそう増しそうです、なんて少し無茶振りしてみたりして。

さておき、この Swiftra で使っている takebayashi/http4swift にも個人的にちょっと興味をそそられました。

Swift Package Manager

ところで、この Advent Calendar 記事内の Swiftra を利用して Web アプリケーションを作成するSwiftra を Swift Package Manager を使って導入する 話が綴られています。

Swift Package Manager といえば Swift がオープンソースになったときに一緒に登場したもので Swift コードをパッケージとして頒布できる ものという噂は聞いたのですけど、それ以上のことを知らなくて分からない部分が多かったので Advent Calendar 記事を参考にしながら自分なりにも調べたことを綴ってみることにします。

依存定義の追加行

Advent Calendar 記事を読んでいて、記載されていた 下記のように依存定義を1行追加するだけで というところが、最初はよくわかりませんでした。

これはどうやら、まず Swift Package Manager 対応のパッケージを使いたいときに、それを使う Swift コードも Swift Package Manager を想定したフォルダー構造にして、その中に Package.swift を用意して、内容を記載することになる様子でした。

そしてそのとき、それが使うパッケージを次のように dependencies で指定することになるようです。

dependencies: [
	.Package(url: "https://github.com/takebayashi/swiftra.git", majorVersion: 0)
]

実際に試してみる

Swift Package Manager にしても Swiftra にしても、複雑な手順は何も求められない印象ですし、実際に動かしてみたら実感が湧くだろうと思って試してみることにしました。

嬉しいことにすぐに試せる Swiftra サンプルコード を用意してくれていたので、それをビルドしようとしたのですけど、次のようなエラーで進めなくなってしまいました。

<unknown>:0: error: no such file or directory: 'build'

この問題自体は単純で Xcode 7.2 に付属の Swift 2.1.1 が Swift Package Manager に対応していないというだけだった様子です。

swift-build

ただ、オープンソースの apple/swift を手順に従ってビルドして、それを使うようにしても、次のようなエラーになって上手くいきませんでした。

error: unable to invoke subcommand: ./build/Ninja-DebugAssert/swift-macosx-x86_64/bin/swift-build (No such file or directory)

Swift Package Manager を使うためには、どうやら Swift.org - Download Swift で公開されている "Swift Open Source Xcode Toolchain" パッケージをインストールする必要がある様子でした。

Swift パッケージのビルド

これをインストールすると、関連するファイルが /Library/Developer/Toolchains/swift-latest.xctoolchain にインストールされました。

ここの usr/bin に入っている swift を使うと、同じくそこに収められている swift-build を使って Swift パッケージをビルドすることができました。

/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin/swift build

先ほどの Swiftra のサンプルコード内に用意された README に従いつつ swift build のところだけ上記のように実行したら、次のような表示がされて、無事にビルドが通った様子です。

Cloning Packages/swiftra
Cloning Packages/http4swift
Compiling Swift Module 'http4swift' (6 sources)
Linking Library:  .build/debug/http4swift.a
Compiling Swift Module 'swiftra' (5 sources)
Linking Library:  .build/debug/swiftra.a
Compiling Swift Module 'swiftraexample' (1 sources)
Linking Executable:  .build/debug/swiftra-example

この出力から窺えるように、swift build を実行したフォルダーにある Package.swift に記載されている依存パッケージが、そのさらに依存するものも含めて自動的にダウンロードされて、それらが静的ライブラリとしてビルドされ、そして最後に、カレントフォルダーの Package.swift で指定されているパッケージ名で実行ファイルが生成された様子でした。

生成された実行ファイルはドットで始まる名前の .build フォルダーに入れられるので、ターミナルで普通に ls を実行しても表示されなかったりしますけど ls -a で確認すると見つかります。

Swiftra サンプルアプリを実行してみる

ここまで出来たら、あとは Advent Calendar 記事内の Swiftra アプリケーションを実行する のとおりに試してみると、記載のとおり、しっかりとサンプルコードが動いてくれました。

やってみると、その手軽さがなおさら実感できますね。個人的には Swift Package Manager 周りの学習も出来てさらに良かったです。

Swift その2 Advent Calendar 2015

Swift その2 Advent Calendar 2015 の6日目は syamaokaさんSwift で HTML を構文解析して、UITableView にマッピングする方法 というお話でした。

具体的には mattt/Ono というライブラリを使って HTML を解析し、特定のタグごとにテーブルビューセルを作って表示するということが実現されています。

所感

この Advent Calendar 記事内では、そんな mattt/Ono ライブラリを使って実際にそれを実現する上での要所がピンポイントに紹介されていて、このライブラリの基本的なところを学んだ後の、実際にどう使ったらいいかみたいなところを手早く垣間見れるように構成されていました。

実際のソースコード全体は bpyamasinn/MappingToTableView に公開されているので、この Advent Calendar で要所を掴めば、コード全体を読み進めるのもずいぶん楽な印象でした。

個人的に XML の解析周りに興味があるところだったのですけど、この mattt/Ono も XML に対応しているみたいなので、自分もちょっとこのライブラリを気にしてみたくなりました。

列挙型の String な Raw 値について

ところで完全に余談なのですけど、Swift の列挙型で Raw 値を String 型にした場合、たしか Swift 2 あたりから、特に何も明記しないと列挙子と同じ文字列が設定されるようになりました。

そのため Advent Calendar 記事内の PermitTag 列挙型の定義を次のように書き換えても、今のコードと全く同じに動作します。

enum PermitTag: String {

	case none = ""
	case p
	case h1
	case h2
	case h3
}

上記の例の none のように、列挙子と違う Raw 文字列を当てたい時だけそれを明記すればいいので、今回のサンプルコードでサポートしたいタグがたくさん増えたときには記載がずいぶん楽になるはずです。

Swift(一人) Advent Calendar 2015

Swift(一人) Advent Calendar 2015 は全日を shimesabaさん が埋め尽くすという素晴らしさですけれど、その6日目は class と static の挙動の違いを整理する というお話でした。

この話!自分はついつい、なんとなく違いを想像して済ませていたところだったので、とても嬉しいテーマです。そのため、把握しきれていな部分がたくさん思い当たるので、今回の shimesabaさん の話を頼りに学習して、そこから思い描かれることを自分も追記してみたいと思います。

継承関係

まず、今回の Advent Calendar を見て、記事内の まとめ にあるとおりの認識であっててホッとしました。

static が静的(つまり不動のもの)で class がクラス継承を意識したものと捉えれば、オーバーライドの可否と適用できる型については自然に捉えられそうです。

class var が使えないことについては、エラーメッセージが class stored properties not yet supported in classes であることを見ると 何故できないのか とかを考える以前に とりあえずできないことになっている と捉えておくのが良さそうです。

static var については、型そのものに備わるプロパティとして、構造体に対するそれと同じに捉えておけばきっと大丈夫でしょう。

余談:保持型プロパティの継承について

ところで完全に余談ですけど、クラスに実装した普通の var で実装した保持型プロパティは、そのクラスを継承したサブクラスで、保持型プロパティとしてオーバーライドすることはできなくなっています。

同じ名前のプロパティでオーバーライドしたいときには、サブクラスでは計算型プロパティとしてオーバーライドすることになります。

class MyClass {
    
    var value: Int = 0
}

class SubClass : MyClass {

	override var value: Int {

		get {

			return super.value + 1
		}

		set {

			super.value - 1
		}
	}
}

もし、保持型の class var が使えるようになったとしたら、きっとこんな感覚で使えるようになるのかなって想像しつつ。

続きの考察

ここからは Advent Calendar 記事で培った知識を踏まえて、そこから思い浮かんだ疑問についていろいろ試してみることにします。

メタタイプ型での利用について

型情報を変数に取得して、そこから呼び出す場合に staticclass に違いが出てきたりするのかな?と思って調べてみました。

要は こちら で紹介した required init みたいな感覚になるのかな?という話です。


結果的には、どちらとも同じように扱うことができました。

class MyClass {
    
    static var staticValue: Int = 1
    class var classValue: Int { return -1 }
}

class MySubClass : MyClass {

	override static var classValue: Int {

		get {

			return super.classValue * 2
		}
	}
}

MyClass.staticValue
MyClass.classValue

let type = MyClass.self

type.staticValue    // OK (1)
type.classValue    // OK (-1)

let subtype = MySubClass.self

subtype.staticValue    // OK (1)
subtype.classValue    // OK (-2)

サブクラスを親のメタタイプ型から扱う場合

次に、サブクラスの型情報を親の型で扱った時に static var がどういう扱いになるかが気になってきます。

そこで、先ほどのクラス定義はそのまま、次のテストコードを追加してみたところ、先ほどと同じ実行結果になりました。メタタイプは継承関係もちゃんと把握してくれている様子です。

let basetype: MyClass.Type = MySubClass.self

basetype.staticValue    // OK (1)
basetype.classValue    // OK (-2)

static var は final class var ということ?

そんな検証をしていたところ、試しに static var を継承先でオーバーライドしてみようとしたところで、次のような興味深いエラーメッセージが表示されるのに気がつきました。

error: class var overrides a 'final' class var

具体的には、次のようなコードを書いてこのエラーが発生しました。

class MyClass {
    
    static var staticValue: Int { return 1 }
}

class SubClass : MyClass {

	// error: class var overrides a 'final' class var
	override static var staticValue: Int {

		return super.staticValue * 2
	}
}

つまり、内部的には static varfinal class var として扱われている可能性が窺えます。


そこで、次のように static varfinal class var に書き換えてみたところ、継承先のオーバーライドでエラーが出るのは想像がつきますけど、その時のエラーが先ほどの static var の時と全く同じになりました。

class MyClass {
    
    final class var staticValue: Int { return 1 }
}

class SubClass : MyClass {

	// error: class var overrides a 'final' class var
	override class var staticValue: Int {

		return super.staticValue * 2
	}
}

じゃあ、もし内部で final class var とみなされているとすると 親クラスでは final を付けない class var で定義して、サブクラスで static var でオーバーライドしてみるとどうなるんだろう? と思ってやってみると、エラーなくコンパイルが通りました。

class MyClass {
    
    class var staticValue: Int { return 1 }
}

class SubClass : MyClass {

	override static var staticValue: Int {

		return super.staticValue * 2
	}
}

さらにそれを継承した先で static var を継承することはできない感じで、このことからも内部的な扱いは static varfinal class var と等価 という見方をしていて大丈夫そうな感じがしました。

そんな捉え方をしてみると、具体的な好例はまだ思い浮かばないものの static varclass var の使い分けはイメージできるようになるかもしれませんね。もっとも、観察から得られた class varfinal class var という差でこれらの違いを捉えた時に、それが必ずしも理屈的に適切かどうかは分かりませんけど。

プロトコルでの class var の扱い

プロトコルには static var だけ定義できて class var は定義できなくなっています。

protocol MyProtocol {

	static var staticValue: Int { get }    // OK
	class var classValue: Int { get }    // NG
}

この性質は、プロトコルを AnyObject から継承したり、プロトコルに @objc ディレクティブを付けても変わりません。


このとき素直に次のようにして、クラス型の MyClassMyProtocol に準拠することができます。

protocol MyProtocol {

	static var staticValue: Int { get }
}

class MyClass : MyProtocol {

	static var staticValue: Int { return 7 }
}

そしてここで static var のところを class var に書き換えてみても、問題なくビルドすることができました。

class MyClass : MyProtocol {

	class var staticValue: Int { return 7 }
}

もちろん素直な class var なので継承することも可能です。そしてそれを特定のプロトコル型として扱った時でも、ちゃんと継承関係を意識した値が得られる様子でした。

protocol MyProtocol {

	static var staticValue: Int { get }
}

class MyClass : MyProtocol {

	static var staticValue: Int { return 7 }
}

class MySubClass : MyClass {

	static var staticValue: Int { return super.staticValue * -1 }
}

let type: MyProtocol.Type = MySubClass.self

type.staticValue    // -7

所感

以上の感じから static varclass var の特徴的なところをいったんおさえてしまえば、あとは特に例外もなく安定して扱えそうな印象でした。

構造体などの値型とクラス型との間で生まれる差も class varfinal class var として扱えるところを意識すると、ちょうど こちらSelf のところで紹介した final 付きのクラスが構造体と同等に扱える(将来の継承を気にしたコードを書かなくて良くなる)のと同じ感じになるのも、しっくりくるような印象でした。