Say hello to gRPC Swift 2: a major update that brings first-class concurrency
support and more expressive APIs for a seamless developer experience.
Inconsistent and poorly documented service APIs create integration headaches for
developers. gRPC is a modern, high-performance framework for building service
APIs, enabling efficient communication between systems over a network.
Since services may be written with a different language than their clients, most
gRPC services use Protocol Buffers (or “protobufs”) to
define their APIs and the messages exchanged between clients and servers.
Service contracts are defined in a neutral, cross-platform format using .proto
files. This is the foundation of your service, not an artefact of its
implementation. And thanks to the format’s efficient binary serialization, these
messages are typically smaller and faster to process than other standard formats
like JSON.
gRPC Swift is one of a family of similar tools that use the Protocol Buffers
contract to generate code in the language you’re working with, making it easy to
build clients and servers that adhere to your service contract. And the new gRPC
Swift 2 offers an idiomatic, cross-platform, performant and feature-rich library
for building highly-scalable services.
This release is a major update that takes advantage of many modern Swift
features for cross-platform services development. When gRPC Swift was first
developed back in 2018, Swift had not yet introduced concurrency features like
async/await,
so it was instead based on SwiftNIO’s event-driven concurrency model. For
developers unfamiliar with these concepts, the prior version of gRPC Swift
presented a steep learning curve. Now that Swift’s modern concurrency model is
fully established, we seized the opportunity to rethink gRPC Swift for today’s
Swift, incorporating lessons learned from our years of use at Apple for building
internet-scale services.
Table of Contents
Highlights
- Modern, flexible, and easy-to-use APIs with idiomatic generated code.
- Full support for building services and clients on Linux and Apple platforms.
- Pluggable transports including a high-performance HTTP/2 transport built on
top of SwiftNIO, and an in-process
transport which is great for testing. - Smart client features like client-side load balancing, a pluggable name
resolution mechanism and automatic retries. - A flexible interceptor layer allowing you to implement cross-cutting logic
like authentication, logging, and metrics.
Hello, swift.org!
Consider the canonical “hello world” service with a single API which returns a
greeting. You might define it like this in a .proto
file:
syntax = "proto3";
service GreetingService {
// Returns a personalized greeting.
rpc SayHello(SayHelloRequest) returns (SayHelloResponse);
}
message SayHelloRequest {
// The name of the person to greet.
string name = 1;
}
message SayHelloResponse {
// The personalized greeting message.
string message = 1;
}
gRPC can be configured to generate:
- Service code so you can implement the business logic.
- Client code to make requests against the service.
Code for messages is generated by
SwiftProtobuf and used in
conjunction with the generated gRPC code.
Generated Service Code
The generated code includes a Swift protocol describing the requirements of the
service with one method for each rpc
in the service definition. To implement
the business logic of your service just implement one of the service protocols.
The example below uses the SimpleServiceProtocol
which is the highest level
API. If you need more flexibility you can use the ServiceProtocol
or
StreamingServiceProtocol
which trade off conciseness for flexibility.
To start the service you need to create a server configured to use a transport
and an instance of your service:
import GRPCCore
import GRPCNIOTransportHTTP2
struct Greeter: GreetingService.SimpleServiceProtocol {
func sayHello(
request: SayHelloRequest,
context: ServerContext
) async throws -> SayHelloResponse {
return SayHelloResponse.with {
$0.message = "Hello, \(request.name)!"
}
}
}
@main
struct GreeterServer {
static func main() async throws {
// Create a plaintext server using the SwiftNIO based HTTP/2 transport
// listening on 128.0.0.1:8080.
let server = GRPCServer(
transport: .http2NIOPosix(
address: .ipv4(host: "127.0.0.1", port: 8080),
transportSecurity: .plaintext
),
services: [Greeter()]
)
// Start serving indefinitely.
try await server.serve()
}
}
Generated Client Code
gRPC generates an idiomatic client for you, simplifying service calls. To use
it, first create a raw client and wrap it with the generated client specific to
your service. This generated client provides a type-safe way for you to easily
interact with your service.
import GRPCCore
import GRPCNIOTransportHTTP2
@main
struct SayHello {
static func main() async throws {
// Create a plaintext client using the SwiftNIO based HTTP/2 transport
// connecting to a service listening on 128.0.0.1:8080.
try await withGRPCClient(
transport: .http2NIOPosix(
target: .dns(host: "127.0.0.1", port: 8080),
transportSecurity: .plaintext
)
) { client in
let greeter = GreetingService.Client(wrapping: client)
let greeting = try await greeter.sayHello(.with { $0.name = "swift.org" })
print(greeting.message)
}
}
}
Package Ecosystem
gRPC Swift 2 was designed with flexibility in mind. It’s distributed as a
collection of packages, allowing you to pick and choose the components which
best suit your needs. These features are provided by the following packages:
- grpc/grpc-swift provides runtime
abstractions and types. - grpc/grpc-swift-nio-transport
implements client and server transports using HTTP/2 and is built on top of
SwiftNIO. - grpc/grpc-swift-protobuf
integrates with SwiftProtobuf to
provide a code generator for services defined in.proto
files. - grpc/grpc-swift-extras includes
common gRPC add-ons, like the reflection and health services, integrations
with Swift Service
Lifecycle, and
interceptors to trace RPCs using OpenTelemetry.
Next Steps
To get started with gRPC Swift 2 check out the tutorials and
documentation
which are hosted on the Swift Package Index, or try out one of the examples in
the grpc/grpc-swift repository.
If you have feature requests, want to report a bug, or would like to contribute
to the project then please reach out to us on
GitHub or join us on the Swift
forums. Let’s connect!
Leave a Reply