From c18a7de6fc39718f3fd86d961e7e844efb8f1714 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 4 Jun 2025 09:13:36 +0100 Subject: [PATCH] Document how to create a public service with private implementation Motivation: Sometimes libraries want to vend a public gRPC service but without exposing the generated types as part of the public API. This is easy to achieve but non-obvious. Modifications: - Add an article explaining how to achieve this Result: Easier for users to learn how to do this --- ...c-services-with-private-implementations.md | 45 +++++++++++++++++++ .../Documentation.docc/Documentation.md | 1 + 2 files changed, 46 insertions(+) create mode 100644 Sources/GRPCProtobuf/Documentation.docc/Articles/Public-services-with-private-implementations.md diff --git a/Sources/GRPCProtobuf/Documentation.docc/Articles/Public-services-with-private-implementations.md b/Sources/GRPCProtobuf/Documentation.docc/Articles/Public-services-with-private-implementations.md new file mode 100644 index 0000000..4d5485f --- /dev/null +++ b/Sources/GRPCProtobuf/Documentation.docc/Articles/Public-services-with-private-implementations.md @@ -0,0 +1,45 @@ +# Public services with private implementations + +Learn how to create a `public` gRPC service with private implementation details. + +## Overview + +It's not uncommon for a library to provide a gRPC service as part of its API. +For example, the gRPC Swift Extras package provides implementations of the gRPC +health and reflection services. Making the implementation of a service `public` +would require its generated gRPC and message types to also be `public`. This is +undesirable as it leaks implementation details into the public API of the +package. This article explains how to keep the generated types private while +making the service available as part of the public API. + +## Hiding the implementation + +You can hide the implementation details of your service by providing a wrapper +type conforming to `RegistrableRPCService`. This is the protocol used by +`GRPCServer` to register service methods with the server's router. Implementing +`RegistrableRPCService` is straightforward and can delegate to the underlying +service. This is demonstrated in the following code: + +```swift +public struct GreeterService: RegistrableRPCService { + private var base: Greeter + + public init() { + self.base = Greeter() + } + + public func registerMethods( + with router: inout RPCRouter + ) where Transport: ServerTransport { + self.base.registerMethods(with: &router) + } +} +``` + +In this example `Greeter` implements the underlying service and would conform to +the generated service protocol but would have a non-public access level. +`GreeterService` is a public wrapper type conforming to `RegistrableRPCService` +which implements its only requirement, `registerMethods(with:)`, by calling +through to the underlying implementation. The result is a service which can be +registered with a server where none of the generated types are part of the +public API. diff --git a/Sources/GRPCProtobuf/Documentation.docc/Documentation.md b/Sources/GRPCProtobuf/Documentation.docc/Documentation.md index f0ccd83..f3fb528 100644 --- a/Sources/GRPCProtobuf/Documentation.docc/Documentation.md +++ b/Sources/GRPCProtobuf/Documentation.docc/Documentation.md @@ -23,6 +23,7 @@ This package provides three products: - - - +- ### Serialization