Generating code
A Protobuf schema is a simple file that describes a service, its methods (APIs), and their request/response types:
syntax = "proto3";
package connectrpc.eliza.v1;
message SayRequest {
string sentence = 1;
}
message SayResponse {
string sentence = 1;
}
service ElizaService {
rpc Say(SayRequest) returns (SayResponse) {}
}
A fully documented version of the above definition can be seen in the Buf Schema Registry (BSR).
The rpc
keyword stands for Remote Procedure Call — an API method that can be
invoked remotely. The schema is a contract between the server and client, and
it precisely defines how data is exchanged.
The schema comes to life by generating code. For the server, an interface is generated, and the engineer can focus on filling the methods with business logic. For the client, there really isn't anything more to do — the engineer can simply call the client methods, rely on the generated types for compile-time type-safety and serialization, and focus on the application logic.
Remote plugins
Note: The example in the tutorial covers much of this section's content.
Protobuf plugins are executables that accept .proto
file inputs and generate various outputs (.swift
files in this case).
Performing generation on a remote machine makes local setup easier
and allows the generation to take place in an isolated
environment. We'll use Buf, a modern replacement for
Google's protobuf compiler, along with remote plugins.
This requires installing Buf's CLI:
brew install bufbuild/buf/buf
When developing a new project, 2 new files need to be created:
The first file, buf.yaml
, can be created by running:
buf mod init
The second file, buf.gen.yaml
, needs to be created manually and specifies
which plugins should be used to generate code. An example of this
file is shown below:
version: v1
plugins:
- plugin: buf.build/connectrpc/swift
opt:
- GenerateAsyncMethods=true
- GenerateCallbackMethods=true
- Visibility=Public
out: Generated
- plugin: buf.build/apple/swift
opt:
- Visibility=Public
out: Generated
This file specifies that the connect-swift
plugin
should be invoked with the options in opt
, and that its outputs should be
placed in the Generated
directory. This plugin is responsible for generating
.connect.swift
files which contain Swift protocol interfaces and their
corresponding implementations from the defined service
and rpc
types in
Protobuf files.
The config also includes the apple/swift
plugin with
another set of options that place its .pb.swift
outputs
in the same Generated
directory. This plugin
generates models from Protobuf types such as message
and enum
.
Together, the two plugins generate all the code that you'll need.
Details on configuring plugins in
buf.gen.yaml
may be found in the documentation, and you can browse the full list of available remote plugins here.
With these configuration files in place, you can now generate code:
buf generate
Given the above config and example eliza.proto
file, you should now see some
generated Swift files in the Generated
directory:
Generated
├── eliza.connect.swift
└── eliza.pb.swift
Local generation
Both the connect-swift
and connect-swift-mocks
plugins are regular
Protobuf plugins which can be used with protoc
and buf
to generate
code locally.
The easiest way to install these plugins is to download their executables
from the latest GitHub release and install them in
your PATH
.
The same setup used for remote plugins above applies to
local generation, except the buf.gen.yaml
file should be modified to use
local plugins instead of remote plugins:
version: v1
plugins:
- plugin: connect-swift # protoc-gen-connect-swift in your PATH
opt:
- GenerateAsyncMethods=true
- GenerateCallbackMethods=true
- Visibility=Public
out: Generated
- plugin: swift # protoc-gen-swift in your PATH
opt:
- Visibility=Public
out: Generated
Using generated code
You'll need to add the generated .swift
files from the previous steps to your
project by dragging the Generated
directory (the directory specified in
the out
option) into Xcode.
You may want to create one or more separate Swift modules for the generated outputs so that you can import them from the code that will use these APIs.
The generated code depends on both the Connect
and SwiftProtobuf
libraries.
You can add these dependencies by following
these steps in the tutorial.
For guidance on how to call the generated code, see the documentation for using clients.
Generation options
Both the connect-swift
and connect-swift-mocks
plugins support a
variety of options that can be
used to customize outputs. These options can be combined in the opt
field of
the buf.gen.yaml
file as shown in the example above.
Option | Type | Default | Repeatable | Details |
---|---|---|---|---|
ExtraModuleImports | String | None | Yes | Allows for specifying additional modules that generated Connect sources should import |
FileNaming | String | FullPath | No | Documentation |
GenerateAsyncMethods | Bool | true | No | Generates RPC functions that provide Swift async/await interfaces |
GenerateCallbackMethods | Bool | false | No | Generates RPC functions that provide closure-based callback interfaces |
GenerateServiceMetadata | Bool | true | No | Generates metadata on client implementations, providing information on RPC paths, stream types, etc. |
KeepMethodCasing | Bool | false | No | Generated RPC function names will match the rpc specified in the .proto file instead of being lower-camel-cased |
ProtoPathModuleMappings | Custom | None | No | Documentation |
SwiftProtobufModuleName | String | SwiftProtobuf | No | Allows for overriding the SwiftProtobuf module name in import statements. Useful if the SwiftProtobuf dependency is being renamed by custom build configurations |
Visibility | String | Internal | No | Documentation |