Swiftでプロトコルバッファを使う

先日、Apple から [Swift Protobuf Plugin](https://github.com/apple/swift-protobuf) が公開されました。これは Swift でプロトコルバッファを使うためのツールです。
私はプロトコルバッファにそこまで詳しくないのでこれは良い機会だと思って試しに使ってみることにしました



####そもそもプロトコルバッファとは？

プロトコルバッファ(protobuf)とは、Googleが開発したオブジェクトシリアライズツールです。JSONのようにデバイス間でデータを送受信する際などに使えます。JSONと異なりパーズの必要はなく、バイト列から決め打ちでデータをとり出すので速い上にデータ量も非常に小さいです。

あらかじめデータコンテナ( protobuf では message という)の仕様を表す定義ファイル(.proto)を作成し、それを元に message クラスを自動生成してコンテナにアクセスします。データの構造をあらかじめ定義するため、JSON でよくあるキー値が変わってデータとれなくなるミスを未然に防ぐこともできます。

今回はそのクラスを自動生成するツールが Apple 公式で Swift に対応したので使ってみたというお話です。


#導入

https://github.com/apple/swift-protobuf
本家のREADME.mdにそって進めていきます


##必要なもの

- Swift Package Manager 込みの最新の Swift 3 コンパイラ。Xcode を最新にしていれば勝手に入ってると思います
- Google のプロトコルバッファ用コンパイラの protoc。[本家](https://github.com/google/protobuf)を参照して各自インストール
    - コンパイル済みの実行ファイルはhttps://github.com/google/protobuf/releases のprotoc-3.1.0-osx-x86_64.zip (Macの場合)から入手できます


##Swift用プラグインのインストール
プロトコルバッファのコンパイラ本体は protoc なのですが、Swift 用に出力するためのプラグインとして protoc-gen-swift を用意する必要があります


ソース落としてきてビルドします

```
$ git clone https://github.com/apple/swift-protobuf.git
$ cd swift-protobuf
$ swift build
```

実行したら`.build/debug`内に`protoc-gen-swift`が生成されていました。これにパスを通してターミナル上で実行できるようにします。

```
$ cp .build/debug/protoc-gen-swift ~/Documents
```

手っ取り早く`echo $PATH`で調べて適当な場所にコピーしましたがちょっと適当すぎるかも。（/usr/local/binなどお好きな場所に配置してください。protoc と一緒の場所においておけばいいと思います）


##.protoファイルの作成

READMEに書いてあるサンプルを見てmessageを作成してみます

```DataModel.proto
syntax = "proto3";

message BookInfo {
   int64 id = 1;
   string title = 2;
   string author = 3;
}

message MyLibrary {
   int64 id = 1;
   string name = 2;
   repeated BookInfo books = 3;
   map<string,string> keys = 4;
}
```

##.pb.swiftファイルの生成！
Swift 内で↑で作成した BookInfo 及び MyLibrary コンテナを利用できるようにするために、Swift 用のデータクラスを自動生成します

```
$ protoc --swift_out=＜出力先ディレクトリ＞ ＜入力ファイル＞`
```

```
$ protoc --swift_out=. DataModel.proto
$ ls
DataModel.pb.swift	DataModel.proto
```

わーい、`DataModel.pb.swift`が生成されました！


#自分の Xcode プロジェクトで使う
ここから Xcode を使って作業します。

まずは先程生成した .pb.swift ファイルをそのままいれる

![スクリーンショット 2016-09-27 22.50.00.png](https://qiita-image-store.s3.amazonaws.com/0/121299/8c942d32-96d2-3994-0466-c3f94d80caf5.png)

.pb.swift ファイル内では SwiftProtobuf モジュールを用いるため、こちらもインストールします。
リポジトリは上と同様の[apple/swift-protobuf](https://github.com/apple/swift-protobuf)です。普通のフレームワークを用いる場合と同様に導入してください。下に一例を示しますが、CocoaPods や Carthage についての説明は割愛します

- CocoaPodsの場合（CocoaPods v1.1以上が必要）

```
pod 'SwiftProtobuf', git: 'https://github.com/apple/swift-protobuf.git', :tag => '0.9.24'
```

- Carthageの場合

```
github "apple/swift-protobuf" "0.9.24"
```


これで、自分のプロジェクト内で protobuf の message が使えるようになります！
![スクリーンショット 2016-09-27 23.30.08.png](https://qiita-image-store.s3.amazonaws.com/0/121299/96a7f1d4-1dc5-b233-65e1-44d502df39eb.png)


###適当に動かしてみる

```Swift
do {
    
    let hoge: BookInfo = BookInfo(id: 100, title: "aa", author: "ii")
    let json: String   = try hoge.serializeAnyJSON()
    print("json: \(json)")
    
    let data: Data     = try hoge.serializeProtobuf()
    let fuga: BookInfo = try BookInfo(protobuf: data)
    print("id: \(fuga.id)  title: \(fuga.title) author: \(fuga.author)")
    
} catch {
    
}
```

```:Output
json: {"@type":"type.googleapis.com/BookInfo","id":"100","title":"aa","author":"ii"}
id: 100  title: aa author: ii
```

なんとなく動いてるっぽいですね。
`let data: Data = try hoge.serializeProtobuf()`
で取得してるやつが Data 構造体（旧NSDataクラス）なので、ファイルに書き込むなりパケットにのせるなり自由に扱えると思います


#おわりに
JSONより速いしデータは小さいしキー値が合わないことによるバグにすぐに気づくことができるしでいいことずくめです！

Apple の Swift 用プロトコルバッファプラグインは現在も開発中であり破壊的な変更が加えられることもあるため注意が必要です。何か変だと思ったら公式リポジトリのREADMEを眺めて最初からやりなおすなど試すと良いと思います