Edited at

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

More than 1 year has passed since last update.

先日、Apple から Swift Protobuf Plugin が公開されました。これは 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。本家を参照して各自インストール




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

.pb.swift ファイル内では SwiftProtobuf モジュールを用いるため、こちらもインストールします。

リポジトリは上と同様の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


適当に動かしてみる

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を眺めて最初からやりなおすなど試すと良いと思います