Connect-Swift
Connect-Swift is a small library (<200KB!) that provides support for using generated, type-safe, and idiomatic Swift APIs to communicate with your app's servers using Protocol Buffers (Protobuf). It works with the Connect, gRPC, and gRPC-Web protocols.
Imagine a world where
you don't have to handwrite Codable
models for REST/JSON endpoints
and you can instead get right to building features by calling a generated
API method that is guaranteed to match the server's modeling. Furthermore,
imagine never having to worry about serialization again, and being able to
easily write tests using generated mocks that conform to the same protocol
that the real implementations do.
All of this is possible with Connect-Swift.
Given a simple Protobuf schema, Connect-Swift generates idiomatic Swift protocol interfaces and client implementations:
Click to expand eliza.connect.swift
public protocol Eliza_V1_ChatServiceClientInterface: Sendable {
func say(request: Eliza_V1_SayRequest, headers: Headers)
async -> ResponseMessage<Eliza_V1_SayResponse>
}
public final class Eliza_V1_ChatServiceClient: Eliza_V1_ChatServiceClientInterface, Sendable {
private let client: ProtocolClientInterface
public init(client: ProtocolClientInterface) {
self.client = client
}
public func say(request: Eliza_V1_SayRequest, headers: Headers = [:])
async -> ResponseMessage<Eliza_V1_SayResponse>
{
return await self.client.unary(path: "connectrpc.eliza.v1.ElizaService/Say", request: request, headers: headers)
}
}
This code can then be integrated with just a few lines:
final class MessagingViewModel: ObservableObject {
private let elizaClient: Eliza_V1_ChatServiceClientInterface
init(elizaClient: Eliza_V1_ChatServiceClientInterface) {
self.elizaClient = elizaClient
}
@Published private(set) var messages: [Message] {...}
func send(_ userSentence: String) async {
let request = Eliza_V1_SayRequest.with { $0.sentence = userSentence }
let response = await self.elizaClient.say(request: request, headers: [:])
if let elizaSentence = response.message?.sentence {
self.messages.append(Message(sentence: userSentence, author: .user))
self.messages.append(Message(sentence: elizaSentence, author: .eliza))
}
}
}
That’s it! You no longer need to manually define Swift response models,
add Codable
conformances, type out URL(string: ...)
initializers,
or even create protocol interfaces to wrap service classes - all this is taken
care of by Connect-Swift, and the underlying network transport is
handled automatically.
Testing also becomes a breeze with generated mocks which conform to the same protocol interfaces as the production clients:
Click to expand eliza.mock.swift
open class Eliza_V1_ChatServiceClientMock: Eliza_V1_ChatServiceClientInterface, @unchecked Sendable {
public var mockAsyncSay = { (_: Eliza_V1_SayRequest) -> ResponseMessage<Eliza_V1_Response> in .init(message: .init()) }
open func say(request: Eliza_V1_SayRequest, headers: Headers = [:])
async -> ResponseMessage<Eliza_V1_SayResponse>
{
return self.mockAsyncSay(request)
}
}
func testMessagingViewModel() async {
let client = Eliza_V1_ChatServiceClientMock()
client.mockAsyncSay = { request in
XCTAssertEqual(request.sentence, "hello!")
return ResponseMessage(result: .success(.with { $0.sentence = "hi, i'm eliza!" }))
}
let viewModel = MessagingViewModel(elizaClient: client)
await viewModel.send("hello!")
XCTAssertEqual(viewModel.messages.count, 2)
XCTAssertEqual(viewModel.messages[0].message, "hello!")
XCTAssertEqual(viewModel.messages[0].author, .user)
XCTAssertEqual(viewModel.messages[1].message, "hi, i'm eliza!")
XCTAssertEqual(viewModel.messages[1].author, .eliza)
}
Quick Start
Head over to our quick start tutorial to get started. It only takes ~10 minutes to complete a working SwiftUI chat app that uses Connect-Swift!
Documentation
Comprehensive documentation for everything, including interceptors, mocking/testing, streaming, and error handling is available on the connectrpc.com website.
Example Apps
Example apps are available in the Examples
directory and can be opened and built using Xcode. They demonstrate:
- Using streaming APIs
- Integrating with Swift Package Manager
- Integrating with CocoaPods
- Using the Connect protocol
- Using the gRPC protocol
- Using the gRPC-Web protocol
Contributing
We'd love your help making Connect better!
Extensive instructions for building the library and generator plugins locally,
running tests, and contributing to the repository are available in our
CONTRIBUTING.md
guide. Please check it out
for details.
Ecosystem
- connect-kotlin: Idiomatic gRPC & Connect RPCs for Kotlin
- connect-go: Go service stubs for servers
- connect-es: Type-safe APIs with Protobuf and TypeScript
- Buf Studio: Web UI for ad-hoc RPCs
- conformance: Connect, gRPC, and gRPC-Web interoperability tests
Status
This project is in beta, and we may make a few changes as we gather feedback from early adopters. Join us on Slack!
Legal
Offered under the Apache 2 license.