Skip to content

feat(compiler): Add grpc support for Swift#3776

Draft
yash-agarwa-l wants to merge 16 commits into
apache:mainfrom
yash-agarwa-l:grpc-swift
Draft

feat(compiler): Add grpc support for Swift#3776
yash-agarwa-l wants to merge 16 commits into
apache:mainfrom
yash-agarwa-l:grpc-swift

Conversation

@yash-agarwa-l

Copy link
Copy Markdown
Contributor

Why?

Swift users can generate Fory model types today, but schemas that define services
do not produce Swift gRPC companions. This leaves Swift out of the existing --grpc
workflow used by the other supported service-generation targets.

What does this PR do?

  • Adds Swift gRPC companion generation for Fory compiler services, including service
    metadata descriptors, an EventLoopFuture provider, an async/await provider, an
    async client, and Fory-backed request/response stream adapters, targeting grpc-swift 1.x.
  • Serializes request and response bodies with Fory instead of protobuf through an
    internal GRPCPayload wrapper. Because the Swift Fory runtime is single-threaded,
    the wrapper builds one Fory per thread from the schema module's own configuration
    and registrations, so concurrent RPCs are race-free (verified under ThreadSanitizer).
  • Emits an async-only client; the EventLoopFuture client and interceptor hooks are
    omitted because their generated types would expose the internal wrapper.
  • Adds Swift preflight validation for generated output-path and top-level symbol
    collisions, and reserves inherited provider/client member names (handle,
    serviceName, channel, defaultCallOptions) so a clashing rpc fails codegen
    with a clear message.
  • Updates compiler and service-codegen tests to cover the four streaming shapes,
    identifier escaping, imported and nested service types, the default package, the
    protobuf and FlatBuffers frontends, collision handling, a SwiftPM build-and-run
    fixture, and a marshaller concurrency test under ThreadSanitizer (gated behind
    FORY_SWIFT_TSAN).
  • Wires Swift output into the cross-language gRPC generation helper.
  • Documents Swift gRPC support, dependencies, generated API shape, streaming, the
    shared-top-level-package limitation, troubleshooting, and compiler guide updates.

Draft: Java<->Swift cross-language interop tests (all four modes, all three IDL
frontends) are in progress and will be added before this PR is marked ready for review.

Related issues

#3266
#3370

AI Contribution Checklist

  • Substantial AI assistance was used in this PR: yes
  • If yes, I included a completed AI Contribution Checklist in this PR description and the required AI Usage Disclosure.
  • If yes, my PR description includes the required ai_review summary and screenshot evidence of the final clean AI review results from both fresh reviewers on the current PR diff or current HEAD after the latest code changes.

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?
    • Adds generated Swift gRPC companion APIs when foryc --swift_out=... --grpc is used.
  • Does this PR introduce any binary protocol compatibility change?
    • The generated services use Fory-encoded gRPC message bodies, but this PR does not
      change the Fory binary protocol.

Benchmark

Not applicable.

Schemas with services now emit a <Service>Grpc.swift companion beside the
Swift model. Each service gets Fory-backed async and NIO providers plus an
async client; request and response bytes ride a private GRPCPayload wrapper
that serializes through the schema module's Fory instance.
Before writing Swift output, check that no two schemas or services claim the
same file path or top-level symbol. A service named after a generated type, or
a duplicate service, now fails fast with a clear message instead of emitting
Swift that will not compile.
Exercise the Swift companion across the four streaming shapes, keyword-escaped
methods, imported request and response types, the default package, both IDL
frontends, and the collision preflight, so the emitter and its symbol names stay
pinned.
Generate a two-package schema, then swift build and run a SwiftPM package on
grpc-swift and local Fory that hosts the generated provider and round-trips all
four streaming shapes across the import boundary. Skipped when swift is absent.
Wire Swift into the shared gRPC generation step so the interop schemas emit
Swift companions alongside the other targets.
Add a Swift gRPC guide covering dependencies, server and client usage, streaming,
and troubleshooting, link it from the Swift guide index, and note the Swift
companion in the compiler guide and agent rules.
Break the streaming handler closures across lines so generated companions stay
under the swiftlint line-length limit even with long package-qualified names.
Put handler braces on the declaration line, give each async parameter its own
aligned line, name the unwrapped stream value, and scope a type_name disable
around the package-prefixed symbols so swiftlint reports no violations.
Schemas that share a top-level package component make the model generator emit a
duplicate root enum, which the Swift compiler rejects in one module. Pin it with
a strict xfail fixture and a docs note pointing at disjoint packages.
The Swift Fory instance is single-threaded, but gRPC drives the marshaller from
many threads at once, so sharing one instance races. Build one Fory per thread
from the module config and registrations, and fire 200 parallel calls in the
fixture to exercise it.
Record that the generated client is async only and that interceptors are not
emitted, both because grpc-swift types them on the internal Fory wrapper, and
describe the per-thread marshalling.
Name the wire wrapper per service so it is reachable, then drive it from 2000
parallel threads under ThreadSanitizer, asserting no data race and that the
per-thread Fory stays wire-compatible with the module's shared instance. Against
a shared instance TSan flags a race in the type resolver.
The ThreadSanitizer build adds about three minutes and is environment sensitive,
so keep it opt-in for a sanitizer or nightly job while the functional fixtures
still run on every swift-capable run.
An rpc whose Swift name is handle, serviceName, channel, or defaultCallOptions
would clash with a member the generated provider or client inherits, so fail
codegen with a clear message. Cover the reserved names and nested plus imported
request and response payloads.
# Conflicts:
#	compiler/fory_compiler/tests/test_service_codegen.py
#	docs/compiler/compiler-guide.md
#	integration_tests/grpc_tests/generate_grpc.py
@yash-agarwa-l yash-agarwa-l changed the title Grpc swift feat(swift): Add grpc support for Swift Jun 21, 2026
@chaokunyang

Copy link
Copy Markdown
Collaborator

@yash-agarwa-l Please git merge apache/main first to address the conflicts

@yash-agarwa-l

Copy link
Copy Markdown
Contributor Author

I already did that, so I'm not sure why it's still showing up. Let me mark it as ready for review and double-check.

@yash-agarwa-l yash-agarwa-l marked this pull request as ready for review June 21, 2026 14:06
The SwiftPM build-and-run round-trip and the ThreadSanitizer marshaller test
need the Swift toolchain, so move them out of the compiler pytest suite (where
they only skipped) into a SwiftPM package under integration_tests/grpc_tests/swift.
The common-root package limitation stays pinned as a build-free generation check.
@yash-agarwa-l yash-agarwa-l changed the title feat(swift): Add grpc support for Swift feat(compiler): Add grpc support for Swift Jun 21, 2026
@yash-agarwa-l yash-agarwa-l marked this pull request as draft June 21, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants