• SwiftPM command plugin
  • Xcode 14+
  • No dependencies
  • MIT

A scheme for every product,
in one command.

Scheme Generator reads the products of your Package.swift and writes a shared Xcode .xcscheme for each one, right where your workspace looks for them. Add a module, run the plugin, and its scheme is there. Remove one, and its scheme goes away.

Terminal
$ swift package --allow-writing-to-package-directory \
    scheme-generator
Found 42 products.
Schemes that will be removed:
LegacyNetworking
Schemes that will be created:
OnboardingFeature
SettingsFeature
Finished

Why

Modular apps have many products.
Xcode only shows the schemes you made.

Split an app into a package of feature modules, as with The Composable Architecture, and each module deserves its own scheme: build it alone, run its tests, preview it fast. Xcode's autocreated schemes are per user and get out of step; shared schemes written by hand drift the moment someone adds or renames a product. Scheme Generator makes the schemes a function of Package.swift.

Read

It loads schemeGenerator.json and the list of products SwiftPM reports for your package.

Plan

It compares them with the .xcscheme files already in schemesDirectory: new products get a scheme, obsolete schemes are removed, excluded ones are left alone.

Write

It writes each scheme from a template that builds, tests, profiles and archives that product, and remembers the product list so an unchanged package is a no-op.

Features

Small, predictable, out of your way.

Every product, one scheme each

Libraries and executables alike get a shared scheme with build, test, launch, profile, analyze and archive actions, named after the product.

Cleans up after itself

With removeNotGeneratedSchemes, a scheme whose product left Package.swift is deleted. excludedSchemes protects the hand-made ones.

A no-op when nothing changed

It keeps a hash of the product list and stops early when it is unchanged, so it is cheap to run on every build or on CI. overwriteAlreadyGeneratedSchemes forces a refresh.

Xcode or terminal

Right-click your package in Xcode's navigator, or run swift package scheme-generator from a script or a CI job. Messages appear in Xcode's Report navigator.

Sandboxed and dependency-free

A plain SwiftPM command plugin with no package dependencies. It asks for permission before writing, and writes only to the folder you configure.

Fast for large packages

Planning and rendering 1,000 schemes takes under 2 ms; the disk is the only cost you will notice. Measured with package-benchmark.

Configuration

One JSON file at the root of your package.

Name it schemeGenerator.json, or pass --confFile other.json once: the plugin remembers the name. If it is missing, the plugin writes a default one for you to fill in.

schemeGenerator.json
{
  "schemesDirectory": "Project/project.xcworkspace/xcshareddata/xcschemes",
  "excludedSchemes": ["Target1Tests", "Target2Tests"],
  "removeNotGeneratedSchemes": true,
  "overwriteAlreadyGeneratedSchemes": false,
  "verbose": true
}
KeyMeaning
schemesDirectoryWhere schemes are written. Required. Point it into a workspace's xcshareddata/xcschemes to share them.
removeNotGeneratedSchemesDelete schemes whose product is no longer in Package.swift.
overwriteAlreadyGeneratedSchemesRewrite schemes that already exist.
excludedSchemesNames of existing scheme files the plugin must never remove.
verbosePrint the configuration and every scheme it creates or removes.

Install

Add the package, run the command.

Package.swift
dependencies: [
  .package(url: "https://github.com/mackoj/SchemeGeneratorPlugin.git", from: "0.5.8"),
]
Terminal
swift package --allow-writing-to-package-directory scheme-generator
  1. Add the dependency to the package whose products need schemes. No target needs to use it.
  2. Create schemeGenerator.json next to Package.swift.
  3. In Xcode, right-click the package and choose Scheme Generator, then Allow Command to Change Files. In a terminal or on CI, pass --allow-writing-to-package-directory.
  4. If the schemes go into a workspace, restart Xcode to see them all.

Requires Xcode 14 or later (swift-tools-version 5.7) on macOS 12 or later.