diff --git a/Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift b/Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift index 77fdbd4f67..96241ce547 100644 --- a/Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift +++ b/Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift @@ -286,7 +286,7 @@ public struct ApolloCodegenConfiguration: Codable, Equatable { /// Generated test mock files will be located in the specified path. /// No module will be created for the generated test mocks. /// - ///- Note: Generated files must be manually added to your test target. Test mocks generated + /// - Note: Generated files must be manually added to your test target. Test mocks generated /// this way may also be manually embedded in a test utility module that is imported by your /// test target. case absolute(path: String) diff --git a/Tests/TestCodeGenConfigurations/EmbeddedInTarget-InSchemaModule/Package.resolved b/Tests/TestCodeGenConfigurations/EmbeddedInTarget-InSchemaModule/Package.resolved index b129b8364c..4d4b7171e0 100644 --- a/Tests/TestCodeGenConfigurations/EmbeddedInTarget-InSchemaModule/Package.resolved +++ b/Tests/TestCodeGenConfigurations/EmbeddedInTarget-InSchemaModule/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { - "revision" : "9f39744e025c7d377987f30b03770805dcb0bcd1", - "version" : "1.1.4" + "revision" : "fddd1c00396eed152c45a46bea9f47b98e59301d", + "version" : "1.2.0" } }, { diff --git a/docs/.vscode/settings.json b/docs/.vscode/settings.json new file mode 100644 index 0000000000..5f3108a993 --- /dev/null +++ b/docs/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "markdown.extension.toc.updateOnSave": false +} \ No newline at end of file diff --git a/docs/shared/cli-install/pods.mdx b/docs/shared/cli-install/pods.mdx new file mode 100644 index 0000000000..1544c3450b --- /dev/null +++ b/docs/shared/cli-install/pods.mdx @@ -0,0 +1,7 @@ +When using Cocoapods, Apollo iOS compiles the Codegen CLI into an executable shell application during `pod install`, which is located in your project at `Pods/Apollo/apollo-ios-cli`. + +After installing the Apollo iOS pod, you can run the Codegen CLI from the directory of your `Podfile`: + +```bash +./Pods/Apollo/apollo-ios-cli ${Command Name} -${Command Arguments} +``` \ No newline at end of file diff --git a/docs/shared/spm-install-cli.mdx b/docs/shared/cli-install/spm-xcode.mdx similarity index 80% rename from docs/shared/spm-install-cli.mdx rename to docs/shared/cli-install/spm-xcode.mdx index 3a5712d9db..f1b6396270 100644 --- a/docs/shared/spm-install-cli.mdx +++ b/docs/shared/cli-install/spm-xcode.mdx @@ -4,15 +4,9 @@ To simplify accessing the Codegen CLI, you can run the included `InstallCLI` SPM This plugin builds the CLI and creates a symbolic link to the executable in your project root. -When using a `Package.swift` file, install the CLI by running: - -```bash -swift package --allow-writing-to-package-directory apollo-cli-install -``` - When using Swift packages through Xcode, right-click on your project in the Xcode file explorer and at the bottom of the menu you will find the `InstallCLI` plugin command. Clicking on this will present a dialog asking for permission for the plugin to write to your project directory. -Where to find the SPM plugin commands in Xcode +Where to find the SPM plugin commands in Xcode After running the installation plugin, a symbolic link to the Codegen CLI named `apollo-ios-cli` is located in your project root folder. You can now run the CLI from the command line with `./apollo-ios-cli`. diff --git a/docs/shared/cli-install/spm.mdx b/docs/shared/cli-install/spm.mdx new file mode 100644 index 0000000000..6d8c1ccb47 --- /dev/null +++ b/docs/shared/cli-install/spm.mdx @@ -0,0 +1,15 @@ +The Apollo iOS SPM package includes the Codegen CLI as an executable target. This ensures you always have a valid CLI version for your Apollo iOS version. + +To simplify accessing the Codegen CLI, you can run the included `apollo-cli-install` SPM plugin. + +This plugin builds the CLI and creates a symbolic link to the executable in your project root. + +When using a `Package.swift` file, install the CLI by running: + +```bash +swift package --allow-writing-to-package-directory apollo-cli-install +``` + +After running the installation plugin, a symbolic link to the Codegen CLI named `apollo-ios-cli` is located in your project root folder. You can now run the CLI from the command line with `./apollo-ios-cli`. + +> **Note:** Because the `apollo-ios-cli` in your project root is only a symbolic link, it will only work if the compiled CLI exectuable exists. This is generally located in your Xcode Derived Data or the `.build` folder. If these are cleared, you can run the install plugin again to re-build the CLI executable. diff --git a/docs/shared/pods-setup-codegen-panel.mdx b/docs/shared/pods-setup-codegen-panel.mdx deleted file mode 100644 index 689dbb05d0..0000000000 --- a/docs/shared/pods-setup-codegen-panel.mdx +++ /dev/null @@ -1,63 +0,0 @@ - - -Apollo iOS compiles the Codegen CLI during `pod install`. This ensures you always have a valid CLI version for your Apollo iOS version. - -The Codegen CLI is located in your project at `Pods/Apollo/apollo-ios-cli` - - - - -#### Initialize the code generation configuration - -The Codegen CLI uses a JSON file to configure the code generation engine. Use the Codegen CLI `init` command to create this file with default values. From the directory of your `Podfile`, run: - -```bash -./Pods/Apollo/apollo-ios-cli init --schema-name ${MySchemaName} --module-type other -``` - -* `${MySchemaName}` should be the name you want for the namespace of your generated schema files. -* `embeddedInTarget` or `other` are both suitable module types to use when your dependency manager for Apollo iOS is CocoaPods. - -This will create an `apollo-codegen-config.json` file with the default values. - - - - -#### Configure code generation options - -Open the `apollo-codegen-config.json` file to configure code generation for your project. - -The default configuration will: -- Find all GraphQL schema files ending with the file extension `.graphqls` within your project directory. -- Find all GraphQL operation and fragment definition files ending with the file extension `.graphql` within your project directory. -- Generate Swift code for the schema types in a directory with the `schema-name` provided. -- Generate Swift code for the operation and fragment models relative to the `.graphql` files that define them. - - - - -#### Run code generation - -From the directory of your `Podfile` file, run: - -```bash -./Pods/Apollo/apollo-ios-cli generate -``` - -Your generated files will be created with the file extension `.graphql.swift`. - - - - -#### Add the generated schema and operation files to your target - -By default, a directory containing your generated schema files will be located in a directory with the `schema-name` you provided; your generated operation and fragment files will be in the same location as the `.graphql` files they are defined by. - -When using Cocoapods, you will need to manually add the generated files to your target. - -> **Note:** Because adding generated files to your Xcode targets must be done manually each time you generate new files, we highly recommend defining your project targets with SPM. For more information see the documentation for [Code Generation Confiugration](./codegen-configuration). - - - - - diff --git a/docs/shared/separate-local-cache-mutation-note.mdx b/docs/shared/separate-local-cache-mutation-note.mdx new file mode 100644 index 0000000000..79f537b5a7 --- /dev/null +++ b/docs/shared/separate-local-cache-mutation-note.mdx @@ -0,0 +1,7 @@ +> #### Separating Cache Mutations from Network Operations +> +> The generated models for queries defined as cache mutations will conform to `LocalCacheMutation`, but not `GraphQLQuery`. This means they cannot be sent as query operations to your server using an `ApolloClient`. Because network response data is immutable and cache mutation models are mutable, you must use separate models. +> +> Because mutable data requires a lot more generated code, generating mutable models for all operations would nearly double the size of the generated operations. Additionally, if the models were mutable, mutating them outside of a `ReadWriteTransaction` would not persist any changes to the cache. By maintaining immutable models, we avoid any confusion this could cause. +> +> Cache mutations are designed to be narrowly scoped to access and mutate only the necessary data. You should avoid creating mutable versions of entire query operations. Instead, define mutable fragments or queries to mutate only the necessary fields. \ No newline at end of file diff --git a/docs/shared/setup-codegen/combined.mdx b/docs/shared/setup-codegen/combined.mdx new file mode 100644 index 0000000000..b1e3441323 --- /dev/null +++ b/docs/shared/setup-codegen/combined.mdx @@ -0,0 +1,25 @@ +import SPMInstallCLI from "../cli-install/spm.mdx" +import SPMXcodeInstallCLI from "../cli-install/spm-xcode.mdx" +import PodsInstallCLI from "../cli-install/pods.mdx" +import SetupCodegenPanel from "./single-panel.mdx" + + + + +} /> + + + + + + +} /> + + + + + + +} /> + + \ No newline at end of file diff --git a/docs/shared/spm-setup-codegen-panel.mdx b/docs/shared/setup-codegen/single-panel.mdx similarity index 60% rename from docs/shared/spm-setup-codegen-panel.mdx rename to docs/shared/setup-codegen/single-panel.mdx index 062ebd8dde..5ee92250ab 100644 --- a/docs/shared/spm-setup-codegen-panel.mdx +++ b/docs/shared/setup-codegen/single-panel.mdx @@ -1,6 +1,4 @@ -import SPMInstallCLI from "../shared/spm-install-cli.mdx" - - +import {CodeBlock, CodeBlockProps} from "@apollo/chakra-helpers" @@ -8,7 +6,7 @@ import SPMInstallCLI from "../shared/spm-install-cli.mdx" #### Install the Codegen CLI - +<>{props.installComponent} @@ -16,14 +14,22 @@ import SPMInstallCLI from "../shared/spm-install-cli.mdx" #### Initialize the code generation configuration -The Codegen CLI uses a JSON file to configure the code generation engine. Use the Codegen CLI `init` command to create this file with default values. From your project's root directory, run: +The Codegen CLI uses a JSON file to configure the code generation engine. Use the Codegen CLI `init` command to create this file with default values. + +From your project's root directory, run: -```bash -./apollo-ios-cli init --schema-name ${MySchemaName} --module-type swiftPackageManager -``` + -* `${MySchemaName}` should be the name you want for the namespace of your generated schema files. -* `embeddedInTarget` or `swiftPackageManager` are both suitable module types to use when your dependency manager for Apollo iOS is Swift Package Manager. +- `${MySchemaName}` should be the name you want for the namespace of your generated schema files. +- `${ModuleType}` is used to configure how your generated schema types will be included in your project. + > This is an important decision that must be made prior to configuring code generation. To determine the right option for your project, check our our [project configuration documentation](/project-configuration). + > + > To get started quickly, you can use the `.embeddedInTarget` option. + > If you use this option you will need to also supply a target name using the `--target-name` command line option. This will create an `apollo-codegen-config.json` file with the default values. @@ -38,7 +44,7 @@ The default configuration will: - Find all GraphQL schema files ending with the file extension `.graphqls` within your project directory. - Find all GraphQL operation and fragment definition files ending with the file extension `.graphql` within your project directory. - Generate Swift code for the schema types in a directory with the `schema-name` provided. -- Generate Swift code for the operation and fragment models relative to the `.graphql` files that define them. +- Generate Swift code for the operation and fragment models in a subfolder within the schema types output location. @@ -47,9 +53,11 @@ The default configuration will: From your project's root directory, run: -```bash -./apollo-ios-cli generate -``` + Your generated files will be created with the file extension `.graphql.swift`. @@ -58,12 +66,11 @@ Your generated files will be created with the file extension `.graphql.swift`. #### Add the generated schema and operation files to your target -By default, a directory containing your generated schema files will be located in a directory with the `schema-name` you provided; your generated operation and fragment files will be in the same location as the `.graphql` files they are defined by. +By default, a directory containing your generated schema files will be located in a directory with the `schema-name` you provided; your generated operation and fragment files will be in a subfolder in that same directory. If your target is created in an Xcode project or workspace, you will need to manually add the generated files to your target. -> **Note:** Because adding generated files to your Xcode targets must be done manually each time you generate new files, we highly recommend defining your project targets with SPM. Alternatively, you can generate your operations into the package that includes your schema files. For more information see the documentation for [Code Generation Configuration](./codegen-configuration). +> **Note:** Because adding generated files to your Xcode targets must be done manually each time you generate new files, we highly recommend defining your project targets with SPM. Alternatively, you can generate your operations into the package that includes your schema files. For more information see the documentation for [Code Generation Configuration](./../../code-generation/codegen-configuration). - diff --git a/docs/shared/spm-package-installation-panel.mdx b/docs/shared/spm-package-installation-panel.mdx index 17b2ab78b9..3521940a78 100644 --- a/docs/shared/spm-package-installation-panel.mdx +++ b/docs/shared/spm-package-installation-panel.mdx @@ -9,7 +9,10 @@ If your project uses its own `Package.swift` file, you can add Apollo iOS as a d ```swift title="Package.swift" dependencies: [ - .package(url: "https://github.com/apollographql/apollo-ios.git", .upToNextMajor(from: "1.0.0")), + .package( + url: "https://github.com/apollographql/apollo-ios.git", + .upToNextMajor(from: "1.0.0") + ), ], ``` diff --git a/docs/source/caching/cache-transactions.mdx b/docs/source/caching/cache-transactions.mdx index 6a59f023f7..9aacd88c11 100644 --- a/docs/source/caching/cache-transactions.mdx +++ b/docs/source/caching/cache-transactions.mdx @@ -2,6 +2,8 @@ title: Direct cache access --- +import SeparateLocalCacheMutationsNote from "../../shared/separate-local-cache-mutation-note.mdx" + Apollo iOS provides the ability to directly read and update the cache as needed using type-safe generated operation models. This provides a strongly-typed interface for accessing your cache data in pure Swift code. The `ApolloStore` has APIs for accessing the cache via both `ReadTransaction` and `ReadWriteTransaction`. @@ -73,13 +75,7 @@ fragment MutableHeroDetails on Hero The generated models for your mutations will have mutable fields (`var` instead of `let`). Generating both getters and setters for fields on the mutable models means that they are larger than immutable generated models. -> #### Seperating Cache Mutations from Network Operations -> -> The generated models for queries defined as cache mutations will conform to `LocalCacheMutation`, but not `GraphQLQuery`. This means they cannot be sent as query operations to your server using an `ApolloClient`. Because network response data is immutable and cache mutation models are mutable, you must use seperate models. -> -> Because mutable data requires a lot more generated code, generating mutable models for all operations would nearly double the size of the generated operations. Additionally, if the models were mutable, mutating them outside of a `ReadWriteTransaction` would not persist any changes to the cache. By maintaining immutable models, we avoid any confusion this could cause. -> -> Cache mutations are designed to be narrowly scoped to access and mutate only the necessary data. You should avoid creating mutable versions of entire query operations. Instead, define mutable fragments or queries to mutate only the necessary fields. + ### Writing local cache mutations to the cache diff --git a/docs/source/code-generation/codegen-cli.mdx b/docs/source/code-generation/codegen-cli.mdx index cfd925b5c1..4acf2f0320 100644 --- a/docs/source/code-generation/codegen-cli.mdx +++ b/docs/source/code-generation/codegen-cli.mdx @@ -2,7 +2,9 @@ title: The Codegen CLI --- -import SPMInstallCLI from "../../shared/spm-install-cli.mdx" +import SPMInstallCLI from "../../shared/cli-install/spm.mdx" +import SPMXcodeInstallCLI from "../../shared/cli-install/spm-xcode.mdx" +import PodsInstallCLI from "../../shared/cli-install/pods.mdx" The Codegen CLI provides a command line tool that streamlines the process of running code generation. The CLI can be ran manually from Terminal (or any other shell program) or can be called into from bash scripts. @@ -21,21 +23,21 @@ When Apollo iOS is included as a dependency through Swift Package Manager (SPM) To learn how to run the Codegen CLI with your chosen package manager, open the appropriate section: - + - + -When using Cocoapods, Apollo iOS compiles the Codegen CLI into an executable shell application during `pod install`, which is located in your project at `Pods/Apollo/apollo-ios-cli`. + -After installing the Apollo iOS pod, you can run the Codegen CLI from the directory of your `Podfile`: + -```bash -./Pods/Apollo/apollo-ios-cli ${Command Name} -${Command Arguments} -``` + + + diff --git a/docs/source/code-generation/codegen-configuration.mdx b/docs/source/code-generation/codegen-configuration.mdx index be7d771563..5bc0818618 100644 --- a/docs/source/code-generation/codegen-configuration.mdx +++ b/docs/source/code-generation/codegen-configuration.mdx @@ -148,7 +148,7 @@ The properties to configure `output.schemaTypes` are: | Property Name | Description | | ------------- | ----------- | | `path` | A file path where the generated schema types should be output.

Relative to your project root | -| [`moduleType`](#moduleType) | How generated schema types will be linked to your project. | +| [`moduleType`](#module-type) | How generated schema types will be linked to your project. | #### Module type @@ -166,21 +166,21 @@ The possible values for `moduleType` are: | Value | Description | | ----- | ----------- | -| [`embeddedInTarget(name:)`](#embedded-in-target) | Indicates that you would like to include the generated models directly in your application target. | +| [`embeddedInTarget(name:)`](#embedded-in-target) | Indicates that you would like to include the generated module directly in your application target. | | [`swiftPackageManager`](#swift-package-manager) | Creates a schema module as an SPM package that can be included in your project. | -| [`other`](#other-schema-module-types) | Indicates that you would like to define a schema module using manually. | +| [`other`](#other-schema-module-types) | Indicates that you would like to manually define a schema module using a third party package manager (such as Cocoapods). | #### Embedded in target **`ModuleType.embeddedInTarget(name: String)`** -This option indicates that you would like to include the generated models directly in your application target. +This option indicates that you would like to include the generated module directly in your application target. The `name` parameter must specify the name of the target the schema types will be included in. This will be used to generate `import` statements for generated operations. No module will be created for the schema types. Instead, generated schema types will be enclosed in a [caseless namespace `enum`](https://www.swiftbysundell.com/articles/powerful-ways-to-use-swift-enums/#namespaces-and-non-initializable-types). -When creating a single target application, this option allows you to include Apollo iOS's generated models directly in your application target. +When creating a single target application, this option allows you to include Apollo iOS's generated module directly in your application target. > **Note:** When using this `moduleType`, you are responsible for ensuring the generated files are linked to your application target. diff --git a/docs/source/config.json b/docs/source/config.json index f5bd4b3d4b..d248ac01ae 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -7,6 +7,8 @@ "sidebar": { "Introduction": "/", "Get Started": "/get-started", + "Migrating to v1.0": "/migrations/1.0", + "Project Configuration": "/project-configuration", "Tutorial": { "Code Generation": "/tutorial/codegen-getting-started" }, diff --git a/docs/source/custom-scalars.mdx b/docs/source/custom-scalars.mdx index 4c292a7ea8..b4e1ae8b36 100644 --- a/docs/source/custom-scalars.mdx +++ b/docs/source/custom-scalars.mdx @@ -133,7 +133,7 @@ extension MySchema { The `GeoPoint` struct conforms to both `CustomScalarType` and `Hashable`. You must explicitly declare the conformance to `Hashable`, which inherits `Equatable` conformance. Because Swift can synthesize the `Hashable` and `Equatable` conformancs here, you do not need to implement them. -The `init(_jsonValue:)` initializer casts the `JSONValue` as a `String` and seperates it into two coordinates. It converts those coordinates using `Float(_jsonValue:)` which is provided by Apollo. Each of the [built-in scalar types](https://graphql.org/learn/schema/#scalar-types) has JSON serialization support that you can use within your custom scalar implementations. +The `init(_jsonValue:)` initializer casts the `JSONValue` as a `String` and separates it into two coordinates. It converts those coordinates using `Float(_jsonValue:)` which is provided by Apollo. Each of the [built-in scalar types](https://graphql.org/learn/schema/#scalar-types) has JSON serialization support that you can use within your custom scalar implementations. To ensure consistency of the serialized JSON, the `_jsonValue` function ensures that the coordinates are formatted with a single decimal point using `String(format:,_:)`. diff --git a/docs/source/get-started.mdx b/docs/source/get-started.mdx index 03379cf626..9d6bb177ed 100644 --- a/docs/source/get-started.mdx +++ b/docs/source/get-started.mdx @@ -6,8 +6,7 @@ sidebar_title: Get Started import SPMXcodeInstallationPanel from "../shared/spm-xcode-installation-panel.mdx" import SPMPackageInstallationPanel from "../shared/spm-package-installation-panel.mdx" import PodsInstallationPanel from "../shared/pods-installation-panel.mdx" -import SPMSetupCodegenPanel from "../shared/spm-setup-codegen-panel.mdx" -import PodsSetupCodegenPanel from "../shared/pods-setup-codegen-panel.mdx" +import SetupCodegen from "../shared/setup-codegen/combined.mdx" Follow the steps below to add Apollo iOS to your app: @@ -50,11 +49,7 @@ The easiest way to do this is with the Codegen CLI provided with Apollo iOS. > > To use Apollo's code generation and schema downloader from within any Swift script or library, check out [Running code generation in Swift code](./code-generation/run-codegen-in-swift-code). -
- - - - + ## 5. Create an `ApolloClient` diff --git a/docs/source/migrations/1.0.mdx b/docs/source/migrations/1.0.mdx new file mode 100644 index 0000000000..df9030ade0 --- /dev/null +++ b/docs/source/migrations/1.0.mdx @@ -0,0 +1,455 @@ +--- +title: Apollo iOS 1.0 migration guide +description: From 0.x to 1.0 +--- + +import SetupCodegen from "../../shared/setup-codegen/combined.mdx" +import SeparateLocalCacheMutationsNote from "../../shared/separate-local-cache-mutation-note.mdx" + +Apollo iOS 1.0 provides a stable API that uses modern Swift language conventions and features. Among other improvements, it features: + +- New generated models that improve readability and functionality while reducing generated code size in most cases +- New code generation tooling that is written in pure Swift code +- Support for using generated models in multi-module projects +- Type-safe APIs for cache key resolution + +Because most of the library's core concepts are unchanged, the migration to 1.0 from the legacy 0.x versions of Apollo iOS should not require serious rewrites in most cases. This page describes the most important changes, along with how to migrate an existing project from Apollo iOS 0.x to 1.0. + +> **To start your migration to Apollo iOS 1.0:** +> - You should first read about the [key changes](#key-changes) highlighted below. +> - Then, follow the [step-by-step instructions](#step-by-step-instructions). +> - Lastly, use the [breaking changes](#breaking-changes) section to help you resolve any remaining build errors in your code. + +## Key changes + +### Generated schema module + +In addition to generating models for your GraphQL operation definitions, the 0.x version generated models for the input objects and enums referenced from your schema. + +Apollo iOS 1.0 expands on this, generating a whole module that contains models and metadata for your GraphQL schema and its type definitions. +In addition to your input objects and enum types, this module contains: +- Types for the objects, interfaces, and unions referenced +- Editable definitions for your custom scalars +- An editable `SchemaConfiguration.swift` file +- Metadata used by the Apollo GraphQL executor + +To prevent naming conflicts with other types in your project, the schema types are contained in their own namespace. +This namespace can be generated as a stand-alone module, which can be imported by your project, or as a caseless namespace enum which can be embedded in your application target. + +### Multi-module support + +Apollo iOS 1.0 was designed to support complex applications composed of multiple modules as well as monolithic application targets. + +To support multi-module projects, the [generated schema module](#generated-schema-module) contains all of the schema's shared types and metadata. This allows your generated operation models to be located anywhere you would like in your project structure, as long as they are linked to the schema module. + +The code generation engine provides flexible configuration options that aims to make code generation work seamlessly for any project structure you prefer. + + +## Step-by-step instructions + +> **Before performing the migration to Apollo iOS 1.0, you should consider your project structure and decide how you would like to include your generated schema module and operation models.** +> +> To learn about how you can best integrate Apollo iOS to suit your project's needs, see our [Project configuration documentation](./project-configuration). + +To begin your migration to Apollo iOS 1.0 you'll need to: + +- [Update your dependency to Apollo iOS version 1.0](#1-update-to-apollo-ios-10) +- [Setup code generation](#2-setup-code-generation) +- [Replace the code generation build phase](#3-replace-the-code-generation-build-phase) +- [Refactor your code to use the new APIs](#4-refactor-your-code) + +Much of the migration process involves the new code generation mechanism, which is detailed in this step-by-step guide. It will walk you through this process, explaining how to remove deprecated pieces of the legacy 0.x version along the way. + +Below the step-by-step instructions, you will find explanations of each breaking API change in Apollo iOS 1.0. + +### 1. Update to Apollo iOS 1.0 + +The first step in the migration is to update your dependency to the latest version. Apollo iOS can be included as a package using Swift Package Manager (SPM) or Cocoapods. + +In order to receive bug fixes and new features, we recommend including `1.0` up to the next major release. + +> To see what modules are provided by the Apollo iOS SDK and determine which modules you need to include in your project, see our [SDK components documentation](./project-configuration#apollo-ios-sdk-components). + + + +```swift title="Package.swift" +.package( + url: "https://github.com/apollographql/apollo-ios.git", + .upToNextMajor(from: "1.0.0") +), +``` + +```ruby title="Podfile" +pod 'Apollo' ~> '1.0' +``` + + + +> **Note:** Apollo iOS 1.0 supports being built as a dynamic `.xcframework` or as a static library. While our documentation does not provide step-by-step instructions on this process, building and including Apollo iOS 1.0 as a pre-compiled binary with a build tool such as Carthage or Buck is possible. + +### 2. Setup code generation + +Apollo iOS 1.0 includes a new code generation engine, written in 100% Swift code, to replace the legacy `apollo-tooling` code generation. To use 1.0, you will need to install the new code generation engine and remove the old engine. + +The recommended way to use the new code generation engine is through the Apollo Codegen CLI. For advanced usage, you can run code generation in a Swift script. + +#### Codegen CLI setup + +For CLI setup instructions, select the method you are using to include `Apollo`. + + + +#### Swift scripts setup + +If you are [running code generation using a Swift script](./../code-generation/run-codegen-in-swift-code), update your swift script to use the version of `ApolloCodgenLib` matching your `Apollo` version. + +Then, update your `ApolloCodegenConfiguration` in your script with the new configuration values. All configuration options can be found in the [Codegen configuration documentation](./../code-generation/codegen-configuration). + +### 3. Replace the code generation build phase + +Running Apollo's code generation as an Xcode build phase is no longer recommended. Generated files only change when you modify your `.graphql` operation definitions, which usually happens infrequently. Running code generation on every build causes increased build times and a slower development process. Instead, we recommend that you run code generation manually using the CLI when you modify your `graphql` files. + +If you would like to continue running code generation on each build, you can update your build script to run the CLI `generate` command. + +### 4. Refactor your code + +Apollo iOS 1.0 was designed to make the migration require as few changes to your code as possible, however there are a number of breaking changes that will require you to refactor some of your code. + +Below you will find explanations of each of the breaking API changes and tips on how to address them while migrating to 1.0. + +## Breaking changes + +### Custom scalars + +The way we handle the definitions for custom scalars has changed in 1.0. If you have defined any custom scalar types or used the `--passthroughCustomScalars` code generation option, you will need to migrate your custom scalars. + +In the 0.x version of Apollo iOS, custom scalars in your schema were exposed as a `String` type fields by default. If you used the `--passthroughCustomScalars` option, the name of the custom scalar would be used in your generated models. You were responsible for for defining types for passed through custom scalars. + +In 1.0, operation models always use the custom scalar definitions and default `typealias` definitions are generated for all of your referenced custom scalars. These custom scalar definitions are located in your schema module. +The default implementation of all custom scalars is a `typealias` to `String`. + +The generated custom scalar definition files can be edited, and your changes will not be overwritten when code is generated in the future. To migrate a custom scalar type to 1.0: +- Include the type in your schema module +- Make the type conform to the [`CustomScalarType`](https://www.apollographql.com/docs/ios/docc/documentation/apolloapi/customscalartype) protocol +- Change the `typealias` to point to the new type + - Or if the type has the exact name of your custom scalar, remove the `typealias` + +> For a detailed explanation on defining custom scalars, read the [custom scalars documentation](./../fetching/../custom-scalars). + +#### Example + +Given a schema defining `scalar Coordinate` that is referenced in your GraphQL operations, the `Coordinate` custom scalar will be generated: + +```swift title="MySchema/CustomScalars/UUID.swift" +public extension MySchema { + typealias Coordinate = String +} +``` + +A custom scalar with the name `Coordinate` could replace the `typealias` like so: + +```swift title="MySchema/CustomScalars/UUID.swift" +public extension MySchema { + struct Coordinate: CustomScalarType { + let x: Int + let y: Int + + public init (_jsonValue value: JSONValue) throws { + guard let value = value as? String, + let coordinates = value.components(separatedBy: ",").compactMap({ Int($0) }), + coordinates.count == 2 else { + throw JSONDecodingError.couldNotConvert(value: value, to: Coordinate.self) + } + + self.x = coordinates[0] + self.y = coordinates[1] + } + + public var _jsonValue: JSONValue { + "\(x),\(y)" + } + } +} +``` + +### Cache key configuration + +In the 0.x version of Apollo iOS, you could configure the computation of cache keys for the normalized cache by providing a `cacheKeyForObject` block to the `ApolloClient`. In 1.0, this is replaced by a type-safe API in the `SchemaConfiguration.swift` file that is generated alongside your generated schema types. + +To migrate your cache key configuration code, refactor your `cacheKeyForObject` implementation into the `SchemaConfiguration.swift` file's [`cacheKeyInfo(for type:object:)`](https://www.apollographql.com/docs/ios/docc/documentation/apolloapi/schemaconfiguration/cachekeyinfo(for:object:)) function. In this function you will return a `CacheKeyInfo` struct, rather than a cache key `String`. + +In 0.x, it was recommended that you prefix your cache keys with the `__typename` of the object to prevent key conflicts. In 1.0, this is done automatically for you. If you want to group cache keys for objects of different types together (by a common interface type, for example), you can set the `uniqueKeyGroup` property of the `CacheKeyInfo` you return. + +> For a detailed explanation of the new cache key configuration APIs, read the [cache key resolution documentation](./../caching/cache-key-resolution). + +#### Example + +Given a `cacheKeyForObject` block: + +```swift +client.cacheKeyForObject = { + guard let typename = $0["__typename"] as? String, + let id = $0["id"] as? String else { + return nil + } + + return "\(typename):\(id)" +} +``` + +You can migrate this to the new [`cacheKeyInfo(for type:object:)`](https://www.apollographql.com/docs/ios/docc/documentation/apolloapi/schemaconfiguration/cachekeyinfo(for:object:)) function like so: + +```swift +public extension MySchema { + static func cacheKeyInfo(for type: Object, object: JSONObject) -> CacheKeyInfo? { + guard let id = object["id"] as? String else { + return nil + } + + return CacheKeyInfo(id: id) + } +} +``` + +Or using the [JSON value convenience initializer](./../caching/cache-key-resolution#json-value-convenience-initializer) like so: + +```swift +public extension MySchema { + static func cacheKeyInfo(for type: Object, object: JSONObject) -> CacheKeyInfo? { + return try? CacheKeyInfo(jsonValue: object["id"]) + } +} +``` + +### Local Cache Mutations + +In the 0.x version, you could directly make changes to data in the local cache using any of your generated operation or fragment models. While the APIs for direct cache access have not change significantly, the generated model objects are now immutable by default. You can still read cache data directly using all of your generated models, but in order to mutate cache data, you will now need to define separate [local cache mutation](./../caching/cache-transactions#defining-local-cache-mutations) operations or fragments. + +You can define a local cache mutation model by applying the `@apollo_client_ios_localCacheMutation` directive to any GraphQL operation or fragment definition. + +> For a detailed explanation of the new local cache mutation APIs, read the [direct cache access documentation](./../caching/cache-transactions). + + + +#### Example + +Given an operation and write transaction from the 0.x version: + + + +```graphql +query UserDetails { + loggedInUser { + id + name + posts { + id + body + } + } +} +``` + +```swift +store.withinReadWriteTransaction({ transaction in + let cacheMutation = UserDetailsQuery() + + let newPost = UserDetailsQuery.Data.LoggedInUser.Post(id: "789, body: "This is a new post!") + + try transaction.update(cacheMutation) { (data: inout UserDetailsQuery.Data) in + data.loggedInUser.posts.append(newPost) + } +}) +``` + + +You can migrate this to a new `LocalCacheMutation` like so: + + + +```graphql +query AddUserPostLocalCacheMutation @apollo_client_ios_localCacheMutation { + loggedInUser { + posts { + id + body + } + } +} +``` + +```swift +store.withinReadWriteTransaction({ transaction in + let cacheMutation = AddUserPostLocalCacheMutation() + + let newPost = AddUserPostLocalCacheMutation.Data.LoggedInUser.Post(data: DataDict( + ["__typename": "Post", "id": "789", "body": "This is a new post!"], + variables: nil + )) + + try transaction.update(cacheMutation) { (data: inout AddUserPostLocalCacheMutation.Data) in + data.loggedInUser.posts.append(newPost) + } +}) +``` + + + +### Nullable Input Values + +According to [the GraphQL spec](http://spec.graphql.org/October2021/#sec-Null-Value), explicitly providing a `null` value for an input value to a field argument is semantically different from not providing a value at all (`nil`). This enum allows you to distinguish your input values between `null` and `nil`. + +In order to distinguish between `null` and `nil` the 0.x version generated optional input values as double optional value types (`??`, or `Optional>`). This caused confusion for many users and did not express the intention of the APIs clearly. In 1.0, we have replaced the use of double optional values with a new [`GraphQLNullable`](https://www.apollographql.com/docs/ios/docc/documentation/apolloapi/graphqlnullable) wrapper enum type. + +This new type requires you to explicitly indicate the value or nullability behavior of your input values. This applies to nullable input arguments on your operation definitions and nullable properties on input objects. While this API is slightly more verbose, it provides more clarity and requires you to explicitly express your intent for handling nullable values. This provides you with more expressive and reduced bugs caused by unexpected behavior. + +> For a more examples and best practices using `GraphQLNullable`, read the [working with nullable arguments documentation](./../fetching/operation-arguments#working-with-nullable-arguments). + +#### Example + +When passing a value to a nullable input parameter, you will need to wrap the value in a `GraphQLNullable`. + + + +```swift title="0.x" +MyQuery(input: "Value") +``` + +```swift title="1.0" +MyQuery(input: .some("Value")) +``` + + + +To provide a `null` or `nil` value, use `.null`, or `.none` respectively. + + + +```swift title="0.x" +/// A `nil` double optional value translates to omission of the value. +MyQuery(input: nil) + +/// An optional containing a `nil` value translates to an `null` value. +MyQuery(input: .some(nil)) +``` + +```swift title="1.0" +/// A `GraphQLNullable.none` value translates to omission of the value. +MyQuery(input: .none) + +/// A `GraphQLNullable.null` value translates to an `null` value. +MyQuery(input: .null) +``` + + + +When passing an optional value to a nullable input value, you will now need to explicitly provide the fallback value if your value is `nil`. + + + +```swift title="0.x" +var optionalInput: String? = nil + +MyQuery(input: optionalInput) +``` + +```swift title="1.0" +var optionalInput: String? = nil + +MyQuery(input: optionalInput ?? .null) +``` + + + +### Mocking operation models for testing + +In the 0.x version, you could use the create mocks of your generated operation models by using the generated initializers on your models or initialize them directly with JSON data. Both methods were error-prone, cumbersome, and fragile. + +Apollo iOS 1.0 provides new functionality for generated test mocks based on your schema types. These test mocks make creating mocks of your operation models more readable and type-safe. This also removes the need for the large number of generated initializers for different models types. + +To generate test mocks, add the [`output.testMocks`](./../code-generation/codegen-configuration#test-mocks) value to your code generation configuration, then link your generated test mocks to your unit test target. + +Instead of creating a model using the generated initializer for a type, create a test mock of the schema type for the underlying object, set values for the relevant fields, and then initialize your operation model using the test mock. If you would like to continue to initialize your operation models with JSON data, this is still possible, but the initializer has changed slightly. See the [JSON initializer example](#test-mocks-from-json-data) below for more information. + +> For more details on generating and using test mocks, read our [test mocks documentation](./../testing/test-mocks). + +#### Examples + +Given a `Hero` interface type that can be either a `Human` or `Droid` type and the following operation definition: + +```graphql +query HeroDetails { + hero { + id + ... on Human { + name + } + ... on Droid { + modelNumber + } + } +} +``` + +The 0.x version would generated initializers for each type on the `HeroDetails.Data.Hero` model: + +```swift +struct Hero { + static func makeHuman(id: String, name: String) { + // ... + } + + static func makeDroid(id: String, modelNumber: String) { + // ... + } +} +``` + +These initializers are not generated in 1.0. Instead, initialize either a `Mock`, or a `Mock`: + +```swift +let mockHuman = Mock() +mockHuman.id = "10" +mockHuman.name = "Han Solo" + +let mockDroid = Mock() +mockDroid.id = "12" +mockDroid.modelNumber = "R2-D2" +``` + +Then, create mocks of the `HeroDetails.Data.Hero` model using your test mocks: + +```swift +let humanHero = HeroDetails.Data.Hero(from: mockHuman) +let droidHero = HeroDetails.Data.Hero(from: mockDroid) +``` + +##### Test mocks from JSON Data + +If you want to continue to initalize your models using JSON data directly, change your initializers to initialize your model using the `init(data: DataDict)` initalizer. You will also need to ensure that your JSON data is a `[String: AnyHashable]` dictionary. + + + +```swift title="0.x" +let json: [String: Any] = [ + "__typename: "Human", + // ... +] + +let hero = HeroDetails.Data.Hero( + unsafeResultMap: json +) +``` + +```swift title="1.0" +let json: [String: AnyHashable] = [ + "__typename: "Human", + // ... +] + +let hero = HeroDetails.Data.Hero( + data: DataDict(json) +) +``` + + diff --git a/docs/source/project-configuration.mdx b/docs/source/project-configuration.mdx new file mode 100644 index 0000000000..3ca6b78b56 --- /dev/null +++ b/docs/source/project-configuration.mdx @@ -0,0 +1,303 @@ +--- +title: Project configuration +description: Structuring a GraphQL application with the Apollo iOS SDK +--- + +Apollo iOS 1.0 was designed to support complex applications composed of multiple modules as well as monolithic application targets. The code generation engine and modularized structure of the Apollo iOS SDK provide flexible configuration options that aim to make the Apollo libraries and your generated code work seamlessly for any project structure you prefer. + +This guide helps you understand the contents of the models generated by Apollo iOS, the modules that compose the Apollo iOS SDK, and how to include these components in your project, depending on its structure. + +## Generated models + +There are three groups of files the that Apollo iOS generates for your project: + +* **[Operation models](#operation-models):** The queries, mutations, subscriptions, and fragments defined in your GraphQL files. +* **[Schema types](#schema-types):** Your GraphQL schema's shared types and metadata such as objects, enums, input objects, etc. +* **[Test mocks](#test-mocks):** Test mocks objects for your schema types that enable creation of operation models for your tests. + +#### Operation models + +To execute GraphQL requests, you define a series of GraphQL operation files, which contain the queries, mutations, subscriptions, and fragments you can execute. Apollo iOS generates operation models from these definitions. These operation models are classes that represent your queries, mutations, and subscriptions, which can be used to make GraphQL requests with an `ApolloClient`. + +Each operation model also includes a set of response models objects, which are type-safe models representing the response receieved by the operation. The `ApolloClient` returns instances of these response models upon successful completion of a GraphQL request using the corresponding operation model. + +> To learn more about GraphQL operations, check out [Defining operations](./fetching/fetching-data#defining-operations). + +#### Schema types + +All GraphQL operations depend upon the types in their corresponding GraphQL schema. Your generated operation models also depend on information from the schema. Apollo iOS generates a collection of types that provide the necessary properties and metadata about the types in your GraphQL schema (ie. objects, interfaces, unions, enums, input objects, etc.). These shared schema types are referenced by your generated operation models to provide type information without duplicated code. + +#### Test mocks + +Apollo iOS provides the ability to generate test mock objects for use in your test targets. The mocks enable creation of operation models for your tests. + +For more information on setting up and using test mocks, see our [test mocks documentation](./testing/test-mocks) + +## Configuring your project + +When including Apollo iOS in a new project, it's important to decide how you will structure your project, and where your generated GraphQL models fit in that project. Your GraphQL models can be included in your main application target, spread out across many modules, or exposed as an independent module that can be used by multiple applications. Depending on the needs and constraints of your project, you will need to configure the code generation engine and link to the Apollo iOS libraries differently. + +There are three primary decisions to be made about your project structure: + +> For more specific customization, read about the configuration options below. + +### Single target vs multi-module + +> ** 1. Will your application consist of a single application target or multiple modules?** + +Depending on the size and complexity of your project, it may be built as a single, monolithic application target, or be composed of multiple modules. + +For a monolithic application you can simply link your application target to the `Apollo` library. You may optionally, link to `ApolloSQLite` and/or `ApolloWebSocket` to enable their functionality. + +To include the Apollo iOS SDK in a project with multiple modules: + +- Link `Apollo` to the modules that configure or use the networking and caching APIs. + - Optionally, link `ApolloSQLite` and/or `ApolloWebSocket` to the module that sets up your `ApolloClient` to enable their functionality. +- Link `ApolloAPI` to the modules that only include generated models. + +You may also optionally, link `ApolloTestSupport` to your unit test target to create mocks of your generated models. + +> For more information about the libraries that make up the Apollo iOS SDK, see the [Apollo iOS SDK components section](#apollo-ios-sdk-components). + +>

Important: `ApolloCodegenLib` should not be linked to your application targets.

+> +> It only supports macOS and should only be linked to by development tools that want to use the Apollo code generation engine. +> For most projects, it is recommended to use the Codegen CLI instead of using `ApolloCodegenLib` directly. + +### Schema types module + +> ** 2. How would you like to include your generated schema types?** + +The generated schema types can be included in your project by embedding them directly in your application target, or as a separate module you include. This is configured through the [`output.schemaTypes`](./code-generation/codegen-configuration#schema-types) property of your codegen configuration. When setting up codegen for the first time, you will need to specify a path for the location of the generated module, and choose a module type to configure how the schema types module will be linked to your project. + +For most projects, we recommend creating a separate schema module. This makes it possible to share your generated models across modules as your project grows. For simple, single target applications, you may choose to embed your schema types in your target + +> For more information on configuring your schema types output see the [`output.schemaTypes` configuration documentation](./code-generation/codegen-configuration#schema-types). + +#### Creating a schema module + +This will allow you to link the schema module to each of your other modules that include or consume your generated operation models. The code generation engine can automate the creation of the schema types module for Swift Package Manager and is flexible enough to support manual configuration for projects with customized dependency needs. + +Use the `.swiftPackageManager` or `.other` options to generate schema types that can be included as their own module. + +> Most dependency managers like SPM and Cocoapods can automatically include your generated files in the module's directory. This means when generated files are added or removed from the generated module, they will be linked to your project automatically. + +#### Embedded in your application + +You may also include your schema types directly in a target that you have created. + +For this you should use [`ModuleType.embeddedInTarget(name: String)`](./code-generation/codegen-configuration#embedded-in-target) for the [`output.schemaTypes.moduleType`](./code-generation/codegen-configuration#module-type) property. The generated schema types will be enclosed in a caseless namespace enum to prevent naming conflicts with types you may already have defined in your target. + +> By using this option you are responsible for manually adding the generated files to your chosen target. When new generated files are created, or old ones are deleted, you will need to manually add/remove them from your target's "Compile Sources" build phase. + +### Operation model generation + +> ** 3. Where should your generated operations models be located?** + +The next choice is to consider how operation models are used in your project. This is configured through the [`output.operations`](./code-generation/codegen-configuration#operations) property of your codegen configuration. + +Your operation models can be included in your project in the way that makes the most sense for your needs. The only requirement is that your operation models need to have access to your schema types and to the `ApolloAPI` target to compile. + +Generally, this decision falls into two categories: + +#### Confined models + +If you would like to organize your generated models by specific feature areas or modules within your project, use the [`OperationsFileOutput.relative(subpath: String?)`](./code-generation/codegen-configuration#relative-operations-output) value for the [`output.operations`](./code-generation/codegen-configuration#operations) property. The operation models will be generated relative to the `.graphql` files that define them. + +This gives you the most flexibility and control over your operation models, as they can be generated anywhere in your project structure by organizing your `.graphql` operation definitions. With relative paths you can: +- Co-locate models with the feature code that uses them +- Include your models into different modules within in your project +- Organize them based on feature areas +- Or any other structure you would like + +> When including your operation models in a multi-module project, you will need to ensure that any modules which include your operation models link to both your schema types module and the `ApolloAPI` library. + +#### Shared models + +You can also share your generated operation models across modules in your project either by including them within the shared schema types module, or by manually including them in another shared module. + +##### Bundled with schema types + +For most small projects, this is the most straightforward way of including your operation models. The operation models will be located in a sub-folder of the directory where your schema types are generated. + +For this option use the [`.inSchemaModule`](./code-generation/codegen-configuration#operations-in-the-schema-module) option for the [`output.operations`](./code-generation/codegen-configuration#operations) property. + +**If you are using a schema module**, the operation models will be included in the module, which can be imported by other modules in your project. + +**If you are embedding your schema in another target**, the operations will also be included in the generated schema namespace that you include in your application target. + +##### Absolute path + +To generate your operation models into a single, specified directory, use the [`.absolute(path:)`](./code-generation/codegen-configuration#absolute-operations-output) option. These can then be manually included in your project however you see fit. + +If you choose to generate the operation models in to an absolute path, you are you responsible for any module creation in order to link the generated files with the rest of your project. You will need to ensure that any targets which include your operation models link to both your schema types module and the `ApolloAPI` library. + +### Supporting other configurations + +There are many different possible combinations for a modular architecture and we've only shared examples of the most common configurations. Apollo iOS has many different options to support flexible configurations of the schema types and operation models. Please take the time to familiarize yourself with the [codegen configuration](./code-generation/codegen-configuration) and we're confident you can find a combination to suit your project needs. + +## Apollo iOS SDK components + +These are the libraries that compose the Apollo iOS package: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DescriptionUsage
+ +#### `Apollo` + +
+ +The core Apollo client library. + +Includes the networking and caching APIs, including `ApolloClient` and `ApolloStore`. + + + +Any targets that need to access these core components directly should be linked against `Apollo`. + +
+ +#### `ApolloAPI` + +
+ +Includes the common components that are used by the generated models for your project. + + + +Any targets that include your generated models should be linked to `ApolloAPI`. + +The `Apollo` library has a dependency on this target, so any target that links to `Apollo` does not need to link to `ApolloAPI` directly. + +Because the generated models export the `ApolloAPI` library's interface, targets that consume generated models but do not contain them do not need to link to `ApolloAPI` directly. + +
+ +#### `ApolloSQLite` + +
+ +Provides a `NormalizedCache` implementation backed by a `SQLite` database + +Use this library if you would like to persist cache data across application lifecycles. +> See the [`SQLiteNormalizedCache` documentation](./caching/cache-setup#sqlitenormalizedcache) for more information on setting up a persistent SQLite cache. + + + +This library only needs to be linked to your targets that configure the `SQLiteNormalizedCache` and pass it to the `ApolloStore`. + +
+ +#### `ApolloWebSocket` + +
+ +Provides a web socket transport implementation used to support `GraphQLSubscription` operations + +If your project uses GraphQL subscriptions, you must include this library. + +> See the [Enabling GraphQL subscription support documentation](./fetching/subscriptions#enabling-graphql-subscription-support) for more information on setting up a web socket transport. + + + +This library only needs to be linked to your targets that configure the `WebSocketTransport` and pass it to the `ApolloClient`. + +
+ +#### `ApolloTestSupport` + +
+ +Includes the APIs for creating test mocks for your generated models + + + +Link this library to *unit test targets* that need to create mocks of generated models. + +
+ +#### `ApolloCodegenLib` + +
+ +Includes the code generation engine used to generate your GraphQL models + +Use this library if you want to run the code generation engine from your own Swift executable targets. + +> For most projects, it is recommended to use the Codegen CLI instead of using `ApolloCodegenLib` directly. + + + +Link this library to development tools that want to use the Apollo code generation engine. This library only supports macOS. + +> **Note: `ApolloCodegenLib` should not be linked to your application targets.** + +
+ +> **Note for Cocoapods users:** +> +> Cocoapods combines subspecs into a single target. This means that: +> - `ApolloAPI` will be merged into the `Apollo` target. +> - If the `ApolloSQLite` and `ApolloWebSocket` subspecs are used, they will also be merged into the `Apollo` target.