Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
# Provide configurable packages using traits.
# Providing configurable packages using traits

Define one or more traits to offer default and configurable features for a package.
Define one or more package traits to offer default and configurable features for a package.

## Overview

Swift packages prior to Swift 6.1 offered a non-configurable API surface for each version.
With Swift 6.1, packages may offer traits, which express a configurable API surface for a package.
Starting with Swift 6.1, packages may offer traits, which express a configurable API surface for a package.

Use traits to enable additional API beyond the core API of the package.
For example, a trait may enable an experimental API, optional extended functionality that requires additional dependencies, or functionality that isn't critical that a developer may want to enable only in specific circumstances.
For example, a trait may enable an experimental API, optional extended functionality that requires additional dependencies, or functionality that isn't critical that you may want to enable only in specific circumstances.

Traits that you specify when building a package only activate within the package you're building.
If your package wants to use a trait in a dependent package, it needs to encode the traits it needs in its dependencies.

> Note: Traits should never "remove" or disable public API when a trait is enabled.

Within the package, traits express conditional compilation, and may be used to declare additional dependencies that are enabled when that trait is active.
Within the package where a trait is defined, the trait expresses conditional compilation.
Swift Package Manager exposes enabled traits as conditional blocks (for example, `#if YourTrait`) that you can use to conditionally enable imports or different compilation paths in code.

Traits are identified by their names, which are name-spaced within the package that hosts them.
Trait names are namespaced within the package that hosts them - a trait name in one package has no impact on any other package.
Trait names must be [valid swift identifiers](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure#Identifiers) with the addition of the characters of `-` and `+`.
The trait names `default` and `defaults` (regardless of any capitalization) aren't allowed to avoid confusion with the default traits that a package defines.

Enabled traits are exposed as conditional blocks (for example, `#if YourTrait`) that you can use to conditionally enable imports or different compilation paths in code.

## Overview
Don't use the trait names `default` or `defaults` (regardless of capitalization) to avoid confusion with the default traits that a package defines.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds less strict than before. We do throw an error if one tries to use those names.


### Declaring Traits
### Declare traits

Create a trait to define a discrete amount of additional functionality, and define it in the [traits](https://docs.swift.org/swiftpm/documentation/packagedescription/package/traits) property of the package manifest.
Use [`.default(enabledTraits:)`](https://docs.swift.org/swiftpm/documentation/packagedescription/trait/default(enabledtraits:)) to provide the set of traits that the package uses as a default.
If you don't define a default set of traits to enable, no traits are enabled by default.
If you don't define a default set of traits to enable, Swift Package Manager enables no traits by default.

The following example illustrates a single trait, `FeatureA`, that is enabled by default:

Expand All @@ -37,7 +39,7 @@ traits: [
// ...
```

Traits may also be used to represent a set of other traits, which allows you to group features together.
Traits can also represent a set of other traits, which allows you to group features together.
The following example illustrates defining three traits, and an additional trait (`B-and-C`) that enables both traits `FeatureB` and `FeatureC`:

```swift
Expand All @@ -46,18 +48,21 @@ traits: [
.trait(name: "FeatureA"),
.trait(name: "FeatureB"),
.trait(name: "FeatureC"),
.trait(name: "B-and-C", enabledTraits: ["FeatureB", "FeatureC"]).
.trait(name: "B-and-C", enabledTraits: ["FeatureB", "FeatureC"]),
.default(enabledTraits: ["FeatureA"]),
],
// ...
```

The traits enabled by default for the example above is `FeatureA`.
For the example above, the default trait is `FeatureA`.

> Note: Changing the default set of traits for your package is a major semantic version change if it removes API surface.
> Adding additional traits is not a major version change.

#### Mutually Exclusive Traits
Swift Package Manager treats traits as purely additive, and unifies enabled traits across all packages within the build graph.
Design your traits such that they enable additional API (and their dependencies, if needed).

#### Define mutually exclusive traits

The package manifest format doesn't support declaring mutually exclusive traits.
In the rare case that you need to offer mutually exclusive traits, protect that scenario in code:
Expand All @@ -70,11 +75,34 @@ In the rare case that you need to offer mutually exclusive traits, protect that

> Note: Providing mutually exclusive traits can result in compilation errors when a developer enables the mutually exclusive traits.

### Using traits in your code
### Depend on a package with a trait

A package dependency that doesn't specify traits in its declaration uses the package with its default traits enabled.
Add one or more traits that the package provides within the `traits` parameter for the package dependency to use that package with those traits enabled.

The following example shows depending on the `swift-configuration` with both the `defaults` and `YAML` traits enabled:

```swift
dependencies: [
.package(
url: "https://github.com/apple/swift-configuration.git",
from: "1.0.0",
traits: [
.defaults,
"YAML"
]
),
]
```

> Tip: The traits you define for a dependency are a strict, additive list of the traits that the package provides.
> If you want to use defaults and additional traits, make sure to add `.defaults` to the list of traits you specify.

### Use traits in your code

Use the name of a trait for conditional compilation.
Wrap the additional API surface for that trait within a conditional compilation block.
For example, if the trait `FeatureA` is defined and enabled, the compiler see and compile the function `additionalAPI()`:
For example, if the trait `FeatureA` is defined and enabled, the compiler sees and compiles the function `additionalAPI()`:

```swift
#if FeatureA
Expand All @@ -84,7 +112,7 @@ public func additionalAPI() {
#endif // FeatureA
```

### Using a trait to enable conditional dependencies
### Use a trait to enable conditional dependencies

You can use a trait to optionally include a dependency, or a dependency with specific traits enabled, to support the functionality you expose with a trait.
To do so, add the dependency you need to the manifest's `dependencies` declaration,
Expand Down Expand Up @@ -137,5 +165,5 @@ With the above example, the following code illustrates wrapping the import with
MyAPI.provideExtraFunctionality()
// ...
}
#endif // MyTrait
#endif // FeatureB
```
Loading