Understanding-the-generated-code.md 5.1 KB

Understanding the generated code

Understand what code is generated by protoc-gen-grpc-swift from a .proto file and how to use it.

Overview

The gRPC Swift Protobuf package provides a plugin to the Protocol Buffers Compiler (protoc) called protoc-gen-grpc-swift. The plugin is responsible for generating the gRPC specific code for services defined in a .proto file.

Package namespace

Most .proto files contain a package directive near the start of the file describing the namespace it belongs to. Here's an example:

package foo.bar.v1;

The package name "foo.bar.v1" is important as it is used as a prefix for generated types. The default behaviour is to replace periods with underscores and to capitalize each word and add a trailing underscore. For this package the prefix is "Foo_Bar_V1_". If you don't declare a package then the prefix will be the empty string.

You can override the prefix by setting the swift_prefix option:

option swift_prefix = "FooBarV1";

package foo.bar.v1;

The prefix for types in this file would be "FooBarV1" instead of "Foo_Bar_V1_".

Service namespace

For each service declared in your .proto file, gRPC will generate a caseless enum which is a namespace holding the generated protocols and types. The name of this enum is {PREFIX}{SERVICE} where {PREFIX} is as described in the previous section and {SERVICE} is the name of the service as declared in the .proto file.

As an example the following definition creates a service namespace enum called Foo_Bar_V1_BazService (the {PREFIX} is "Foo_BarV1" and {SERVICE} is "BazService"):

package foo.bar.v1;

service BazService {
  // ...
}

Code generated for each service falls into three categories:

  1. Service metadata,
  2. Service code, and
  3. Client code.

Service metadata

gRPC generates metadata for each service including a descriptor identifying the fully qualified name of the service and information about each method in the service. You likely won't need to interact directly with this information but it's available should you need to.

Service code

Within a service namespace gRPC generates three service protocols:

  1. StreamingServiceProtocol,
  2. ServiceProtocol, and
  3. SimpleServiceProtocol.

The full name of each protocol includes the service namespace.

Example:

For the BazService in the foo.bar.v1 package the protocols would be:

  • Foo_Bar_V1_BazService.StreamingServiceProtocol,
  • Foo_Bar_V1_BazService.ServiceProtocol, and
  • Foo_Bar_V1_BazService.SimpleServiceProtocol.

Each of these protocols mirror the service defined in your .proto file with one requirement per RPC. To implement your service you must implement one of these protocols.

The protocols form a hierarchy with StreamingServiceProtocol at the bottom and SimpleServiceProtocol at the top. ServiceProtocol refines StreamingServiceProtocol, and SimpleServiceProtocol refines ServiceProtocol (and StreamingServiceProtocol in turn).

The StreamingServiceProtocol implements each RPC as if it were a bidirectional streaming RPC. This gives you the most flexibility at the cost of a harder to implement API. It also puts the responsibility on you to ensure that each RPC sends and receives the correct number of messages.

The ServiceProtocol enforces that the correct number of messages are sent and received via its API. It also allows you to read request metadata and send both initial and trailing metadata. The request and response types for these requirements are in terms of ServerRequest and ServerResponse.

The SimpleServiceProtocol also enforces the correct number of messages are sent and received via its API. However, it isn't defined in terms of ServerRequest or ServerResponse so it doesn't allow you access metadata. This limitation allows it to have the simplest API and is preferred if you don't need access to metadata.

Protocol Enforces number of messages Access to metadata
StreamingServiceProtocol
ServiceProtocol
SimpleServiceProtocol

Client code

gRPC generates two types for the client within a service namespace:

  1. ClientProtocol, and
  2. Client.

Like the service code, the full name includes the namespace.

Example:

For the BazService in the foo.bar.v1 package the client types would be:

  • Foo_Bar_V1_BazService.ClientProtocol, and
  • Foo_Bar_V1_BazService.Client.

The ClientProtocol defines one requirement for each RPC in terms of ClientRequest and ClientResponse. You don't need to implement the protocol as Client provides a concrete implementation.

gRPC also generates extensions on ClientProtocol to provide more ergonomic APIs. These include versions which provide default arguments for various parameters (like the message serializer and deserializers; call options and response handler) and versions which don't use ClientRequest and ClientResponse directly.