w3resource

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 and then using it to:

  • Download a schema
  • Generate Swift code for your model object 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:

schema + operations = code

Each 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

To begin, let's set up a Swift Package Manager executable:

  1. Using Terminal, cd into your project's SRCROOT. This is generally the directory that contains your .xcodeproj or.xcworkspace file. The directory structure of this folder should look something like this:
  2. Sample Project Structure
    MyProject // Source root
      | MyProject.xcodeproj
      | - MyProject // Contains app target source files
      | - MyLibraryTarget // Contains lib target source files
      | - MyProjectTests // Contains test files
      
  3. Create a new directory for the Codegen executable by running mkdir Codegen in Terminal. Your directory structure should now look like this:
  4. Sample Project Structure
    MyProject // Source root
      | MyProject.xcodeproj
      | - MyProject // Contains app target source files
      | - MyLibraryTarget // Contains lib target source files
      | - MyProjectTests // Contains test files
      | - Codegen // Contains your Swift Scripting files
      
  5. Using Terminal, change directories into the codegen folder, and initialize an SPM executable by using the following commands:
  6. cd Codegen
    swift package init --type executable 
    

    When this command finishes, you'll see that the Codegen folder now has new contents:

    Sample Project Structure
    MyProject // Source root
      | MyProject.xcodeproj
      | - MyProject // Contains app target source files
      | - MyLibraryTarget // Contains lib target source files
      | - MyProjectTests // Contains test files
      | - Codegen // Contains your Swift Scripting files
           | Package.swift
           | README.md
           | - Sources
               | - Codegen
           | - Tests
               | - CodegenTests
    		   
  7. Double-click Package.swift in this new folder (or run open Package.swift in Terminal). This opens the package you've just created in Xcode.
  8. Update the dependencies section to grab the Apollo iOS library:
  9. .package(name: "Apollo",
             url: "https://github.com/apollographql/apollo-ios.git", 
    .upToNextMinor(from: "0.29.0"))
    

    NOTE: The version should be identical to the version you're using in your main project.

    ALSO NOTE: Having to specify the name is a workaround for SR-12110. Hopefully once that's fixed, SPM should pick up the name automatically.

  10. For the main executable target in the targets section, add ApolloCodegenLib as a dependency:
  11. .target(name: "Codegen",
            dependencies: [                    
    .product(name: "ApolloCodegenLib", package: "Apollo"),
            ])
  12. In main.swift, import the Codegen lib and Apple's Foundation library at the top of the file:
  13. import Foundation
    import ApolloCodegenLib
  14. Run swift run in Terminal - you should still be in the same directory where the Package.swift file was checked out, and this is the proper place to run it. This will download dependencies, then build and run your package. This should create an output of "Hello, world!", confirming that the package and its dependencies are set up correctly.

Now it's time to use the executable to do some stuff for you!

Accessing your project's file tree

Because Swift Package manager doesn't have an environment, there's no good way to access the $SRCROOT variable if you're running it directly from the command line or using a scheme in Xcode.

Because almost everything the code generation can do requires access to the file tree where your code lives, there needs to be an alternate method to pass this through.

Fortunately, there's a class for that: FileFinder automatically uses the calling #file as a way to access the Swift file you're currently editing.

For example, let's take a main.swift in a folder in /Codegen/Sources, assuming following file system structure:

Sample Project Structure

MyProject // Source Root
  | MyProject.xcodeproj
  | - MyProject // Contains app target source files
  | - MyLibraryTarget // Contains lib target source files
  | - MyProjectTests // Contains test files
  | - Codegen // Contains Swift Scripting files
      | Package.swift
      | README.md
      | - Sources
          |- Codegen
              | main.swift

Here's how you obtain the parent folder of the script, then use that to get back to your source root:

let parentFolderOfScriptFile = FileFinder.findParentFolder()
let sourceRootURL = parentFolderOfScriptFile
.apollo.parentFolder() // Result: Sources folder
.apollo.parentFolder() // Result: Codegen folder
.apollo.parentFolder() // Result: MyProject source root folder

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

let cliFolderURL = sourceRootURL
.apollo.childFolderURL(folderName: "Codegen")
.apollo.childFolderURL(folderName: "ApolloCLI")
This would put the folder to download the CLI here in your filesystem:
Sample Project Structure

MyProject // SourceRoot
  | MyProject.xcodeproj
  | - MyProject // Contains app target source files
  | - MyLibraryTarget // Contains lib target source files
  | - MyProjectTests // Contains test files
  | - Codegen // Contains Swift Scripting files
      | Package.swift
      | README.md
      | - ApolloCLI // Contains downloaded typescript CLI
      | - Sources      
          | - Codegen
              | main.swift
			  

Note: We recommend adding this folder to your root .gitignore, because otherwise you'll be adding the zip file and a ton of JS code to your repo.

If you're on versions prior to 0.24.0, throw an empty .keep file and force-add it to git to preserve the folder structure. Versions after 0.24.0 automatically create the folder being downloaded to if it doesn't exist.

Now, with access to both the sourceRootURL and the cliFolderURL, it's time to use your script to do neat stuff for you!

Previous: Subscriptions
Next: Composable networking for GraphQL



Follow us on Facebook and Twitter for latest update.