Library
Configuration
A Swift library for reading configuration in applications and libraries.
Overview
Swift Configuration defines an abstraction between configuration readers and providers.
Applications and libraries read configuration through a consistent API, while you set up the actual provider once at your application’s entry point.
For example, to read the timeout configuration value for an HTTP client, check out the following examples using different providers:
For a selection of more detailed examples, read through Example use cases.
For a video introduction, check out our talk on YouTube.
You can combine these providers to form a hierarchy. For details, check out Configuration.
Quick start
Add the dependency to your Package.swift:
.package(url: "https://github.com/apple/swift-configuration", from: "1.0.0")
Add the library dependency to your target:
.product(name: "Configuration", package: "swift-configuration")
Import and use in your code:
import Configuration
let config = ConfigReader(provider: EnvironmentVariablesProvider())
let httpTimeout = config.int(forKey: "http.timeout", default: 60)
print("The HTTP timeout is: \(httpTimeout)")
Package traits
This package offers additional integrations you can enable using package traits. To enable an additional trait on the package, update the package dependency:
.package(
url: "https://github.com/apple/swift-configuration",
from: "1.0.0",
+ traits: [.defaults, "YAML"]
)
Available traits:
Supported platforms and minimum versions
The library is supported on Apple platforms, Linux, and Android.
| Component |
macOS |
Android |
Linux |
iOS |
tvOS |
watchOS |
visionOS |
| Configuration |
✅ 15+ |
✅ API 28+ |
✅ |
✅ 18+ |
✅ 18+ |
✅ 11+ |
✅ 2+ |
Key features
Three access patterns
The library provides three distinct ways to read configuration values:
Get: Synchronously return the current value available locally, in memory:
let timeout = config.int(forKey: "http.timeout", default: 60)
Fetch: Asynchronously get the most up-to-date value from disk or a remote server:
let timeout = try await config.fetchInt(forKey: "http.timeout", default: 60)
Watch: Receive updates when a configuration value changes:
try await config.watchInt(forKey: "http.timeout", default: 60) { updates in
for try await timeout in updates {
print("HTTP timeout updated to: \(timeout)")
}
}
For detailed guidance on when to use each access pattern, see Choosing the access pattern. Within each of the access patterns, the library offers different reader methods that reflect your needs of optional, default, and required configuration parameters. To understand the choices available, see Choosing reader methods.
Providers
Built-in providers
The library includes comprehensive built-in provider support:
Community providers
You can also implement a custom ConfigProvider.
Provider hierarchy
In addition to using providers individually, you can create fallback behavior using an array of providers. The first provider that returns a non-nil value wins.
The following example shows a provider hierarchy where environment variables take precedence over command line arguments, a JSON file, and in-memory defaults:
// Create a hierarchy of providers with fallback behavior.
let config = ConfigReader(providers: [
// First, check environment variables.
EnvironmentVariablesProvider(),
// Then, check command-line options.
CommandLineArgumentsProvider(),
// Then, check a JSON config file.
try await FileProvider<JSONSnapshot>(filePath: "/etc/config.json"),
// Finally, fall back to in-memory defaults.
InMemoryProvider(values: [
"http.timeout": 60,
])
])
// Uses the first provider that has a value for "http.timeout".
let timeout = config.int(forKey: "http.timeout", default: 15)
Hot reloading
You can periodically reload configuration in long-running services with ReloadingFileProvider:
let provider = try await ReloadingFileProvider<JSONSnapshot>(filePath: "/etc/config.json")
// Omitted: add provider to a ServiceGroup
let config = ConfigReader(provider: provider)
try await config.watchInt(forKey: "http.timeout", default: 60) { updates in
for try await timeout in updates {
print("HTTP timeout updated to: \(timeout)")
}
}
Read Using reloading providers for details on how to receive updates as configuration changes.
Namespacing and scoped readers
The built-in namespacing of ConfigKey interprets "http.timeout" as an array of two components: "http" and "timeout". The following example uses scoped(to:) to create a namespaced reader with the key "http", to allow reads to use the shorter key "timeout":
Consider the following JSON configuration:
{
"http": {
"timeout": 60
}
}
// Create the root reader.
let config = ConfigReader(provider: provider)
// Create a scoped reader for HTTP settings.
let httpConfig = config.scoped(to: "http")
// Now you can access values with shorter keys.
// Equivalent to reading "http.timeout" on the root reader.
let timeout = httpConfig.int(forKey: "timeout")
Debugging and troubleshooting
Use AccessReporter to log all accesses to a config reader:
let logger = Logger(label: "config")
let config = ConfigReader(
provider: provider,
accessReporter: AccessLogger(logger: logger)
)
// Now all configuration access is logged, with secret values redacted
You can also add the following environment variable to emit access logs to a file without any code changes:
CONFIG_ACCESS_LOG_FILE=/var/log/myapp/config-access.log
and then read the file:
tail -f /var/log/myapp/config-access.log
Check out the built-in AccessLogger, FileAccessLogger, and Troubleshooting and access reporting.
Handling Secrets
The library provides built-in support for handling sensitive configuration values securely:
// Mark sensitive values as secrets to prevent them from appearing in logs
let privateKey = try snapshot.requiredString(forKey: "mtls.privateKey", isSecret: true)
let optionalAPIToken = config.string(forKey: "api.token", isSecret: true)
When you mark values as secrets, the library automatically redacts them from access logs and debugging output. Read Handling secrets correctly for guidance on best practices for secrets management.
Consistent snapshots
Retrieve related values from a consistent snapshot using ConfigSnapshotReader, which snapshot() returns.
This ensures that you read multiple values from a single snapshot inside each provider, even when using providers that update their internal values. For example by downloading new data periodically:
let config = /* a reader with one or more providers that change values over time */
let snapshot = config.snapshot()
let certificate = try snapshot.requiredString(forKey: "mtls.certificate")
let privateKey = try snapshot.requiredString(forKey: "mtls.privateKey", isSecret: true)
// `certificate` and `privateKey` are guaranteed to come from the same snapshot in the provider
Extensible ecosystem
Any package can implement a ConfigProvider, making the ecosystem extensible for custom configuration sources.
Topics
Essentials
Readers and providers
Built-in providers
Creating a custom provider
Configuration keys
Troubleshooting and access reporting
Value conversion
Contributing
Extended Modules