Skip to content

Commit f5c8edc

Browse files
Merge pull request #1263 from apollographql/update/debeta
Bring Swift Scripting out of Beta
2 parents 00d3869 + 92e8a03 commit f5c8edc

3 files changed

Lines changed: 111 additions & 44 deletions

File tree

SwiftScripts/Sources/Codegen/main.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ let parentFolderOfScriptFile = FileFinder.findParentFolder()
88
// Use that to calculate the source root
99
let sourceRootURL = parentFolderOfScriptFile
1010
.apollo.parentFolderURL() // Sources
11-
.apollo.parentFolderURL() // SwiftScripts
12-
.apollo.parentFolderURL() // apollo-ios
11+
.apollo.parentFolderURL() // SwiftScripts
12+
.apollo.parentFolderURL() // apollo-ios
1313

1414
// In a typical app, you'll only need to do this for one target, so you'd
1515
// set these up directly within this file. Here, we're using more than one

docs/source/swift-scripting.md

Lines changed: 108 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,38 @@
11
---
22
title: Swift scripting
3-
sidebar_title: Swift scripting (beta)
3+
sidebar_title: Swift scripting
44
---
55

6-
⚠️ **This functionality is in beta.** ⚠️
7-
86
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 and then using it to:
97

108
- Download a schema
119
- Generate Swift code for your model object based on your schema and operations
1210

11+
## Conceptual background
12+
13+
Apollo's code generation requires both of the following to run:
14+
15+
* Your **schema**, which defines what it's *possible* for you to request from or send to your server
16+
* One or more **operations**, which define what you are *actually* requesting from the server
17+
18+
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.
19+
20+
Or, more succinctly:
21+
22+
```
23+
schema + operations = code
24+
```
25+
26+
Each operation you define can be one of the following:
27+
28+
- A **query**, which is a one-time request for specific data
29+
- A **mutation**, which changes data on the server and then receives updated data back
30+
- A **subscription**, which allows you to listen for changes to a particular object or type of object
31+
32+
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.
33+
34+
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.
35+
1336
## Setup
1437

1538
To begin, let's set up a Swift Package Manager executable:
@@ -63,7 +86,7 @@ To begin, let's set up a Swift Package Manager executable:
6386
```swift:title=Package.swift
6487
.package(name: "Apollo",
6588
url: "https://github.com/apollographql/apollo-ios.git",
66-
.upToNextMinor(from: "0.27.0"))
89+
.upToNextMinor(from: "0.29.0"))
6790
```
6891
**NOTE**: The version should be identical to the version you're using in your main project.
6992
@@ -118,17 +141,17 @@ Here's how you obtain the parent folder of the script, then use that to get back
118141
```swift:title=main.swift
119142
let parentFolderOfScriptFile = FileFinder.findParentFolder()
120143
let sourceRootURL = parentFolderOfScriptFile
121-
.deletingLastPathComponent() // Result: Sources folder
122-
.deletingLastPathComponent() // Result: Codegen folder
123-
.deletingLastPathComponent() // Result: MyProject source root folder
144+
.apollo.parentFolder() // Result: Sources folder
145+
.apollo.parentFolder() // Result: Codegen folder
146+
.apollo.parentFolder() // Result: MyProject source root folder
124147
```
125148

126149
You can use this to get the URL of the folder you plan to download the CLI to:
127150

128151
```swift:title=main.swift
129152
let cliFolderURL = sourceRootURL
130-
.appendingPathComponent("Codegen")
131-
.appendingPathComponent("ApolloCLI")
153+
.apollo.childFolderURL(folderName: "Codegen")
154+
.apollo.childFolderURL(folderName: "ApolloCLI")
132155
```
133156

134157
This would put the folder to download the CLI here in your filesystem:
@@ -187,10 +210,10 @@ One of the convenience wrappers available to you in the target is `ApolloSchemaD
187210

188211
```swift:title=main.swift
189212
let output = sourceRootURL
190-
.appendingPathComponent("MyProject")
213+
.apollo.childFolderURL(folderName:"MyProject")
191214
```
192215

193-
You might want to make sure the folder exists before proceeding:
216+
Note that particularly if you're not just downloading the schema into your target's folder, you will want to make sure the folder exists before proceeding:
194217

195218
```swift:title=main.swift
196219
try FileManager
@@ -201,8 +224,8 @@ One of the convenience wrappers available to you in the target is `ApolloSchemaD
201224
3. Set up your `ApolloSchemaOptions` object. In this case, we'll use the [default arguments for all the constructor parameters that take them](./api/ApolloCodegenLib/structs/ApolloSchemaOptions#methods), and only pass in the endpoint to download from and the folder to put the downloaded file into:
202225

203226
```swift:title=main.swift
204-
let options = ApolloSchemaOptions(endpointURL: endpoint,
205-
outputFolderURL: output)
227+
let schemaDownloadOptions = ApolloSchemaOptions(endpointURL: endpoint,
228+
outputFolderURL: output)
206229
```
207230

208231
With these defaults, this will download a JSON file called `schema.json`.
@@ -212,7 +235,7 @@ One of the convenience wrappers available to you in the target is `ApolloSchemaD
212235
```swift:title=main.swift
213236
do {
214237
try ApolloSchemaDownloader.run(with: cliFolderURL,
215-
options: options)
238+
options: schemaDownloadOptions)
216239
} catch {
217240
exit(1)
218241
}
@@ -231,28 +254,7 @@ One of the convenience wrappers available to you in the target is `ApolloSchemaD
231254

232255
Note the warning: This isn't relevant for schema downloading, but it *is* relevant for generating code: In order to generate code, you need both the schema and some kind of operation.
233256

234-
## Using codegen to create a `.graphql` file with an operation
235-
236-
Code generation requires both of the following to run:
237-
238-
* Your **schema**, which defines what it's *possible* for you to request from or send to your server
239-
* One or more **operations**, which define what you are *actually* requesting from the server
240-
241-
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.
242-
243-
Or, more succinctly:
244-
245-
```
246-
schema + operations = code
247-
```
248-
249-
Each operation you define can be one of the following:
250-
251-
- A **query**, which is a one-time request for specific data
252-
- A **mutation**, which changes data on the server and then receives updated data back
253-
- A **subscription**, which allows you to listen for changes to a particular object or type of object
254-
255-
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.
257+
## Creating a `.graphql` file with an operation
256258

257259
Because you've already [downloaded a schema](#downloading-a-schema), you can now proceed to creating an operation. The easiest and most common type of operation to create is a Query.
258260

@@ -295,15 +297,15 @@ Here, for example, is what this looks like in a file for one of the queries in o
295297

296298
<img alt="Launch list file" src="screenshot/graphql_file_launchlist.png" class="screenshot"/>
297299

298-
## Generating code for a target
300+
## Generating Swift code for a target
299301

300-
>**Before you start**: Remember, you need to have a locally downloaded copy of your schema and at least one `.graphql` file containing an operation in your file tree. If you don't have **both** of these, code generation will fail. [Read the section above](#using-codegen-to-create-a-graphql-file-with-an-operation) if you don't have an operation set up!
302+
>**Before you start**: Remember, you need to have a locally downloaded copy of your schema and at least one `.graphql` file containing an operation in your file tree. If you don't have **both** of these, code generation will fail. [Read the section above](#creating-a-graphql-file-with-an-operation) if you don't have an operation set up!
301303

302304
1. Specify the URL for the root of the target you're generating code for:
303305

304306
```swift:title=main.swift
305307
let targetURL = sourceRootURL
306-
.appendingPathComponent("MyProject")
308+
.apollo.childFolderURL(folderName: "MyProject")
307309
```
308310

309311
Again, you might want to make sure the folder exists before proceeding:
@@ -317,7 +319,7 @@ Here, for example, is what this looks like in a file for one of the queries in o
317319
2. Set up your `ApolloCodegenOptions` object. In this case, we'll use the constructor that [sets defaults for you automatically](./api/ApolloCodegenLib/structs/ApolloCodegenOptions#methods):
318320

319321
```swift:title=main.swift
320-
let options = ApolloCodegenOptions(targetRootURL: targetURL)
322+
let codegenOptions = ApolloCodegenOptions(targetRootURL: targetURL)
321323
```
322324

323325
This creates a single file called `API.swift` in the target's root folder.
@@ -328,7 +330,7 @@ Here, for example, is what this looks like in a file for one of the queries in o
328330
do {
329331
try ApolloCodegen.run(from: targetURL,
330332
with: cliFolderURL,
331-
options: options)
333+
options: codegenOptions)
332334
} catch {
333335
exit(1)
334336
}
@@ -367,6 +369,71 @@ Now, you're able to generate code from a debuggable Swift Package Manager execut
367369
4. Build your target.
368370

369371
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.
372+
373+
## Full Script Example
374+
375+
Here's an example of a full `main.swift` file for your `Codegen` project which follows the file structure outlined above, and both downloads the schema and uses it to run the codegen:
376+
377+
```swift:title=main.swift
378+
import Foundation
379+
import ApolloCodegenLib
380+
381+
// Grab the parent folder of this file on the filesystem
382+
let parentFolderOfScriptFile = FileFinder.findParentFolder()
383+
384+
// Use that to calculate the source root of both the
385+
let sourceRootURL = parentFolderOfScriptFile
386+
.apollo.parentFolderURL() // Sources
387+
.apollo.parentFolderURL() // Codegen
388+
.apollo.parentFolderURL() // MyProject
389+
390+
// From the source root, figure out where your target
391+
// root is within your main project
392+
let targetRootURL = sourceRootURL
393+
.apollo.childFolderURL(folderName: "MyProject")
394+
395+
// Set up the URL you want to use to download the project
396+
let endpoint = URL(string: "http://localhost:8080/graphql")!
397+
398+
// Create an options object for downloading the schema
399+
let schemaDownloadOptions = ApolloSchemaOptions(endpointURL: endpoint,
400+
outputFolderURL: targetRootURL)
401+
402+
// Calculate where you want to create the folder where the CLI will
403+
// be downloaded by the ApolloCodegenLib framework.
404+
let cliFolderURL = sourceRootURL
405+
.apollo.childFolderURL(folderName: "Codegen")
406+
.apollo.childFolderURL(folderName: "ApolloCLI")
407+
408+
do {
409+
// Actually attempt to download the schema.
410+
try ApolloSchemaDownloader.run(with: cliFolderURL,
411+
options: schemaDownloadOptions)
412+
} catch {
413+
// This makes the error message in Xcode a lot more legible,
414+
// and prevents the script from continuing to try to generate
415+
// code if the schema download failed.
416+
exit(1)
417+
}
418+
419+
// Create the default Codegen options object (assumes schema.json
420+
// is in the target root folder, all queries are in some kind
421+
// of subfolder of the target folder and will output as a
422+
// single file to API.swift in the target folder)
423+
let codegenOptions = ApolloCodegenOptions(targetRootURL: targetRootURL)
424+
425+
do {
426+
// Actually attempt to generate code.
427+
try ApolloCodegen.run(from: targetRootURL,
428+
with: cliFolderURL,
429+
options: codegenOptions)
430+
} catch {
431+
// This makes the error message in Xcode a lot more legible.
432+
exit(1)
433+
}
434+
```
435+
436+
Note that in practice, you will probably want to break the codegen and schema download into separate files, since you'll need to run the code generation considerably more frequently.
370437

371438
## Swift-specific troubleshooting
372439

scripts/run-bundled-codegen.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
set -euo pipefail
55

66
# Advertisement!
7-
echo "Have you tried our new Swift Package Manager wrapper around codegen? It's now available in beta! See docs at https://www.apollographql.com/docs/ios/swift-scripting/. Note that when this comes out of beta, this Bash script will be deprecated, so give it a try today!"
7+
echo "Have you tried our new Swift Package Manager wrapper around codegen? It's now out of beta and ready to go! See docs at https://www.apollographql.com/docs/ios/swift-scripting/. This Bash script will be deprecated soon, so give it a try today!"
88

99
# Get the path to the script directory
1010
SCRIPT_DIR="$(dirname "$0")"

0 commit comments

Comments
 (0)