Google Cloud Swift SDK: What It Means for Vapor and Hummingbird
If you run server-side Swift against Google Cloud, you have been on your own. Reading a secret or writing to a bucket from a Vapor app meant signing a JWT yourself, trading it for an OAuth token and building REST calls with AsyncHTTPClient, or depending on a community package that stopped moving in 2024. On October 1, 2026, Google announced an official Google Cloud Swift SDK: the Google Cloud API Client Libraries for Swift, which the announcement calls the Server Side Cloud Swift SDK.
It is real, and it is good Swift: async/await throughout, Sendable clients, pagination as AsyncSequence. It is also a 0.x release labelled GA, it ships as one package per service, it needs Swift 6.2 or later, and some services you would expect are missing. This article covers what Google shipped, how to call it from Vapor with code we compiled against release 0.4.0, and what an existing Swift 6.0 deployment, such as the one serving this page, has to change first. Everything here is as of October 4, 2026.
What Google actually shipped
The code lives in one monorepo, googleapis/google-cloud-swift. On October 4 its generated/ directory held 233 entries; Google's own architecture document says "over 200 client packages". They are generated from Google's service definitions. Beside them sit a handful of handwritten packages: authentication, GAX (retries, pagination, the HTTP layer), a gRPC variant of GAX, the well-known protobuf types, and Cloud Storage, which gets a hand-written client on top of its generated one.
You never depend on the monorepo. Each package is mirrored to its own GitHub repository, so you add swift-google-cloud-secretmanager-v1 or swift-google-cloud-storage individually. There is no umbrella package, and that is deliberate: a server that only reads secrets never compiles Vertex AI.
The client shape is consistent across services. A client is a final class marked Sendable, wrapping a transport stub with retry and logging layers. Requests are built with a .with { } closure. Paged list calls come back as an AsyncSequence, long-running operations as a PollableOperation you can wait() on, and clients own no threads or dispatch queues.
The release history is short. A preview tag appeared in July 2026, then 0.2.0 on September 18, 0.3.0 on September 23 and 0.4.0 on September 29. The README declares the project "General Availability (GA)" as of 0.4.0 and says these 0.x releases are "supported for production use", while reserving "the right to introduce minor breaking changes prior to releasing 1.0". The 0.4.0 notes, arriving within a week of 0.3.0, list at least a dozen breaking changes: renamed enums, well-known types gaining a WKT prefix, Storage renames. Read GA here as a support commitment, not a frozen API.
Google Cloud Blog: Introducing the Server Side Cloud Swift SDK
Requirements and the Swift 6.2 floor
Every package manifest we read declares swift-tools-version 6.2. The README's policy is to support and test the last three minor Swift releases, currently 6.2, 6.3 and 6.4, and to raise the minimum as new versions ship. It also states that a change to swift-tools-version is not a breaking change. So the floor will move, and a version bump will not warn you.
Platforms: Linux is the primary target, tested on Ubuntu 24.04. macOS 15 or later works for local development and needs platforms: [.macOS(.v15)] in your manifest. iOS is not a target. The announcement says not to embed the SDK in an App Store app and to use the Firebase SDK on the client instead.
Now the part that applies to us. This site runs Vapor 4 on Swift 6.0: .swift-version says 6.0, the Docker build stage is swift:6.0-jammy, the runtime image is ubuntu:jammy, and Server/Package.swift is tools 5.9 with a macOS 14 floor. We cannot add a single Google Cloud package today. A Swift 6.0 toolchain refuses a 6.2 manifest outright. Adopting the SDK starts with a toolchain upgrade, not with a dependency line.
For us that means four changes: .swift-version to 6.2 or later, the build stage to a matching image (swift:6.2-noble exists on Docker Hub, as do 6.3 and 6.4), the runtime image to match the builder's distribution, and the macOS platform to .v15. Google's own Cloud Run guide builds on swift:6.3-bookworm and runs on debian:bookworm-slim with ca-certificates and libcurl4 installed. Then build it on Linux before you merge, because a newer Swift on a Mac will accept code the deploy image rejects.
The framework itself does not have to move. In our scratch package, Vapor resolved at 4.122.2 next to the 0.4.0 Google packages, pulling swift-nio up to 2.103.0 and async-http-client to 1.36.2 (this repo pins 2.94.1 and 1.31.0). Vapor 5 will need Swift 6.4, so if that migration is on your roadmap, one toolchain move can cover both.
Vapor 5 Migration: Preparing a Vapor 4 App Before the Guide Exists
Authentication on and off Google Cloud
Auth lives in swift-google-auth (module GoogleAuth). Calling Credentials() resolves Application Default Credentials in order: a key file named by GOOGLE_APPLICATION_CREDENTIALS, the file gcloud auth application-default login writes, then the service account attached to Compute Engine, GKE, Cloud Run or Cloud Functions. Every client falls back to ADC when you pass no credentials.
On Google Cloud, then, there is nothing to configure in code. Attach a service account to the Cloud Run service and give it only the roles it needs; Google's Cloud Run guide grants roles/aiplatform.user for Gemini.
Off Google Cloud it is less tidy. GOOGLE_APPLICATION_CREDENTIALS expects a file path, and a Heroku config var is a string. The auth package also accepts a service account key's contents directly, as Data, so the JSON can live in an environment variable and never touch the disk:
import Foundation
import GoogleAuth
import GoogleGax
let cloudPlatform =
"https://www.googleapis.com/auth/cloud-platform"
func googleOptions() throws -> ClientOptions {
let env = ProcessInfo.processInfo.environment
guard let json = env["GCP_SERVICE_ACCOUNT_JSON"]
else {
// On Google Cloud, ADC finds the attached
// service account. Nothing to configure.
return ClientOptions()
}
let credentials = try Credentials(
configuration: .serviceAccount(
keyJSON: Data(json.utf8),
accessSpecifier: .scopes([cloudPlatform])
)
)
return ClientOptions().with {
$0.credentials = credentials
}
}That is our reading of the documented signature. It compiles against 0.4.0; we have not run it against Google. Two caveats. Google discourages long-lived service account keys, so treat that variable like a password. And the package supports Workload Identity Federation, which swaps a token from another identity provider for a short-lived Google one with no key at all. If your host can issue OIDC tokens, prefer that.
Calling it from Vapor
Google's guide ships one framework example: Hummingbird on Cloud Run, calling Gemini, with a PredictionServiceClient created once at startup and shared by every request. There is no Vapor example. What follows is ours. It compiles with Vapor 4.122.2 and the 0.4.0 packages on Swift 6.4; it has not been run against Google Cloud.
The manifest first. Note the version requirement: SwiftPM's from: allows anything below the next major version, which for a 0.x package means 0.5.0 and its breaking changes would arrive on the next swift package update.
// swift-tools-version: 6.2
import PackageDescription
let gcp = "https://github.com/googleapis/"
let secrets = "swift-google-cloud-secretmanager-v1"
let vertex = "swift-google-cloud-aiplatform-v1"
let storage = "swift-google-cloud-storage"
func google(_ repo: String) -> Package.Dependency {
// 0.x minors may break: stay inside 0.4.x.
.package(url: gcp + repo + ".git",
.upToNextMinor(from: "0.4.0"))
}
let package = Package(
name: "App",
platforms: [.macOS(.v15)],
dependencies: [
.package(url: "https://github.com/vapor/vapor",
from: "4.121.0"),
google("swift-google-auth"),
google("swift-google-gax"),
google(secrets),
google(vertex),
google(storage),
],
targets: [
.executableTarget(name: "App", dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "GoogleAuth",
package: "swift-google-auth"),
.product(name: "GoogleGax",
package: "swift-google-gax"),
.product(name: "GoogleCloudSecretManagerV1",
package: secrets),
.product(name: "GoogleCloudAIPlatformV1",
package: vertex),
.product(name: "GoogleCloudStorage",
package: storage),
]),
]
)GoogleAuth and GoogleGax are listed because the auth snippet imports them, and the README asks you to declare what you use rather than lean on transitive dependencies. Vertex AI is split by package traits: its default trait is PredictionService, which is where Gemini lives, and the other Vertex services compile only when you enable their traits.
Secret Manager
Reading the latest version of a secret is one call. The version name accepts latest as an alias:
import Foundation
import GoogleCloudSecretManagerV1
func readSecret(
_ name: String,
project: String,
client: SecretManagerServiceClient
) async throws -> String {
let path = "projects/\(project)/secrets/\(name)"
+ "/versions/latest"
let response = try await client.accessSecretVersion(
request: AccessSecretVersionRequest().with {
$0.name = path
}
)
let data = response.payload?.data ?? Data()
return String(decoding: data, as: UTF8.self)
}Call it in configure(_:) for the values your app needs at boot, not inside a request handler. One sign of how young this is: the announcement's list example calls listSecretsByItem, which does not compile against 0.4.0. The method is now listSecretsByItems, plural.
Gemini and Cloud Storage in one route
Because every client is Sendable, a plain struct holding them is Sendable too, and a RouteCollection is a natural home. The clients are created once and shared, as in Google's Hummingbird sample:
import GoogleCloudAIPlatformV1
import GoogleCloudStorage
import Vapor
func configure(_ app: Application) async throws {
let options = try googleOptions()
let project = Environment.get("GCP_PROJECT") ?? ""
let modelID = Environment.get("GEMINI_MODEL") ?? ""
try app.register(collection: GoogleRoutes(
gemini: try PredictionServiceClient(options),
storage: try StorageClient(
StorageClientOptions(client: options)
),
model: "projects/\(project)/locations/global"
+ "/publishers/google/models/\(modelID)",
bucket: Environment.get("GCS_BUCKET") ?? ""
))
}The route takes a prompt, asks Gemini through Vertex AI, writes the answer to a bucket and returns it:
import Foundation
import GoogleCloudAIPlatformV1
import GoogleCloudStorage
import Vapor
// Vertex AI also defines a Content type,
// so Vapor's protocol needs its module name.
struct Prompt: Vapor.Content {
let text: String
}
struct GoogleRoutes: RouteCollection {
let gemini: PredictionServiceClient
let storage: StorageClient
let model: String
let bucket: String
func boot(routes: any RoutesBuilder) throws {
routes.post("summaries", use: summarize)
}
@Sendable
func summarize(req: Request) async throws -> String {
let prompt = try req.content.decode(Prompt.self)
let text = try await generate(prompt.text)
_ = try await storage.writeObject(
Data(text.utf8),
to: "projects/_/buckets/\(bucket)",
as: "summaries/\(UUID().uuidString).txt"
)
return text
}
}import GoogleCloudAIPlatformV1
import Vapor
extension GoogleRoutes {
func generate(_ prompt: String) async throws
-> String
{
let part = Part().with { $0.data = .text(prompt) }
let message = GoogleCloudAIPlatformV1.Content()
.with {
$0.role = "user"
$0.parts = [part]
}
let request = GenerateContentRequest().with {
$0.model = model
$0.contents = [message]
}
let response = try await gemini.generateContent(
request: request
)
let parts = response.candidates.first?
.content?.parts ?? []
for part in parts {
if case .text(let text)? = part.data {
return text
}
}
throw Abort(.badGateway)
}
}Three things the compiler taught us. First, the name clash in the comment is real: without the module prefix, Vapor and Vertex AI both export Content, and the build fails with "'Content' is ambiguous for type lookup in this context". Second, there is no streaming call. PredictionServiceClient in 0.4.0 has generateContent and no streamGenerateContent, which matches the release notes, so token-by-token output is not available yet. Third, the model path format comes from Google's sample, but we read the model ID from configuration on purpose: Google's two Gemini samples name different models, and we have not checked either against Vertex AI.
Reading an object back is the mirror image: readObject(from:object:) returns a handle whose body is an async sequence of chunks.
What it replaces and what it does not
Before this, the options were thin. The community GoogleCloudKit package under vapor-community was last pushed on August 29, 2024, still advertises Swift 5.2, and supports service account OAuth only. Google's own older Swift repositories, google-api-swift-client and google-auth-library-swift, last moved in January and August 2024. People were asking for server-side Google client libraries on the Swift Forums as early as 2019.
The common answer was raw AsyncHTTPClient. Moving off it costs little, because the SDK uses AsyncHTTPClient internally for its REST transport; it just hides it. What you gain is token refresh, retries with backoff, pagination and typed requests that you no longer maintain.
It does not replace Firebase in your iOS app, and it does not cover every service, which is the next section. If Gemini is one model among several behind your backend, the SDK is a provider adapter, not an architecture.
Multi-provider AI architecture for apps
AWS is the obvious comparison, and it is years ahead here. Soto, the community SDK, was at 7.17.0 on October 1, 2026 and declares tools 6.0. AWS's own aws-sdk-swift was at 1.8.4 a day later. Both are past 1.0, and Soto's 6.0 manifest still resolves on the toolchain Google's packages refuse. What Google brings is a first-party SDK, generated across 200-plus services, written for strict concurrency from the start.
Coverage gaps and contradictions
As of October 4, 2026 we found no client package for Pub/Sub, the Firestore data plane, Spanner, the core BigQuery query API or the Gemini Developer API (generativelanguage). We checked the generated/ listing and probed the likely repository names. BigQuery is present only as side APIs such as reservations and connections, and the nearest thing to Firestore is the Datastore admin API. These are absences on one date, not a roadmap statement.
What is there: Cloud Storage, Secret Manager, Vertex AI, Cloud KMS, Cloud Run admin, Cloud Tasks, Cloud Scheduler, Logging, Monitoring, Speech, Text-to-Speech, Translate, Vision, Document AI, Natural Language and IAM, among others.
The sources also disagree with each other in three places:
- Transport. The announcement headlines "gRPC transport". The repo's ARCHITECTURE.md says generated clients use REST/JSON over HTTP by default, to avoid linking large transport stacks, and use gRPC only where a service needs it, such as Storage Control and streaming APIs. The manifests agree with the architecture document: Secret Manager has no gRPC dependency, while Storage depends on both.
- Readiness. The README says GA and supported for production. ARCHITECTURE.md, in the same repository, still says "The APIs are not stable; they are not ready for use in production code." It is probably stale text. It is also unresolved.
- Names. The announcement lists the auth package as swift-google-cloud-auth; the repository is swift-google-auth. Add the listSecretsByItem example above, and the rule is simple: trust the 0.4.0 sources over the blog post.
googleapis/google-cloud-swift on GitHub
What this cannot tell you
- Whether the code works against Google Cloud. It compiled on macOS with Swift 6.4. It has not run against a real project, and it has not been built on Linux with Swift 6.2.
- Build times, binary size or image size. Google publishes none. Its Cloud Run guide recommends a 32-vCPU Cloud Build machine "to speed up Swift compilation", which hints at the answer without giving one.
- Latency or throughput. We have measured nothing, and REST versus gRPC per service makes any general figure suspect.
- How it holds up in production. The SDK was days old when we wrote this, and we found no production reports.
- How often minor releases will break. Three 0.x releases shipped in eleven days, and the last of them carried breaking changes. That is a short history to plan against.
- Which Gemini model IDs to use. Check the Vertex AI model list for your region rather than copying one from a sample.
A practical adoption checklist
- List the Google services you call and check each against the package list. If you need Pub/Sub or Firestore, keep your REST code for those.
- Upgrade the toolchain first: .swift-version, the Docker build and runtime images, and platforms: [.macOS(.v15)]. Build on Linux before merging.
- Pin with .upToNextMinor(from:), not from:, so a breaking 0.x minor arrives when you choose it.
- Declare every Google product you import directly in your manifest, including GoogleAuth and GoogleGax.
- Use ADC on Google Cloud with a least-privilege service account. Elsewhere, prefer Workload Identity Federation; if you must use a key, load the JSON from a secret environment variable.
- Create each client once at boot and share it. They are Sendable for exactly this.
- Read secrets at boot, not per request.
- Keep the Gemini model ID in configuration, and confirm it in your Vertex AI region.
- Read the ! entries in the release notes before every minor bump, and re-check whether ARCHITECTURE.md and the README have been reconciled.
If you are still choosing the framework this will sit in, the SDK does not decide it for you: Google's only sample is Hummingbird, and our compiled example is Vapor.
Vapor vs Hummingbird in 2026: Choosing a Server-Side Swift Framework
Comments
Loading comments…