# Apollo iOS treats code generation as the product, not the client

> A close read of Apollo iOS: a two product Swift Package, a dual v1 and v2 release line shipped on the same day, an opt in codegen naming layer, and a cache whose extension points keep widening.

**apollographql/apollo-ios** — 📱  A strongly-typed, caching GraphQL client for iOS, written in Swift.

- Repository: https://github.com/apollographql/apollo-ios
- Website: https://www.apollographql.com/docs/ios/
- Stars: 4,037 · Forks: 762
- Language: Swift
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/apollographql-apollo-ios

## One dependency, two products, and a deliberate split

The install path is a single Swift Package Manager entry, pinned to the next major from 2.0.0:

```swift
dependencies: [
    .package(
        url: "https://github.com/apollographql/apollo-ios.git",
        .upToNextMajor(from: "2.0.0")
    ),
],
```

What follows from that is a distinction the README makes in a single note, and it is the most useful architectural fact on the page. Generated models are not the same product as the runtime. A target that only needs your GraphQL types does not have to link `Apollo` at all:

```swift
.target(
    name: "MyApp",
    dependencies: [
        .product(name: "Apollo", package: "apollo-ios"),
    ]
)
```

So there is a dependency link for targets that construct an `ApolloClient`, and there is no link for targets that merely reference a `GetLaunchesQuery` response type. That is a small design decision with a large effect on build times, because the runtime is where the cache, the network transport, and the retry logic live, and a feature target that only decodes structs should not pay to compile any of it. Teams with a modular app architecture can put generated types in a leaf module that nearly everything imports, and link the client once at the top.

The supported surface is broad. Badges list iOS 15+, macOS 12+, tvOS 15+, watchOS 8+, and visionOS 1+, with Swift 6.1 support and Swift Package Manager compatibility. The license is MIT, which matters more for Apollo iOS than for most client libraries, because the caching layer is the part you are most tempted to fork or wrap in a hosted commercial product.

## Two release lines published on the same afternoon

The three most recent tags are the first thing that tells you this is a mature project under active stewardship rather than a rewrite in progress. Version 2.4.0 landed on 2026-08-20. Then, on 2026-07-21, the project published 2.3.0 and 1.25.7 within seconds of each other.

That pairing is the whole story of the project's release discipline. A v1 line kept receiving patches after v2 shipped, and the same improvement was ported to both. The clearest example is `DatabaseRow`: in 1.25.7 its stored properties, `cacheKey` and `storedInfo`, were made public, complementing a public initializer added earlier. In 2.4.0 the equivalent work landed for the v2 line, surfacing the required members through the public `SQLiteDatabase` protocol. The changelog for 2.4.0 says outright that this brings the v1 improvement forward, which is a maintainer telling you the two lines share design intent rather than having drifted.

Doing this is not free. Two active lines means every cache fix, every codegen option, and every bug report gets triaged twice, and a reader who only looks at the highest tag number will miss a security or correctness fix that only landed on 1.x. Projects that adopted Apollo iOS before v2 have a real decision here, and the release history shows the maintainers expect that decision to exist rather than pretending v1 is frozen.

The repository itself carries a second small signal: release notes and CI badges point at an `apollo-ios-dev` path while the package URL and tree resolve to `apollo-ios`. The development branch was renamed at some point and the automation followed only partially. It is cosmetic, but it is the sort of thing that sends a new contributor looking for a second repository that does not exist.

## The cache is an open protocol, and that changes what you can do

The README offers caching in one line, describing intelligent in-memory or SQLite operation out of the box. The release notes are where the actual design becomes visible, because they spend two separate releases widening a single seam: the `SQLiteDatabase` protocol.

Before those changes, `DatabaseRow` was reachable through the public protocol but its stored properties were not publicly accessible. That sounds like a trivial access modifier and is not. If you cannot read a row's contents, you cannot write a wrapper that inspects or transforms them, which rules out the four use cases the changelog names: encryption, compression, logging, and metrics. With the properties public, a wrapper implementation can compose around `ApolloSQLiteDatabase` rather than reimplementing it, reading rows, doing its work, and handing them back. The 1.25.7 entry is explicit that this expands public API surface and introduces no behavioral changes.

That sequence, public initializer first, public properties later, is the signature of a protocol that was designed for substitution but only became substitutable in practice once the access levels caught up. It is worth knowing which side of that line your current version falls on: if you are wrapping the SQLite store for encryption, the version you pin matters more than the version you read about.

The `@defer` fix in 2.3.0 belongs to the same layer. When an incremental payload streamed deferred chunks, the deferred path was converted into a cache key by joining the root key with the raw path components, and that naive join produced the wrong key for nested results. It is exactly the class of bug a normalized cache is supposed to prevent, and it is also the kind of bug that only appears once you use `@defer` against a schema deep enough to nest. The fix resolves deferred paths against the initial cache records instead of the raw components.

## Codegen naming is an opt in layer, added complaint by complaint

The most recent feature work is not about networking or caching but about what the generated Swift identifiers look like. Version 2.3.0 added an opt in `additionalCapitalizationRules` codegen option along with a public `CapitalizationRule` API, and the design decisions inside it are careful enough to be worth reading.

A `CapitalizationRule` is a `Term`, either `.string` or `.regex`, paired with a `CaseStrategy` of `.upper` or `.lower`. The motivating case is the standard GraphQL to Swift mismatch: a schema field called `userId` should become `userID`, because Swift style capitalizes acronyms and the naive lowercase-everything-after-the-first-letter transform produces `userId` instead. The rules then apply across field accessors, initializer parameters, enum cases, input fields, and test mocks, which is the list you would write yourself if you wanted the naming consistent.

Two details matter more than the feature. First, the capitalization follows SwiftFormat semantics, where an acronym is only capitalized when its first character is already uppercase. So `userId` becomes `userID` but `apiKey` is left alone. Without that rule a blanket transform would mangle ordinary camelCase identifiers across every model in the project. Second, the option defaults to empty, so existing generated output does not change. A codegen tool that renames your properties by default is a migration, not a version bump, and this one avoids forcing that.

The 2.4.0 follow on extends the same rules to generated operation and fragment type names, having previously covered property level names only. Read as a series, these two releases show the maintainers responding to a long standing issue by adding a layer that is off by default, scoped narrowly, and extended only after the first version settled. For a team with a large generated surface, that is the friendly shape: turn it on when you are ready for the rename, and control exactly which terms it touches.

## Debugging, tooling, and the honest limits of the README

The tooling story is mostly external, and the README links out for all of it. There is a VS Code extension for the editor experience, a Chrome and Firefox build of Apollo Client DevTools for inspecting queries and cache state, a free course covering codegen and queries, and a generated API reference hosted on the documentation site. There is also an agent skill published for AI assistants, which is a newer addition than the rest of the page and suggests the project is thinking about how its own codegen is consumed by tools other than humans.

What the README does not contain is any actual usage example. Both code fences on the page are package manifest fragments, not a query, a mutation, or a cache read. That is a deliberate choice rather than an omission, since the documentation site is linked as the place for complete setup and a first query, but it means the repository itself will not tell you how the client is meant to be held, whether it is a singleton, or how watch queries are cancelled. A reader deciding between this and a lighter GraphQL wrapper has to leave the repo for that.

Two other things the repository does settle well. The project maintains a public `ROADMAP.md` in the root for work in progress and upcoming features, which is more than most client libraries bother with. And it carries `CodegenProposal.md` as a design document in the root, which suggests codegen is treated as a component with its own proposal history rather than a build script nobody reads.

The repository is not archived, the default branch is `main`, and the last recorded push was on 2026-09-11, with 89 open issues against 4035 stars. The maintainers are named in the README, four of them, which is a small signal about who to expect answers from when something in the cache layer goes wrong.

## Conclusion

Apollo iOS is worth reading as two things wearing one library name. The codegen pipeline is the part with real opinions, and the recent releases show it absorbing Swift naming complaints one at a time through an opt in rules layer. The cache is the part with genuine extension points, made possible by a public SQLiteDatabase protocol that the 1.x and 2.x lines both widened in the same week. Start with the generated models in a small target, since that is the dependency most projects actually need, and turn on the naming rules before your identifiers spread across the codebase.

## FAQ

### What is Apollo iOS used for?

Apollo iOS is a strongly typed, caching GraphQL client written in Swift for iOS, macOS, watchOS, tvOS, and visionOS. It generates model types from your schema at build time and caches responses in memory or SQLite so a screen can render from cached data before a network response arrives.

### How do you add Apollo iOS to a Swift project?

Add the package with Swift Package Manager, pinned to the next major from 2.0.0, then link the `Apollo` product from any target that constructs an `ApolloClient`. Targets that only reference generated models do not need the product linked at all.

### What license is Apollo iOS released under?

Apollo iOS is released under the MIT license. That is worth noting for the SQLite caching layer specifically, because the public `SQLiteDatabase` protocol is designed to be wrapped for encryption, compression, logging, or metrics without reimplementing Apollo's own store.

### Does Apollo iOS still get updates on the 1.x line?

Yes. Version 1.25.7 was published on 2026-07-21, minutes after 2.3.0 on the same day, and the 2.4.0 notes describe porting a v1 cache improvement forward. Projects pinned to v1 should check that line's changelog rather than assuming fixes only land on v2.

## Sources

- [apollographql/apollo-ios on GitHub](https://github.com/apollographql/apollo-ios)
- [License: MIT](https://github.com/apollographql/apollo-ios/blob/main/LICENSE)
- [Project website](https://www.apollographql.com/docs/ios/)
- [README](https://github.com/apollographql/apollo-ios/blob/main/README.md)
- [Releases](https://github.com/apollographql/apollo-ios/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/apollographql-apollo-ios
