About
iOSアプリからgRPCのServiceを呼び出す実験をしてみようと思ったのですが、CocoaPodsを利用した導入についての情報が意外とまとまっていなかったので、その流れをまとめておきます。
Protocol Buffersの定義から、Clientライブラリが自動的に生成される様子をお楽しみ下さい。
前提
- gRPC自体の説明は割愛します。gRPC公式サイトなどを参照して下さい。
- Xcode 7.3.1
- CocoaPods 1.0.1
- gRPC 1.0.0
- サンプルコード
プロジェクト名をGrpcMobileDemo
とし、以下のような構成からスタートすることを想定します。
.
├── GrpcMobileDemo
├── GrpcMobileDemo.xcodeproj
├── GrpcMobileDemo.xcworkspace
├── Podfile
└── Pods
流れ
1. ローカルにgRPC ClientのPodを定義する
仕組みは後述するとして、gRPC Serviceに対するClientとして機能するPodを、ローカルに定義します。
-
RemoteClient
という名前でディレクトリを作成し、podspec
とgRPCのサービスが定義されたproto
を配置します。- ここでは
RemoteClient
という名前にしましたが、名前は自由です。衝突しないお好きな名前をどうぞ。
- ここでは
.
├── GrpcMobileDemo
├── GrpcMobileDemo.xcodeproj
├── GrpcMobileDemo.xcworkspace
├── Podfile
├── Pods
└── RemoteClient
├── RemoteClient.podspec
└── helloworld.proto
RemoteClient.podspec
は次のような内容です。authors
やhomepage
などは、ご自身の値を設定して下さい。
Pod::Spec.new do |s|
s.name = 'RemoteClient'
s.version = '0.0.1'
s.license = 'MIT'
s.authors = { '<your user name>' => '<your email address>' }
s.homepage = '<your homepage>'
s.summary = 'gRPC test client'
s.source = { :git => '<your git repository>' }
s.ios.deployment_target = '7.1'
s.osx.deployment_target = '10.9'
# Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients.
s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0.0"
# Pods directory corresponding to this app's Podfile, relative to the location of this podspec.
pods_root = '../Pods'
# Path where Cocoapods downloads protoc and the gRPC plugin.
protoc_dir = "#{pods_root}/!ProtoCompiler"
protoc = "#{protoc_dir}/protoc"
plugin = "#{pods_root}/!ProtoCompiler-gRPCPlugin/grpc_objective_c_plugin"
s.prepare_command = <<-CMD
#{protoc} \
--plugin=protoc-gen-grpc=#{plugin} \
--objc_out=. \
--grpc_out=. \
-I . \
-I #{protoc_dir} \
*.proto
CMD
s.subspec 'Messages' do |ms|
ms.source_files = '*.pbobjc.{h,m}'
ms.header_mappings_dir = '.'
ms.requires_arc = false
ms.dependency 'Protobuf'
# This is needed by all pods that depend on Protobuf:
ms.pod_target_xcconfig = {
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1',
}
end
s.subspec 'Services' do |ss|
ss.source_files = '*.pbrpc.{h,m}'
ss.header_mappings_dir = '.'
ss.requires_arc = true
ss.dependency 'gRPC-ProtoRPC'
ss.dependency "#{s.name}/Messages"
end
end
gRPCのサービスとして、gRPCのサンプルにあるhelloworld.protoを写経したものを配置してみました。
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.hosopy.grpcdemo.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
2. 定義したPodをPodfileで参照する
先ほど定義したローカルのPodを、Podfile
で参照します。
target 'GrpcMobileDemo' do
# Comment this line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for GrpcMobileDemo
pod 'RemoteClient', path: './RemoteClient'
target 'GrpcMobileDemoTests' do
inherit! :search_paths
# Pods for testing
end
target 'GrpcMobileDemoUITests' do
inherit! :search_paths
# Pods for testing
end
end
3. pod install
あとはpod install
するだけで完了です。
$ pod install
RemoteClient/RemoteClient.podspec
で定義されたprepare_command
によって、pod install
のプロセスでprotoc
が自動的に実行され、Objective-Cのファイルが自動生成されていることが分かります。
.
├── GrpcMobileDemo
├── GrpcMobileDemo.xcodeproj
├── GrpcMobileDemo.xcworkspace
├── Podfile
├── Pods
└── RemoteClient
├── Helloworld.pbobjc.h
├── Helloworld.pbobjc.m
├── Helloworld.pbrpc.h
├── Helloworld.pbrpc.m
├── RemoteClient.podspec
└── helloworld.proto
Xcodeのプロジェクトでもファイルが確認できますね。
4. Serviceの呼び出し
以上で準備は完了なので、あとは生成されたClientを利用してServiceを呼び出すだけです。
以下は、localhost:50051
に非SSLで接続してServiceを呼び出すサンプルです。
import Foundation
import UIKit
import RemoteClient
class HelloWorldViewController: UIViewController {
...
@IBAction func requestButtonPressed(sender: UIButton) {
// For debug
// 非SSLでの接続には必要
GRPCCall.useInsecureConnectionsForHost("localhost:50051")
// Call RPC
let request = HLWHelloRequest()
request.name = "neko"
let client = HLWGreeter(host: "localhost:50051")
client.sayHelloWithRequest(request) { response, error in
// response is HLWHelloReply
if let response = response {
self.labelStatus.text = response.message
} else {
print("Error \(error)")
}
}
}
}
なお、実験に使用したgRPCのサーバは、hosopy/grpc-mobile-demo-serverで公開していますが、Dockerですぐに起動できます。
$ docker run -i -t -p 50051:50051 hosopy/grpc-mobile-demo-server