Package Generator reads the imports in every target of a heavily modularized Swift project and writes the targets, test targets, products and dependencies of your Package.swift. You keep a short YAML file; the plugin keeps the manifest in step with the code.
pragmaMark: true spaces: 2 packageDirectoryTargets: - path: Sources/Core targets: - name: Core - name: CoreTests type: test - path: Sources/Features targets: - name: Auth parameters: - 'resources: [.process("Resources")]' - name: AuthTests type: test
Why
Splitting an app into dozens or hundreds of SwiftPM targets pays off in build times, previews and boundaries. Then every new import means editing a dependencies: array by hand, and the manifest drifts from the code.
is one more line to add to Package.swift, in the right target, with the right .product(name:package:) identity.
The plugin derives dependencies from the source, so a removed import disappears from the manifest on the next run.
to review: a YAML or JSON config listing folders and targets. Paths, test pairing and product names are inferred.
How it works
Package Generator is a pair: this plugin, which you add to your package, and Package Generator CLI, the binary it runs to read your sources. The plugin downloads the CLI for you as a SwiftPM binary target.
packageGenerator.yaml, .yml or .json and resolves each target's folder.Package.swift.List your folders and targets in packageGenerator.yaml. Add per-target parameters, exclusions or product names where you need them.
Right-click your package in Xcode and choose Package Generator, or run the command below in a terminal or on CI.
Set dryRun: true to write Package_generated.swift and diff it first. Output is deterministic, so diffs stay small.
Sources/Features/Auth/Login.swift
import Core import ComposableArchitecture import SwiftUI // …
Terminal
swift package plugin \ --allow-writing-to-package-directory \ package-generator
Generated Package.swift (excerpt)
// MARK: - Generated 2 packages // MARK: - Targets package.products.append(contentsOf: [ .library(name: "Auth", targets: ["Auth"]), .library(name: "Core", targets: ["Core"]) ]) // MARK: - Products package.targets.append(contentsOf: [ // … the Core group: Core, CoreTests … // MARK: - // MARK: Features .target( name: "Auth", dependencies: [ "Core", .product(name: "ComposableArchitecture", package: "swift-composable-architecture") ], path: "Sources/Features/Auth", resources: [.process("Resources")] ), .testTarget( name: "AuthTests", // … ) ])
Features
Local targets become "Name"; products from your root dependencies become .product(name:package:) with the URL-derived package identity SwiftPM expects.
A built-in list of about 270 Apple frameworks is skipped, but only after external products are checked, so a package product named Charts is never lost.
parameters and additionalDependencies go into the manifest as written, for resources, Swift settings, macros and plugins that no import reveals.
Set libraryType to dynamic or static globally or per target, and rename a product with productName when Xcode needs the two to differ.
Sorted products and targets, optional // MARK: groups per folder, a custom header file, and your indentation.
leafInfo annotates each target with its dependency counts and flags the heaviest; unusedThreshold warns about modules nobody imports.
FeatureTests is linked to Feature by name, with regularTargetName for the exceptions.
The legacy targetsParameters map and boolean verbose are migrated in memory. See the migration guide.
Rendering a 1,000-target manifest takes under 10 ms, measured with package-benchmark. See performance.
Install
In the Package.swift you want generated. Requires Swift 6.2 (Xcode 26) on an Apple silicon Mac.
dependencies: [ .package(url: "https://github.com/mackoj/PackageGeneratorPlugin.git", from: "1.0.6"), ]
Create packageGenerator.yaml next to it, like the one at the top of this page. If none exists, the first run writes a packageGenerator.json template.
In Xcode, right-click the package and choose Package Generator. From a terminal or CI:
swift package plugin --allow-writing-to-package-directory package-generator
# or with an explicit config
swift package plugin --allow-writing-to-package-directory package-generator --confFile myconfig.yaml