Swift scripting
Apollo Client for iOS enables you to use Swift scripting to perform certain operations that otherwise require the command line.
This document guides you through setting up a Swift Package Manager executable project using our template, and then using that project to:
Download a schema
Generate Swift code for your model objects based on your schema and operations
Conceptual background
Apollo's code generation requires both of the following to run:
Your schema, which defines what it's possible for you to request from or send to your server
One or more operations, which define what you are actually requesting from the server
If you're missing either of these, codegen can't run. If you define operations but no schema, the operations can't be validated. If you define a schema but no operations, there's nothing to validate or generate code for.
Or, more succinctly:
1schema + operations = codeEach operation you define can be one of the following:
A query, which is a one-time request for specific data
A mutation, which changes data on the server and then receives updated data back
A subscription, which allows you to listen for changes to a particular object or type of object
Code generation takes your operations and compares them to the schema to confirm that they're valid. If an operation isn't valid, the whole process errors out. If all operations are valid, codegen generates Swift code that gives you end-to-end type safety for each operation.
The rest of this guide will help you set up a Swift Package Manager executable that will live alongside your main xcodeproj and which can be used either from your main xcodeproj or on its own to download a schema, generate code, or both.
Setup
We've created a template of a Swift Package Manager Executable to speed things along.
This project is provided as a template for an executable rather than a compiled executable to allow you to make changes to the executable relevant to your setup. This allows you to customize while still using Swift as much as possible and bash as little as possible, to preserve both type safety and readability.
You can download the current version of the template from this repo: https://github.com/apollographql/iOSCodegenTemplate
When you unzip the downloaded repo, you'll see that there's a folder called ApolloCodgen.
If you're using the default target structure for an Xcode project, your project's file structure will look essentially like this in Finder:
1MyProject // Source root
2├─ MyProject.xcodeproj
3├─ MyProject/ // Contains app target source files
4├─ MyLibraryTarget/ // Contains lib target source files
5├─ MyProjectTests/ // Contains test filesDrag the ApolloCodegen folder in at the same level as your other targets (in Finder, not in Xcode):
1MyProject // Source root
2├─ MyProject.xcodeproj
3├─ MyProject/ // Contains app target source files
4├─ MyLibraryTarget/ // Contains lib target source files
5├─ MyProjectTests/ // Contains test files
6├─ ApolloCodegen/ // Contains the swift scripting files you just downloaded and dragged inDouble-click Package.swift in the ApolloCodegen folder to open the executable's package in Xcode.
Important! Since a particular version of code generation is tied to a particular version of the SDK, you need to make sure that the dependencies section of Package.swift is set to grab the same version of the apollo-ios library you're using in your main application:
1.package(name: "Apollo",
2 url: "https://github.com/apollographql/apollo-ios.git",
3 .upToNextMinor(from: "0.53.0"))Note that these instructions are updated along with newer versions of the library - if you're seeing something that doesn't compile, please check that you're on the most recent version of the SDK both in your app and in your Codegen project.
A Tour Of The Template Project
This section will walk you through the already-set up code in the template project. There are two files: main.swift and FileStructure.swift.
FileStructure
This structure exists to simplify accessing your local filesystem without needing to pass any URLs through as environment variables.
It uses FileFinder to find the directory that the file containing the code lives in, and then you can use either standard FileManager APIs or .apollo extension methods provided by ApolloCodegenLib to find parent or child files and folders.
There are two major pieces of information it grabs automatically, assuming you have a filesystem set up as in the example above:
The location where the Typescript CLI will be downloaded. This is what (currently) does the actual generation of code and fetching of schemas. NOTE: This location should be added to your
.gitignorefile, since it's going to contain a ton of JS that can be redownloaded locally very easily, and will otherwise bloat your repo.The
sourceRootof your entire repository, which contains both your Xcode project and your Swift Scripting project.
From sourceRoot you should be able to use FileManager APIs or their .apollo extensions to navigate anywhere in your project's file tree.
SwiftScript
This object uses the Swift Argument Parser to create an outer root command which can be run either from the command line if a binary is exported, or run directly by using swift run (recommended).
There are sub-commands to run specific tasks - these are most often what you'll want to focus on, and where you'll need to make changes appropriate to your project's name and structure.
DownloadSchema
This command will download a GraphQL schema. There are two pieces you definitely need to fill in:
The place you want to download your schema from. This is usually via introspection of your GraphQL endpoint, so the default is set to use this, you just need to replace the
localhostURL with the URL of your GraphQL endpoint. If you've got it set up, you can also download your schema from the Apollo Schema Registry. You'll need to add your API Key and Graph ID for this, which you can get from Apollo Studio.The name of the folder where you want the schema downloaded. If you've followed the template above, you'll want to place the schema in the folder where your target's code lives.
You can also use options provided by the ApolloSchemaDownloadConfiguration object to further configure how and where you want to download your schema.
GenerateCode
This command will take your downloaded schema and your local operations and combine them to generate code. Note that if you don't have a schema or don't have any local operations, code generation will fail.
You will need to replace one placeholder: The location of your target's root folder.
Running as a script
To run the script using the command line, cd into the ApolloCodegen directory and run the following command:
1swift run ApolloCodegen [subcommand]This will build and run the executable, and then run the specified subcommand. The first build may take a minute since it will need to check out dependencies, but the Swift build system's caching will prevent any steps that haven't had changes (including dependency fetching) from re-executing.
If you don't provide a subcommand, a list of available subcommands will be printed.
Downloading a schema
Update the downloadSchema command to have the correct download method and download path within the Swift file. Then, from the command line, run:
1swift run ApolloCodegen downloadSchemaIf you're using the template code and following the sample project structure, the schema should download here:
1MyProject // SourceRoot
2├─ MyProject.xcodeproj
3├─ MyProject/ // Contains app target source files
4│ └─ schema.graphqls
5├─ MyLibraryTarget/ // Contains lib target source files
6├─ MyProjectTests/ // Contains test files
7├─ ApolloCodegen/ // Contains Swift Scripting filesNext, now that you have a schema, you need a GraphQL file with an operation in order to generate code.
Adding a .graphql file with an operation
If you're not familiar with creating an operation in graphQL, please check out the portion of our tutorial on executing your first query. You can stop after the section about adding your query to Xcode.
Make sure you've added the operation file to the project files, ideally at or above the level of the schema.graphqls (Otherwise, you'll need to manually pass the URL of your operation file to your code generation step):
1MyProject // SourceRoot
2├─ MyProject.xcodeproj
3├─ MyProject/ // Contains app target source files
4│ ├─ schema.graphqls
5│ └─ LaunchList.graphql
6├─ MyLibraryTarget/ // Contains lib target source files
7├─ MyProjectTests/ // Contains test files
8├─ ApolloCodegen/ // Contains Swift Scripting filesHere, for example, is what this looks like in a file for one of the queries in our tutorial application:
Note: You do not need to add this file to your target in Xcode. Only the generated Swift code needs to be included in your target for it to work.
Running code generation from your main project
Codegen should be set up to run from your main project when you build - this will allow any changes you've made to your graphQL files to be picked up and the code to be regenerated.
This is best achieved with a Run Script Build Phase.
Select the target in your project or workspace you want to run code generation, and go to the
Build Phasestab.Create a new Run Script Build Phase by selecting the + button in the upper left-hand corner:
Update the build phase run script to
cdinto the folder where your executable's code lives, then runswift run(usingxcrunso that you can ensure it runs with the correct SDK, no matter what type of project you're building):Text1# Don't run this during index builds 2if [ $ACTION = "indexbuild" ]; then exit 0; fi 3 4cd "${SRCROOT}"/ApolloCodegen 5xcrun -sdk macosx swift run ApolloCodegen generate 6 7# propagate the xcrun call's return code to Xcode 8exit $? 9Note: If your package ever seems to have problems with caching, run
swift package cleanbeforeswift runfor a totally clean build. Do not do this by default, because it substantially increases build time.Build your target.
Now, every time you build your project, this script gets called. Because Swift knows not to recompile everything unless something's changed, it should not have a significant impact on your build time.
Swift-specific troubleshooting
If you encounter errors around SecTaskLoadEntitlements that result in an immediate exit of the script instead of showing the permission prompt, verify that all the folders you're looking for exist at the correct path. This error is often caused by a typo.