Skip to content

Releases: apollographql/apollo-ios

Apollo iOS 2.0.0 Alpha 1 - First Preview Release

01 Jul 21:21
Compare
Choose a tag to compare

This is the first preview release of Apollo iOS 2.0. This preview release contains APIs that are still in development and are subject to change prior to stable release.

This version is likely to contain bugs and some features are still limited. This preview is intended to allow interested users to test out the new APIs and provide feedback to help shape the final product.

Feedback

We are looking for bug reports as well as use cases that may not be supported by the current APIs. Any general feedback on the project is welcome as well. Bug reports can be filed as GitHub issues. For feature requests and general feedback, please comment on the 2.0 RFC Megathread.

Web Socket & Pagination Support Not Included

Support for web sockets is not included in this preview release and will be implemented prior to the first Beta release. In the interim, WebSocketNetworkTransport has been temporarily replaced with a stubbed type that throws an error. Subscriptions are still supported over HTTP via the RequestChainNetworkTransport.

Support for pagination using the ApolloPagination package is not included in this preview release and will be implemented prior to the first Beta release.

Installation

This preview is available now under the tag 2.0.0-alpha-1. To try out the alpha, modify your SPM dependency to:

.package(
      url: "https://github.com/apollographql/apollo-io.git", Version(2, 0, 0, prereleaseIdentifiers: ["alpha"]),

Temporary Deprecations

Many of the existing APIs from Apollo iOS 1.0 have been marked as deprecated, with only the minimal necessary modifications to compile Apollo iOS 2.0. These APIs are untested with the new underlying infrastructure and may not be reliable. All deprecated APIs will be removed prior to the stable release of 2.0. These APIs still exist only to aid users in the migration to the new APIs. By deprecating these APIs instead of just removing them, we hope that it will make it easier to incrementally migrate your codebase to Apollo iOS 2.0.

Key Changes

Apollo iOS 2.0 reimagines many of the APIs to take full advantage of the new Swift concurrency model. This is a non-exhaustive list of the key changes:

ApolloClient & CachePolicy

The APIs of ApolloClient have changed significantly to use async/await. Rather than providing a resultHandler closure that may be called one or more times, separate APIs are defined depending on if an operation expects single/multiple responses. CachePolicy has been broken up into multiple types that will automatically force the function with the correct return signature.

// Single response
let response = try await client.fetch(query: query, cachePolicy: .cacheFirst)
let response = try await client.fetch(query: query, cachePolicy: .networkFirst)
let response = try await client.fetch(query: query, cachePolicy: .networkOnly)

// Single response with Optional return value
let response = try await client.fetch(query: query, cachePolicy: .cacheOnly)

// Multiple responses
// Returns an AsyncThrowingStream<GraphQLResponse<Query>, any Swift.Error>
let responses = try client.fetch(query: query, cachePolicy: .cacheAndNetwork)

Task {
  for try await response in responses {
    // Handle response
  }
}

Subscriptions and operations that provide incremental data (via the @defer directive and in the future @stream), will always return an AsyncThrowingStream<GraphQLResponse<Query>, any Swift.Error> of responses unless using the .cacheOnly policy.

let responses = try client.fetch(query: deferQuery, cachePolicy: .cacheFirst) // -> AsyncThrowingStream
let responses = try client.fetch(query: deferQuery, cachePolicy: .networkFirst) // -> AsyncThrowingStream
let responses = try client.fetch(query: deferQuery, cachePolicy: .networkOnly) // -> AsyncThrowingStream
let responses = try client.fetch(query: deferQuery, cachePolicy: .cacheAndNetwork)

Task {
  for try await response in responses {
    // Handle response
  }
}

let response = try await client.fetch(query: deferQuery, cachePolicy: .cacheOnly) // async throws -> GraphQLResponse<DeferQuery>?

The for try await response in responses loop will continue to run until the operation is complete. For subscriptions, this may be indefinite. For this reason, the returned stream should be consumed within a Task.

Sendable Types

In order to support the new Swift concurrency model, most of the types in Apollo iOS have been made Sendable. In order to make these types Sendable, some limitations were necessary.

  • Some fields that were mutable var properties have been converted to constant let properties. We don't believe this should prevent users from accessing any necessary functionality, but we are seeking feedback on the effect this change has on your usage.
  • Public open classes have been changed to final classes or structs. This prevents subclassing types such as RequestChainNetworkTransport, InterceptorProvider, JSONRequest, and others. If you are currently subclassing these types, you will need to convert your existing subclasses to wrappers that wrap these types and passthrough calls to them instead.

New Request Interceptor Framework

The RequestChain and interceptor framework has been completely reimagined. The new version supports async/await and provides the ability to interact with the request at each step within the chain more safely with more explicit APIs.

If you are providing your own custom InterceptorProvider with your own interceptors, you will need to modify your code to utilize these new APIs.

The singular ApolloInterceptor that was used to handle any step of the request chain has been broken up into discrete interceptor types for different portions of request execution. Additionally, requests are sent down the request chain pre-flight and then back up the chain post-flight, allowing each interceptors to interact with the both the request and response in a type-safe way.

Interceptors Types

ApolloInterceptor has been separated into 4 different interceptor types.

  • GraphQLInterceptor
    • Can inspect and mutate the GraphQLRequest and GraphQLResponse
  • HTTPInterceptor
    • Can inspect and mutate the URLRequest
    • After network response can inspect the HTTPURLResponse (readonly) and mutate the actual raw response Data prior to parsing
  • CacheInterceptor
    • Handles read/write of cache data
    • Read currently runs before GraphQLInterceptors (not sure if that is the desired behavior, we should discuss)
    • Write runs after parsing
  • ResponseParsingInterceptor
    • Handles the parsing of the response Data into the GraphQLResponse

NetworkFetchInterceptor is no longer used, as the network fetch is managed by the ApolloURLSession. See the section on ApolloURLSession for more information.

Request Chain Flow

Requests are now processed by the RequestChain using the following flow:

  • GraphQLInterceptors receive and may mutate Request
  • Cache read executed via CacheInterceptor if necessary (based on cache policy)
  • GraphQLRequest.toURLRequest() called to obtain URLRequest
  • HTTPInterceptors receive and may mutate URLRequest
  • ApolloURLSession handles networking with URLRequest
  • HTTPInterceptors receive stream of HTTPResponse objects for each chunk & may mutate raw chunk Data stream
  • ResponseParsingInterceptor receives HTTPResponse and parses data chunks into stream of GraphQLResponse
  • GraphQLInterceptors receive and may mutate GraphQLResponse with parsed GraphQLResult and (possibly) cache records.
  • Cache write executed via CacheInterceptor if necessary (based on cache policy)
  • GraphQLResponse emitted out to NetworkTransport

GraphQLResponse and HTTPResponse separated

Previously, there was a single GraphQLResponse which included the HTTPResponse and optionally the ParsedResult (if the parsing interceptor had been called already). Now, since different interceptors will be called pre/post parsing, we have separate types for these response objects.

Replacing ApolloErrorInterceptor

The ApolloErrorInterceptor protocol has been removed. Instead, any GraphQLInterceptor can handle errors using .mapErrors(). If any following interceptors, or the ApolloURLSession throw an error, the mapErrors closures will be called. You can then re-throw it; throw a different error; or trigger a retry by throwing a RequestChain.Retry error. If you would like to use a dedicated error handling interceptor, it is recommended to place it as the first interceptor returned by your provider to ensure all errors thrown by the chain are handled.

RequestChain.Retry

Interceptors are no longer provided a reference to the RequestChain, so they cannot call RequestChain.retry(request:) directly. Instead, any interceptor may throw a RequestChain.Retry error that contains the request to kick-off the retry with. This error is caught internally by the RequestChain which initiates a retry.

Network Fetching

The network fetch is now managed by an ApolloURLSession provided to the ApolloClient. For your convenience, Foundation.URLSession already conforms to the ApolloURLSession protocol. This allows you to provide your own URLSession and have complete control over the session's configuration and delegate.

You may alternatively provide any other object that conforms to ApolloURLSession, wrapping the URLSession or providing an entirely separate networking stack.

Protocols Require async Functions

Many of the public protocols in Apollo iOS have been modified to use async functions. If you have custom implementations of these types, they will need to b...

Read more

1.23.0

27 Jun 00:39
Compare
Choose a tag to compare

New

  • Added requireNonOptionalMockFields flag to ApolloCodegenConfiguration.OutputOptions. (#669): Added new flag to codegen output options to allow having non-optional fields in the test mocks if desired. Thank you to @dwroth for the contribution.

Improvement

  • Added public initializer to DatabaseRow. (#664): Not having a public initializer on DatabasRow was hindering the ability to create custom SQLiteDatabase implementations. This solves that by adding a public initializer to DatabaseRow.Thank you to @ChrisLaganiere for the contribution.

Fixed

  • Unncessary deprecation warning in codegen options initializer. (#3563): Added @_disfavoredOverload to the deprecated initialized in ApolloCodegenConfiguration to prevent possible warnings caused by the compiler selecting a deprecated initializer versus the new/current initializer. See PR #682. Thank you to @CraigSiemens for raising the issue.

1.22.0

30 May 20:12
Compare
Choose a tag to compare

Improvement

  • Make cache public within ReadTransaction (#661): Some users have use cases for accessing a custom NormalizedCache implementation directly while performing cache transactions. A new ReadOnlyNormalizedCache protocol exposes the cache as read-only in the ReadTransaction and as writable in the ReadWriteTransaction. See PR #661.

Fixed

  • Multiple deprecation warning directives not compiling (#3559): Codegen would generate an incorrect list-style character between the Swift deprecation annotations when using multiple deprecation directives in GraphQL. See PR #658. Thank you to @guilherme-anchorage for raising the issue.
  • Non-all field merging causes selection set initializers to stop being generated for local cache mutations (#3554): Codegen will now force field merging behaviour and selection set initializer generation for local cache mutations. See PR #654.
  • Referenced fragments within a local cache mutation operation are generated as mutable (#3557): Any fragments referenced within a local cache mutation will now be generated as mutable too, including any fragments within those fragments. See PR #659.

1.21.0

29 Apr 23:01
Compare
Choose a tag to compare

New

  • Enhanced Client Awareness (#638): Apollo iOS now sends the library name and version as metadata in the extensions key of each request. This Enhanced Client Awareness metric is collected in GraphOS along with the existing Client Awareness and general operation metrics.

Improvement

  • Removed SQLite.swift dependency (#635): Removed the dependency on SQLite.swift and replaced it with direct interaction with the SQLite C API.

Fixed

  • Fix possible data races in the WebSocketTransport (#636): Fixes possible data race issues in the subscriptions property inside of WebSocketTransport. Thank you to @tahirmt for the contribution.
  • Fix cache reading of null list items (#3527): Null list items would previously generate a wrongType error if stored and read from the cache. This refactors the execution logic to correctly handle values from cache references in lists. See PR #637.

1.20.0

16 Apr 20:26
Compare
Choose a tag to compare

Fixed

  • Location of CLI download script changed in Xcode 16.3 (#3518): Xcode 16.3 changed the execution directory for plugins. This is fixed and will work for both < 16.3 and >= 16.3 Xcode versions. See PR #623. Thank you to @robb for raising the issue.

Improvement

  • More contextual multipart parsing errors (#3536): Apollo iOS will now throw narrower scoped errors when a multipart message cannot be parsed. This will help in determining which part of the response is causing the issue. See PR #628. Thank you to @GRajin for raising the issue.

1.19.0

26 Mar 19:03
Compare
Choose a tag to compare

New

  • New function to mutate the properties of a local cache mutation fragment. (#3433): Removal of the setter for type conditions made it difficult to work with the properties on those types. A new mutateIfFulfilled function was added to facilitate that workflow while still preventing a fragment from being added or removed from an existing model. See PR #608.
  • Configure URLRequest timeout interval (#3522): Added a request context specialization protocol (RequestContextTimeoutConfigurable) that specifies options for configuring the timeout interval of a URLRequest. See PR #618.

1.18.0

18 Feb 21:59
Compare
Choose a tag to compare

New

  • Reduce Generated Schema Types (#3505): Adds a new codegen configuration option to reduce the number of Object types that are generated so that only types that are referenced in an operation document or have a @typePolicy will be generated. See PR #601.

Improvement

  • Identifiable conformance for named fragments (#595): Identifiable conformance was previously implemented (#584) for selection sets and has now been extended to include named fragments. Thank you to @x-sheep for the contribution.

Fixed

  • Accessing an unset deprecated field in input causes a crash (#3506): InputObject needed a GraphQLNullable-specific subscript to prevent nil value keys being forcefully unwrapped. See PR #596. Thank you to @pixelmatrix for raising the issue.
  • Crash in WebSocketTransport due to data races (#3512): This data race would occur if starting or stopping a subscription at the same time as a message received on the websocket. To prevent these data races the subscribers property is now an @Atomic property. See PR #599. Thank you to @tahirmt for the contribution.

1.17.0

28 Jan 01:09
Compare
Choose a tag to compare

New

  • Add suffix to schema type filenames (#2598): When fragments were named the same as schema types code generation would produce two files with the same name, but at different paths, for each respective type. This would cause a build error in Xcode. There is a new codegen configuration option (appendSchemaTypeFilenameSuffix) to add a suffix to schema generated filenames and prevent the build error. See PR #580.
  • Specify caching fields with typePolicy directive (#554): The @typePolicy directive lets you specify an object's cache ID using key fields of the response object. See the documentation for full details. Thank you to @x-sheep for the contribution.
  • Emit Identifiable conformance on SelectionSet (#584): If the @typePolicy of a type uses a keyField of id the selection set will emit conformance to Swifts Identifiable protocol. Thank you to @x-sheep for the contribution.

Improvement

  • Improved performance of code generation on operations with many nested fragments (#3434): When fragment field merging is disabled the fragment selection trees are no longer merged into the EntitySelectionSet while building operations. See PR #571.

Fixed

  • Defer metadata extension (#3505): Metadata extensions for deferred selection sets were incorrectly generated inside the namespace extension for embeddedInTarget and other module types. See PR #581.
  • DataDict initialization of deferredFragments for named fragments (#587): When deferred fragments are named fragments the deferred type should be the fragment generated definition name.

1.16.1

21 Jan 21:10
Compare
Choose a tag to compare

Fixed

  • Web socket data race crash fixed (#578): A data race in the web socket layer was causing crashes in some rare circumstances.

  • Added support for GraphQL over HTTP media type(#558): Apollo iOS now supports the content-type header with a type of application/graphql-response+json.

1.16.0

09 Jan 00:16
Compare
Choose a tag to compare

New

  • Added codegen config support for spm module type versions (#539): There is a new codegen config option for supplying a version to the SPM module type to allow for pointing to specific branches or local versions of Apollo iOS.
  • @oneOf input object support (#537): Adding support for @OneOf Input Objects, more info can be found in the official RFC.

Improvements

  • URLRequest cache policy default changed (#550): The updated default closer matches the original behaviour before the introduction of setting URLRequest.CachePolicy. Thank you to @marksvend for raising the issue.

Fixed

  • DataDict initialization of deferredFragments property (#557): Generated selection set initializers were not correctly setting deferred fragment identifiers. This only affected selection sets that were instantiated with the generated selection set initializers, response-based results are unaffected.
  • Multipart chunk content type (#572): Multipart response parsing would produce an error when the chunk content type contained more than one directive. Thank you to @brettephillips for raising the issue.