Introducing gRPC Swift 2

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.

Highlights

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:

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:

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!