RailsとiOSアプリのAPIにProtocol bufferを導入した
導入のモチベーション
- 型安全にしたい
- 表示速度を向上させたい
- ドキュメント管理を簡単にしたい
- 部分的には緩和されたが、Qiitaの記事とかにも挙がってるようにoptionalの表現が悩ましいので、コメントも併記する運用になりそう。
環境
導入手順
protoファイル
多くのプロジェクトでは、専用のレポジトリで管理することになると思う。
ディレクトリ構成
products/product_detail_message.proto
のようにentityの種類毎に区切った。
※ 注意点
- ディレクトリが別であっても、message名はユニークに命名すること。
- 自動生成されるクラスの都合上、名前空間みたいなものが考慮されないので、クラス名がユニークでないといけない模様。
- もしかしたら回避できるオプションがあるかもしれないが、調べたところ引っかからなかった。
- messageのkey名に
description
を指定できない。- Swift側で提供される関数に競合してしまうみたいだった。
サーバ
ライブラリ
これがググってもあまり引っかからなくて、なかなかハマった。。
# Gemfile gem 'grpc' # 1.17.0で動作確認 gem 'grpc-tools' # 1.17.0で動作確認
ruby_protobuf
を使う記事等があったが、上述のgem以外だと proto3の形式に対応していないみたいだった。
proto2の形式なら、他のgemで大丈夫そう。
実装
1 . protoファイルから対応するrbファイルを作成
# このあたりの構成はプロジェクト次第 mkdir app/messages bundle exec grpc_tools_ruby_protoc -I ./../protobuf --ruby_out=./app/messages products/product_detail_message.proto
※ ↑の構成だと、ファイル名と自動生成されるクラス名が異なったりして、読み込み周りの解決が色々大変だった。
2 . decoratorやpresenterで適切に加工して返す
# app/controllers/products_controller.rb def show product = Product.find(params[:id]) message = ProductDetailMessage.new(id: product.id, name: product.name) render plain: message.to_proto end
キャッシュする場合は、seriarizedした結果をキャッシュした方が良さそうだった。
def product_message(product) serialized_proto = cached_proto(product) message = ProductDetailMessage.decode(serialized_proto) message.is_bookmarked = @current_user.bookmarked?(product) message end def cached_proto(product) Rails.cache.fetch('iikanji_no_key', expires_in: 5.minutes) do ProductDetailMessage.new(id: product.id, name: product.name).to_proto end end
jsonと併用する場合
1 . mime_typeを登録
# config/initializers/mime_types.rb Mime::Type.register 'application/protobuf', :protobuf
2 . controllerで制御
# app/controllers/products_controller.rb def show product = Product.find(params[:id]) message = ProductDetailMessage.new(id: product.id, name: product.name) respond_to do |format| format.json { render json: message.to_h } format.protobuf { render plain: message.to_proto } end end
アプリ
インストール
1 . protobufのインストール
brew install protobuf
2 . protoc-gen-swiftのインストール
git clone https://github.com/apple/swift-protobuf cd swift-protobuf # Cartfileと同じバージョンを指定する git checkout 1.3.1 swift build # いい感じにPATHを通す # 以下は例 cp .build/debug/protoc-gen-swift /usr/local/bin/
3 . iOSのプロジェクトにライブラリ追加
carthageやcocoapodsで以下のライブラリを追加
GitHub - apple/swift-protobuf: Plugin and runtime library for using protobuf with Swift
実装
1 . 最新のprotoファイルから対応するswiftファイルを作成
protoc -I ../protobuf --swift_out=./project_name/proto products/product_detail_message.proto
2 . 生成されたswiftファイルをプロジェクトに追加
3 . APIリクエストのヘッダに application/protobuf
を指定してリクエスト
4 . APIレスポンスを自動生成されたmessageのclassでパース