aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Muxi Yan <mxyan@google.com>2018-10-08 15:49:17 -0700
committerGravatar Muxi Yan <mxyan@google.com>2018-10-08 15:49:17 -0700
commit5e790a3117c90c391209646dee8ae984164b1e04 (patch)
tree378326a670dd7d6d6612c6c91acf71f7582d5fe5
parent9fbc9105a62e5ca309d5152407dea0db86cc1709 (diff)
Proto-related changes
-rw-r--r--src/compiler/objective_c_generator.cc103
-rw-r--r--src/compiler/objective_c_generator.h7
-rw-r--r--src/compiler/objective_c_plugin.cc21
-rw-r--r--src/objective-c/ProtoRPC/ProtoRPC.h49
-rw-r--r--src/objective-c/ProtoRPC/ProtoRPC.m157
-rw-r--r--src/objective-c/ProtoRPC/ProtoService.h28
-rw-r--r--src/objective-c/ProtoRPC/ProtoService.m47
7 files changed, 399 insertions, 13 deletions
diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc
index 39f68cb956..eb67e9bb88 100644
--- a/src/compiler/objective_c_generator.cc
+++ b/src/compiler/objective_c_generator.cc
@@ -113,6 +113,29 @@ void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
PrintMethodSignature(printer, method, vars);
}
+void PrintV2Signature(Printer* printer, const MethodDescriptor* method,
+ map< ::grpc::string, ::grpc::string> vars) {
+ if (method->client_streaming()) {
+ vars["return_type"] = "GRPCStreamingProtoCall *";
+ } else {
+ vars["return_type"] = "GRPCUnaryProtoCall *";
+ }
+ vars["method_name"] =
+ grpc_generator::LowercaseFirstLetter(vars["method_name"]);
+
+ PrintAllComments(method, printer);
+
+ printer->Print(vars, "- ($return_type$)$method_name$With");
+ if (method->client_streaming()) {
+ printer->Print("ResponseHandler:(id<GRPCResponseHandler>)handler");
+ } else {
+ printer->Print(vars,
+ "Message:($request_class$ *)message "
+ "responseHandler:(id<GRPCResponseHandler>)handler");
+ }
+ printer->Print(" callOptions:(GRPCCallOptions *_Nullable)callOptions");
+}
+
inline map< ::grpc::string, ::grpc::string> GetMethodVars(
const MethodDescriptor* method) {
map< ::grpc::string, ::grpc::string> res;
@@ -135,6 +158,16 @@ void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
printer->Print(";\n\n\n");
}
+void PrintV2MethodDeclarations(Printer* printer,
+ const MethodDescriptor* method) {
+ map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
+
+ PrintProtoRpcDeclarationAsPragma(printer, method, vars);
+
+ PrintV2Signature(printer, method, vars);
+ printer->Print(";\n\n");
+}
+
void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
map< ::grpc::string, ::grpc::string> vars) {
printer->Print("{\n");
@@ -177,6 +210,25 @@ void PrintAdvancedImplementation(Printer* printer,
printer->Print("}\n");
}
+void PrintV2Implementation(Printer* printer, const MethodDescriptor* method,
+ map< ::grpc::string, ::grpc::string> vars) {
+ printer->Print(" {\n");
+ if (method->client_streaming()) {
+ printer->Print(vars, " return [self RPCToMethod:@\"$method_name$\"\n");
+ printer->Print(" responseHandler:handler\n");
+ printer->Print(" callOptions:callOptions\n");
+ printer->Print(
+ vars, " responseClass:[$response_class$ class]];\n}\n\n");
+ } else {
+ printer->Print(vars, " return [self RPCToMethod:@\"$method_name$\"\n");
+ printer->Print(" message:message\n");
+ printer->Print(" responseHandler:handler\n");
+ printer->Print(" callOptions:callOptions\n");
+ printer->Print(
+ vars, " responseClass:[$response_class$ class]];\n}\n\n");
+ }
+}
+
void PrintMethodImplementations(Printer* printer,
const MethodDescriptor* method) {
map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
@@ -184,12 +236,16 @@ void PrintMethodImplementations(Printer* printer,
PrintProtoRpcDeclarationAsPragma(printer, method, vars);
// TODO(jcanizales): Print documentation from the method.
+ printer->Print("// Deprecated methods.\n");
PrintSimpleSignature(printer, method, vars);
PrintSimpleImplementation(printer, method, vars);
printer->Print("// Returns a not-yet-started RPC object.\n");
PrintAdvancedSignature(printer, method, vars);
PrintAdvancedImplementation(printer, method, vars);
+
+ PrintV2Signature(printer, method, vars);
+ PrintV2Implementation(printer, method, vars);
}
} // namespace
@@ -231,6 +287,25 @@ void PrintMethodImplementations(Printer* printer,
return output;
}
+::grpc::string GetV2Protocol(const ServiceDescriptor* service) {
+ ::grpc::string output;
+
+ // Scope the output stream so it closes and finalizes output to the string.
+ grpc::protobuf::io::StringOutputStream output_stream(&output);
+ Printer printer(&output_stream, '$');
+
+ map< ::grpc::string, ::grpc::string> vars = {
+ {"service_class", ServiceClassName(service) + "2"}};
+
+ printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
+ for (int i = 0; i < service->method_count(); i++) {
+ PrintV2MethodDeclarations(&printer, service->method(i));
+ }
+ printer.Print("@end\n\n");
+
+ return output;
+}
+
::grpc::string GetInterface(const ServiceDescriptor* service) {
::grpc::string output;
@@ -248,10 +323,16 @@ void PrintMethodImplementations(Printer* printer,
" */\n");
printer.Print(vars,
"@interface $service_class$ :"
- " GRPCProtoService<$service_class$>\n");
+ " GRPCProtoService<$service_class$, $service_class$2>\n");
printer.Print(
- "- (instancetype)initWithHost:(NSString *)host"
+ "- (instancetype)initWithHost:(NSString *)host "
+ "callOptions:(GRPCCallOptions "
+ "*_Nullable)callOptions"
" NS_DESIGNATED_INITIALIZER;\n");
+ printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
+ printer.Print(
+ "+ (instancetype)serviceWithHost:(NSString *)host "
+ "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n");
printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
printer.Print("@end\n");
@@ -273,11 +354,19 @@ void PrintMethodImplementations(Printer* printer,
printer.Print(vars,
"@implementation $service_class$\n\n"
"// Designated initializer\n"
- "- (instancetype)initWithHost:(NSString *)host {\n"
+ "- (instancetype)initWithHost:(NSString *)host "
+ "callOptions:(GRPCCallOptions *_Nullable)callOptions{\n"
" self = [super initWithHost:host\n"
" packageName:@\"$package$\"\n"
- " serviceName:@\"$service_name$\"];\n"
+ " serviceName:@\"$service_name$\"\n"
+ " callOptions:callOptions];\n"
" return self;\n"
+ "}\n\n"
+ "- (instancetype)initWithHost:(NSString *)host {\n"
+ " return [self initWithHost:host\n"
+ " packageName:@\"$package$\"\n"
+ " serviceName:@\"$service_name$\"\n"
+ " callOptions:nil];\n"
"}\n\n");
printer.Print(
@@ -292,7 +381,11 @@ void PrintMethodImplementations(Printer* printer,
printer.Print(
"#pragma mark - Class Methods\n\n"
"+ (instancetype)serviceWithHost:(NSString *)host {\n"
- " return [[self alloc] initWithHost:host];\n"
+ " return [self serviceWithHost:host callOptions:nil];\n"
+ "}\n\n"
+ "+ (instancetype)serviceWithHost:(NSString *)host "
+ "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
+ " return [[self alloc] initWithHost:host callOptions:callOptions];\n"
"}\n\n");
printer.Print("#pragma mark - Method Implementations\n\n");
diff --git a/src/compiler/objective_c_generator.h b/src/compiler/objective_c_generator.h
index eb1c7ff005..c171e5bf77 100644
--- a/src/compiler/objective_c_generator.h
+++ b/src/compiler/objective_c_generator.h
@@ -32,9 +32,14 @@ using ::grpc::string;
string GetAllMessageClasses(const FileDescriptor* file);
// Returns the content to be included defining the @protocol segment at the
-// insertion point of the generated implementation file.
+// insertion point of the generated implementation file. This interface is
+// legacy and for backwards compatibility.
string GetProtocol(const ServiceDescriptor* service);
+// Returns the content to be included defining the @protocol segment at the
+// insertion point of the generated implementation file.
+string GetV2Protocol(const ServiceDescriptor* service);
+
// Returns the content to be included defining the @interface segment at the
// insertion point of the generated implementation file.
string GetInterface(const ServiceDescriptor* service);
diff --git a/src/compiler/objective_c_plugin.cc b/src/compiler/objective_c_plugin.cc
index f0fe3688cc..d0ef9ed0d6 100644
--- a/src/compiler/objective_c_plugin.cc
+++ b/src/compiler/objective_c_plugin.cc
@@ -93,7 +93,13 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
SystemImport("RxLibrary/GRXWriteable.h") +
SystemImport("RxLibrary/GRXWriter.h");
- ::grpc::string forward_declarations = "@class GRPCProtoCall;\n\n";
+ ::grpc::string forward_declarations =
+ "@class GRPCProtoCall;\n"
+ "@class GRPCUnaryProtoCall;\n"
+ "@class GRPCStreamingProtoCall;\n"
+ "@class GRPCCallOptions;\n"
+ "@protocol GRPCResponseHandler;\n"
+ "\n";
::grpc::string class_declarations =
grpc_objective_c_generator::GetAllMessageClasses(file);
@@ -103,6 +109,12 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
class_imports += ImportProtoHeaders(file->dependency(i), " ");
}
+ ::grpc::string ng_protocols;
+ for (int i = 0; i < file->service_count(); i++) {
+ const grpc::protobuf::ServiceDescriptor* service = file->service(i);
+ ng_protocols += grpc_objective_c_generator::GetV2Protocol(service);
+ }
+
::grpc::string protocols;
for (int i = 0; i < file->service_count(); i++) {
const grpc::protobuf::ServiceDescriptor* service = file->service(i);
@@ -120,9 +132,10 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
PreprocIfNot(kProtocolOnly, system_imports) + "\n" +
class_declarations + "\n" +
PreprocIfNot(kForwardDeclare, class_imports) + "\n" +
- forward_declarations + "\n" + kNonNullBegin + "\n" + protocols +
- "\n" + PreprocIfNot(kProtocolOnly, interfaces) + "\n" +
- kNonNullEnd + "\n");
+ forward_declarations + "\n" + kNonNullBegin + "\n" +
+ ng_protocols + protocols + "\n" +
+ PreprocIfNot(kProtocolOnly, interfaces) + "\n" + kNonNullEnd +
+ "\n");
}
{
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h
index 45d3526092..1a27cac2a3 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.h
+++ b/src/objective-c/ProtoRPC/ProtoRPC.h
@@ -21,6 +21,55 @@
#import "ProtoMethod.h"
+@class GPBMessage;
+
+/** A unary-request RPC call with Protobuf. */
+@interface GRPCUnaryProtoCall : NSObject
+
+/**
+ * Users should not use this initializer directly. Call objects will be created, initialized, and
+ * returned to users by methods of the generated service.
+ */
+- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
+ message:(GPBMessage *)message
+ responseHandler:(id<GRPCResponseHandler>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass;
+
+/** Cancel the call at best effort. */
+- (void)cancel;
+
+@end
+
+/** A client-streaming RPC call with Protobuf. */
+@interface GRPCStreamingProtoCall : NSObject
+
+/**
+ * Users should not use this initializer directly. Call objects will be created, initialized, and
+ * returned to users by methods of the generated service.
+ */
+- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
+ responseHandler:(id<GRPCResponseHandler>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass;
+
+/** Cancel the call at best effort. */
+- (void)cancel;
+
+/**
+ * Send a message to the server. The message should be a Protobuf message which will be serialized
+ * internally.
+ */
+- (void)writeWithMessage:(GPBMessage *)message;
+
+/**
+ * Finish the RPC request and half-close the call. The server may still send messages and/or
+ * trailers to the client.
+ */
+- (void)finish;
+
+@end
+
__attribute__((deprecated("Please use GRPCProtoCall."))) @interface ProtoRPC
: GRPCCall
diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m
index 5dca971b08..f89c160650 100644
--- a/src/objective-c/ProtoRPC/ProtoRPC.m
+++ b/src/objective-c/ProtoRPC/ProtoRPC.m
@@ -23,9 +23,166 @@
#else
#import <GPBProtocolBuffers.h>
#endif
+#import <GRPCClient/GRPCCall.h>
#import <RxLibrary/GRXWriteable.h>
#import <RxLibrary/GRXWriter+Transformations.h>
+@implementation GRPCUnaryProtoCall {
+ GRPCStreamingProtoCall *_call;
+}
+
+- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
+ message:(GPBMessage *)message
+ responseHandler:(id<GRPCResponseHandler>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass {
+ if ((self = [super init])) {
+ _call = [[GRPCStreamingProtoCall alloc] initWithRequestOptions:requestOptions
+ responseHandler:handler
+ callOptions:callOptions
+ responseClass:responseClass];
+ [_call writeWithMessage:message];
+ [_call finish];
+ }
+ return self;
+}
+
+- (void)cancel {
+ [_call cancel];
+ _call = nil;
+}
+
+@end
+
+@interface GRPCStreamingProtoCall ()<GRPCResponseHandler>
+
+@end
+
+@implementation GRPCStreamingProtoCall {
+ GRPCRequestOptions *_requestOptions;
+ id<GRPCResponseHandler> _handler;
+ GRPCCallOptions *_callOptions;
+ Class _responseClass;
+
+ GRPCCall2 *_call;
+ dispatch_queue_t _dispatchQueue;
+}
+
+- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
+ responseHandler:(id<GRPCResponseHandler>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass {
+ if ((self = [super init])) {
+ _requestOptions = [requestOptions copy];
+ _handler = handler;
+ _callOptions = [callOptions copy];
+ _responseClass = responseClass;
+ _dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
+
+ [self start];
+ }
+ return self;
+}
+
+- (void)start {
+ _call = [[GRPCCall2 alloc] initWithRequestOptions:_requestOptions
+ handler:self
+ callOptions:_callOptions];
+ [_call start];
+}
+
+- (void)cancel {
+ dispatch_async(_dispatchQueue, ^{
+ if (_call) {
+ [_call cancel];
+ _call = nil;
+ }
+ if (_handler) {
+ id<GRPCResponseHandler> handler = _handler;
+ dispatch_async(handler.dispatchQueue, ^{
+ [handler closedWithTrailingMetadata:nil
+ error:[NSError errorWithDomain:kGRPCErrorDomain
+ code:GRPCErrorCodeCancelled
+ userInfo:@{
+ NSLocalizedDescriptionKey :
+ @"Canceled by app"
+ }]];
+ });
+ _handler = nil;
+ }
+ });
+}
+
+- (void)writeWithMessage:(GPBMessage *)message {
+ if (![message isKindOfClass:[GPBMessage class]]) {
+ [NSException raise:NSInvalidArgumentException format:@"Data must be a valid protobuf type."];
+ }
+
+ dispatch_async(_dispatchQueue, ^{
+ if (_call) {
+ [_call writeWithData:[message data]];
+ }
+ });
+}
+
+- (void)finish {
+ dispatch_async(_dispatchQueue, ^{
+ if (_call) {
+ [_call finish];
+ _call = nil;
+ }
+ });
+}
+
+- (void)receivedInitialMetadata:(NSDictionary *)initialMetadata {
+ if (_handler) {
+ id<GRPCResponseHandler> handler = _handler;
+ dispatch_async(handler.dispatchQueue, ^{
+ [handler receivedInitialMetadata:initialMetadata];
+ });
+ }
+}
+
+- (void)receivedMessage:(NSData *)message {
+ if (_handler) {
+ id<GRPCResponseHandler> handler = _handler;
+ NSError *error = nil;
+ id parsed = [_responseClass parseFromData:message error:&error];
+ if (parsed) {
+ dispatch_async(handler.dispatchQueue, ^{
+ [handler receivedMessage:parsed];
+ });
+ } else {
+ dispatch_async(handler.dispatchQueue, ^{
+ [handler closedWithTrailingMetadata:nil error:error];
+ });
+ handler = nil;
+ [_call cancel];
+ _call = nil;
+ }
+ }
+}
+
+- (void)closedWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
+ if (_handler) {
+ id<GRPCResponseHandler> handler = _handler;
+ dispatch_async(handler.dispatchQueue, ^{
+ [handler closedWithTrailingMetadata:trailingMetadata error:error];
+ });
+ _handler = nil;
+ }
+ if (_call) {
+ [_call cancel];
+ _call = nil;
+ }
+}
+
+- (dispatch_queue_t)dispatchQueue {
+ return _dispatchQueue;
+}
+
+@end
+
static NSError *ErrorForBadProto(id proto, Class expectedClass, NSError *parsingError) {
NSDictionary *info = @{
NSLocalizedDescriptionKey : @"Unable to parse response from the server",
diff --git a/src/objective-c/ProtoRPC/ProtoService.h b/src/objective-c/ProtoRPC/ProtoService.h
index 29c4e9be36..2105de78a3 100644
--- a/src/objective-c/ProtoRPC/ProtoService.h
+++ b/src/objective-c/ProtoRPC/ProtoService.h
@@ -21,16 +21,40 @@
@class GRPCProtoCall;
@protocol GRXWriteable;
@class GRXWriter;
+@class GRPCCallOptions;
+@class GRPCProtoCall;
+@class GRPCUnaryProtoCall;
+@class GRPCStreamingProtoCall;
+@protocol GRPCProtoResponseCallbacks;
__attribute__((deprecated("Please use GRPCProtoService."))) @interface ProtoService
- : NSObject -
+ : NSObject
+
+ -
(instancetype)initWithHost : (NSString *)host packageName
- : (NSString *)packageName serviceName : (NSString *)serviceName NS_DESIGNATED_INITIALIZER;
+ : (NSString *)packageName serviceName : (NSString *)serviceName callOptions
+ : (GRPCCallOptions *)callOptions NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithHost:(NSString *)host
+ packageName:(NSString *)packageName
+ serviceName:(NSString *)serviceName;
- (GRPCProtoCall *)RPCToMethod:(NSString *)method
requestsWriter:(GRXWriter *)requestsWriter
responseClass:(Class)responseClass
responsesWriteable:(id<GRXWriteable>)responsesWriteable;
+
+- (GRPCUnaryProtoCall *)RPCToMethod:(NSString *)method
+ message:(id)message
+ responseHandler:(id<GRPCProtoResponseCallbacks>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass;
+
+- (GRPCStreamingProtoCall *)RPCToMethod:(NSString *)method
+ responseHandler:(id<GRPCProtoResponseCallbacks>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass;
+
@end
/**
diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m
index 3e9cc5c0b2..6df502fb0c 100644
--- a/src/objective-c/ProtoRPC/ProtoService.m
+++ b/src/objective-c/ProtoRPC/ProtoService.m
@@ -18,6 +18,7 @@
#import "ProtoService.h"
+#import <GRPCClient/GRPCCall.h>
#import <RxLibrary/GRXWriteable.h>
#import <RxLibrary/GRXWriter.h>
@@ -31,6 +32,7 @@
NSString *_host;
NSString *_packageName;
NSString *_serviceName;
+ GRPCCallOptions *_callOptions;
}
- (instancetype)init {
@@ -40,7 +42,8 @@
// Designated initializer
- (instancetype)initWithHost:(NSString *)host
packageName:(NSString *)packageName
- serviceName:(NSString *)serviceName {
+ serviceName:(NSString *)serviceName
+ callOptions:(GRPCCallOptions *)callOptions {
if (!host || !serviceName) {
[NSException raise:NSInvalidArgumentException
format:@"Neither host nor serviceName can be nil."];
@@ -49,10 +52,17 @@
_host = [host copy];
_packageName = [packageName copy];
_serviceName = [serviceName copy];
+ _callOptions = [callOptions copy];
}
return self;
}
+- (instancetype)initWithHost:(NSString *)host
+ packageName:(NSString *)packageName
+ serviceName:(NSString *)serviceName {
+ return [self initWithHost:host packageName:packageName serviceName:serviceName callOptions:nil];
+}
+
- (GRPCProtoCall *)RPCToMethod:(NSString *)method
requestsWriter:(GRXWriter *)requestsWriter
responseClass:(Class)responseClass
@@ -65,6 +75,41 @@
responseClass:responseClass
responsesWriteable:responsesWriteable];
}
+
+- (GRPCUnaryProtoCall *)RPCToMethod:(NSString *)method
+ message:(id)message
+ responseHandler:(id<GRPCProtoResponseCallbacks>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass {
+ GRPCProtoMethod *methodName =
+ [[GRPCProtoMethod alloc] initWithPackage:_packageName service:_serviceName method:method];
+ GRPCRequestOptions *requestOptions =
+ [[GRPCRequestOptions alloc] initWithHost:_host
+ path:methodName.HTTPPath
+ safety:GRPCCallSafetyDefault];
+ return [[GRPCUnaryProtoCall alloc] initWithRequestOptions:requestOptions
+ message:message
+ responseHandler:handler
+ callOptions:callOptions ?: _callOptions
+ responseClass:responseClass];
+}
+
+- (GRPCStreamingProtoCall *)RPCToMethod:(NSString *)method
+ responseHandler:(id<GRPCProtoResponseCallbacks>)handler
+ callOptions:(GRPCCallOptions *)callOptions
+ responseClass:(Class)responseClass {
+ GRPCProtoMethod *methodName =
+ [[GRPCProtoMethod alloc] initWithPackage:_packageName service:_serviceName method:method];
+ GRPCRequestOptions *requestOptions =
+ [[GRPCRequestOptions alloc] initWithHost:_host
+ path:methodName.HTTPPath
+ safety:GRPCCallSafetyDefault];
+ return [[GRPCStreamingProtoCall alloc] initWithRequestOptions:requestOptions
+ responseHandler:handler
+ callOptions:callOptions ?: _callOptions
+ responseClass:responseClass];
+}
+
@end
@implementation GRPCProtoService