GitHub Repo stars GitHub GitHub issues GitHub Workflow Status
Twitter Follow Mastodon Follow YouTube Channel Subscribers
GitHub Downloads (specific asset, latest release)
Simplify the management of your Package.swift file with a type-safe, modular DSL:
import PackageDescription let package = Package( name: "MyApp", entries: { AppTarget() NetworkingModule() DatabaseModule() }, dependencies: { Alamofire() SQLite() }, testTargets: { AppTests() NetworkingTests() }, swiftSettings: { InternalImportsByDefault() } ) .supportedPlatforms { WWDC2023() } .defaultLocalization(.english)
- Modular Organization: Split your package definition across multiple files for better maintainability
- Type Safety: Leverage Swift's type system to catch configuration errors at compile time
- Better Discoverability: Clear directory structure makes it easy to find and modify package components
- Reduced Complexity: Simplified syntax for defining products, targets, and dependencies
- Easy Maintenance: Update individual components without touching the entire Package.swift file
Check out BushelKit, which uses PackageDSL to manage its complex package structure with multiple products and dependencies. Its Package/Sources directory demonstrates how to organize:
- Multiple product targets
- Nested dependencies
- Platform-specific code
- Test targets
- Documentation targets
Package
└── Sources
├── Dependencies
│ ├── ArgumentParser.swift
│ ├── DocC.swift
│ ├── RadiantKit
│ │ ├── RadiantDocs.swift
│ │ ├── RadiantPaging.swift
│ │ └── RadiantProgress.swift
│ └── RadiantKit.swift
├── Index.swift
├── Platforms
│ └── WWDC2023.swift
├── Products
│ ├── BushelCommand.swift
│ ├── BushelDocs.swift
│ └── ... more products ...
├── Targets
│ ├── BushelArgs.swift
│ └── ... more targets ...
└── Tests
├── BushelFactoryTests.swift
└── ... more tests ...
Create a minimal package structure:
MyPackage/
└── Package/
└── Sources/
├── Products/
│ └── AppTarget.swift
├── Dependencies/
│ └── Alamofire.swift
└── Index.swift
Package/Sources/Products/AppTarget.swift:
struct AppTarget: Product, Target { var dependencies: any Dependencies { Alamofire() CoreModule() } }
Package/Sources/Dependencies/Alamofire.swift:
struct Alamofire: PackageDependency { var dependency: Package.Dependency { .package( url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0" ) } }
Package/Sources/Index.swift:
let package = Package( entries: { AppTarget() } )
./package.sh . --version 5.9-
Download the
package.shscript from our latest releaseOr using curl:
curl -LO https://github.com/brightdigit/PackageDSL/releases/download/latest/package.sh
-
Make the script executable:
chmod +x package.sh
The script accepts the following arguments:
./package.sh [PACKAGE_DIR] [OPTIONS]
PACKAGE_DIR: Path to your package directory (required)
--version <version>: Specify Swift tools version (default: 6.0)--minimize: Minimize the output by removing comments and extra whitespace
Generate Package.swift for the current directory using Swift 5.9:
./package.sh . --version 5.9Generate a minimized Package.swift for a specific package:
./package.sh ~/Projects/MyPackage --version 5.9 --minimizeYou can create a test target with the TestTarget protocol:
struct BushelCoreTests: TestTarget { var dependencies: any Dependencies { BushelCore() } }
Then add it to your list of test targets using the testTarget argument:
let package = Package { BushelCommand() BushelLibraryApp() BushelMachineApp() BushelSettingsApp() BushelApp() } testTargets: { BushelCoreTests() // right here }
Swift settings can be added to all the targets using swiftSettings argument on the Package constructor or to a specific target by implementing using the swiftSettings property:
struct BushelFactory: Target { var dependencies: any Dependencies { BushelCore() BushelMachine() BushelLibrary() BushelLogging() BushelData() } var swiftSettings: [SwiftSetting] { InternalImportsByDefault() } }
For more details check out the documentation on:
To add resources simply implement the resources property on the Target:
struct BushelLocalization: Target { var resources : [Resource] { .process("Styling/Colors/Colors.xcassets") .process("Styling/Fonts/FontsFiles") .process("Images/Images.xcassets") .process("Images/whiteLoading.json") .process("Images/primaryLoading.json") } }
Right now there are two modifier methods to do this. defaultLocalization which takes in a LanguageTag and supportedPlatforms which can take in a list of platforms or a PlatformSet.
A PlatformSet is useful if use a to define a set of platforms for a specific year such as:
struct WWDC2023: PlatformSet { var body: any SupportedPlatforms { .macOS(.v14) .iOS(.v17) .watchOS(.v10) .tvOS(.v17) } }
Rather then define your platforms as:
let package = Package { ... } .supportedPlatforms { .macOS(.v14) .iOS(.v17) .watchOS(.v10) .tvOS(.v17) }
Each target can implement the platforms property:
struct MacOnlyTarget: Target { var platforms: [SupportedPlatform] { SupportedPlatform.macOS(.v13) } }
The DSL is used only at development time to generate your Package.swift. There's no runtime impact on your package or its users.
- Create the
Packagedirectory structure - Move each product, target, and dependency into separate files
- Run the generator script to create your new
Package.swift - Compare the generated file with your original to ensure everything transferred correctly
- joshdholtz for inspiration with DeckUI