What are you trying to achieve?
I am building an iOS app for a complex product that speaks to multiple logical GRPC services, all served from the same server. So, for example, I can call UserService.GetUser() and ProgramService.GetProgram() and they will both go to the same (load-balanced, horizontally scaled) host.
Every example I've seen for usage of Swift GRPC 2 is for a one-off single call to a single service, looking like:
try await withGRPCClient(
transport: .http2NIOPosix(
target: .dns(host: hostname, port: 443),
transportSecurity: .tls
)
) { client in
let greeter = GreetingService.Client(wrapping: client)
let greeting = try await greeter.sayHello(.with { $0.name = "swift.org" })
print(greeting.message)
}
I'd like to understand best practices for usage with multiple services, within an actual app that needs a connection to function. A couple of options spring to mind:
- Create a new GRPCClient for each service client.
- Create a single GRPCClient and then re-use that for each service client.
- Create a single GRPCClient that has some kind of pool behind it (of say, 4 connections), then re-use that single exposed GRPCClient for each service client.
Option 2/3 is most similar to what I had for Swift GRPC 1, and my preferred approach if possible. (There are already a dozen services, and there will be more in the future.)
I would also like to understand if there's a blocking/synchronous approach to ensuring a connection is established.
What have you tried so far?
Previously, I had a single synchronously established GRPCChannel, exposed as a static computed variable:
class GRPCManager {
private static var host: String {
Bundle.main.infoDictionary?["APP_CORE_HOST"] as! String
}
static var connection: GRPCChannel {
guard host != "" else {
fatalError("GRPC Host is not set")
}
return connect()
}
private static func connect() -> GRPCChannel {
let eventLoopGroup = NIOTSEventLoopGroup(loopCount: 1, defaultQoS: .default)
return ClientConnection.usingPlatformAppropriateTLS(for: eventLoopGroup)
.connect(host: host, port: 443)
}
}
and then each services had an init like:
init() {
client = App_UserServiceAsyncClient(channel: GRPCManager.connection)
}
init() {
client = App_ProgramServiceAsyncClient(channel: GRPCManager.connection)
}
This lazily established the connection the first time it was needed, requiring no explicit setup in the @main App. It seemed to work well. I appreciate that client.runConnections() is now asynchronous, but I need to know when it's reliably available/block until the connection is established, because the app is unusable without it.
I would like to achieve this while keeping the service clients themselves isolated and independent of each other, in particular not requiring a specific order of service usage.
What are you trying to achieve?
I am building an iOS app for a complex product that speaks to multiple logical GRPC services, all served from the same server. So, for example, I can call UserService.GetUser() and ProgramService.GetProgram() and they will both go to the same (load-balanced, horizontally scaled) host.
Every example I've seen for usage of Swift GRPC 2 is for a one-off single call to a single service, looking like:
I'd like to understand best practices for usage with multiple services, within an actual app that needs a connection to function. A couple of options spring to mind:
Option 2/3 is most similar to what I had for Swift GRPC 1, and my preferred approach if possible. (There are already a dozen services, and there will be more in the future.)
I would also like to understand if there's a blocking/synchronous approach to ensuring a connection is established.
What have you tried so far?
Previously, I had a single synchronously established GRPCChannel, exposed as a static computed variable:
and then each services had an init like:
This lazily established the connection the first time it was needed, requiring no explicit setup in the
@main App. It seemed to work well. I appreciate thatclient.runConnections()is now asynchronous, but I need to know when it's reliably available/block until the connection is established, because the app is unusable without it.I would like to achieve this while keeping the service clients themselves isolated and independent of each other, in particular not requiring a specific order of service usage.