square / wire

gRPC and protocol buffers for Android, Kotlin, Swift and Java.
https://square.github.io/wire/
Apache License 2.0
4.24k stars 571 forks source link

Generate methods for Swift, not just structs #2502

Open wbraynen opened 1 year ago

wbraynen commented 1 year ago

Consider the following "hello world" proto: https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto

Compare the following commands:

  1. java -jar wire-compiler-4.7.2-jar-with-dependencies.jar --proto_path=. --kotlin_out=. helloworld.proto
  2. java -jar wire-compiler-4.7.2-jar-with-dependencies.jar --proto_path=. --swift_out=. helloworld.proto

Running the Kotlin version (1 above) generates the following four files:

HelloReply.kt
HelloRequest.kt
GreeterClient.kt
GrpcGreeterClient.kt

Running the Swift version (2 above), however, generates only:

HelloReply.swift
HelloRequest.swift

How to generate the two missing files for Swift also? (For example, a Swift protocol and a class or struct that conforms to that protocol, which contains Swift methods?) Otherwise all we have is models (structs). If it's not currently possible to generate Swift methods, what is the use case for the Swift Wire version in its current form?

oldergod commented 1 year ago

Generating services in swift is not currently supported. I don't believe there are any short term plans to generate something; do you have something in mind of what it would look like?

wbraynen commented 1 year ago

Generating services in swift is not currently supported. I don't believe there are any short term plans to generate something; do you have something in mind of what it would look like?

Given https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto as sample input, without streaming, at a minimum, could just be:

class Greeter {
    func SayHello(request: HelloRequest) async -> HelloReply {
        // implementation
    }
}

Plus could add public as needed, along with a protocol the class conforms to. Or same thing with a protocol if we want to abstract away the implementation.

If it needs to throw an exception or two, the function signature would then be:

func SayHello(request: HelloRequest) async throws -> HelloReply

Plus, ideally, it would follow Swift namespacing conventions as mentioned in this proposal in the alternative repo: https://github.com/apple/swift-protobuf/issues/750 (which I think was maybe met with resistance there).

upanshu21 commented 1 year ago

@oldergod @lickel any updates around this?

lickel commented 1 year ago

We briefly talked about it. There's no current timeline for when this might be tackled. What we did talk about is we would not want to have a full on implementation as you described.

At a minimum we would want the actual networking to be protocol oriented such that you could use (for example) Alamofire, raw URLSessions, or a custom library.

@oldergod pointed out that the Kotlin generated code is built upon GrpcClient

As far as naming is concerned, we would almost certainly follow existing patterns within Wire.

public struct HelloRequest: ProtoMessage {
    public var name: String?
}

public struct HelloReply: ProtoMessage {
    public var message: String?
}

As for what the Greeter object would look like, I could see one of two solutions:

  1. A struct that is inited with a GRPC client:
public struct Greeter: GRPCService {
    public init(client: GRPCClient) { ... }

    func SayHello(request: HelloRequest) async throws -> HelloReply { ... }
}
  1. A protocol with a default implementation when it also conforms to a GRPC client:
protocol Greeter {
    func SayHello(request: HelloRequest) async throws -> HelloReply
}

extension Greeter where Self: GRPCClient {
    public func SayHello(request: HelloRequest) async throws -> HelloReply { ... }
}

2 has the benefit of letting you mock Greeter without mocking out the entire GRPC interface.