Swift Configuration

repository·main·Indexed 21 days ago

https://github.com/apple/swift-configuration

A library providing a consistent API for reading configuration across Swift applications and libraries. It abstracts configuration sources (providers) from consumption logic (readers) and supports a provider hierarchy for value resolution. Built-in providers include EnvironmentVariablesProvider, CommandLineArgumentsProvider, and various FileProviders for JSON, YAML, and Property List files. Optional features like dynamic reloading via ReloadingFileProvider and logging are available through Swift Package Manager traits.

Tokens
46.1K
Snippets
146
Records
200
Agent score
70%

What's inside Swift Configuration

  1. Explore Swift Configuration examples catalog

    main

    The repository provides several examples categorized by complexity and use case:

    • hello-world-cli-example: A simple CLI demonstrating provider hierarchy, environment variables, command-line arguments, and type-safe configuration reading.
    • reloading-example: A simple web server demonstrating live-reloading from a Kubernetes configMap, file providers, and multi-phase initialization.
  2. Use ConfigurationTesting for custom provider validation

    main
    The ConfigurationTesting library provides a suite of testing utilities designed for developers implementing custom ConfigProvider types. It uses the Swift Testing framework to provide a compatibility suite that ensures your custom provider adheres to the expected behavior and interface requirements of the Swift Configuration ecosystem.
  3. Choose between static and reloading providers

    main

    Selecting the right provider type depends on whether your configuration needs to change during runtime:

    • Reloading Providers: Use these when configuration changes must take effect without restarting the application. This is ideal for long-running services, development environments, or applications receiving updates via file deployments.
    • Static Providers: Use these when configuration is immutable once the application starts. This is ideal for containerized applications or scenarios where configuration is set once at startup.
  4. Convert integer configuration values using ExpressibleByConfigInt

    main

    You can automatically convert integer-backed configuration values into richer domain types (like Duration, enums, or custom types) by conforming those types to the ExpressibleByConfigInt protocol.

    Once a type conforms, you can use integer-based accessor methods on ConfigReader or ConfigSnapshotReader (such as int(forKey:as:), requiredInt(forKey:as:), or intArray(forKey:as:)) to perform the conversion in a single step.

    Built-in Support

    • Swift.Duration: Conforms to ExpressibleByConfigInt. The integer value is interpreted as the number of seconds. An invalid value results in nil for failable accessors.
    • RawRepresentable<Int>: Any enum conforming to RawRepresentable with an Int raw value automatically works with these conversion methods.
    // Example: Using built-in Duration support
    let timeout: Duration? = config.int(forKey: "timeout_seconds", as: Duration.self)
    
    // Example: Using an Int-backed enum
    enum Priority: Int, RawRepresentable {
        case low = 0
        case high = 1
    }
    let priority: Priority? = config.int(forKey: "priority", as: Priority.self)
  5. Choose the right ConfigProvider type

    main

    When implementing a custom configuration source, select a provider type based on how the data behaves:

    TypeUse caseExamples
    File-basedCustom file formatsJSON, YAML, TOML, XML, plist files
    ImmutableValues loaded once, never changeCommand-line arguments, in-memory values, test fixtures
    DynamicValues change over timeRemote servers, watched files

    Use the Provider suffix for all implementations. For dynamic variants, use the Reloading prefix for disk-based sources or Refetching for network-based sources (e.g., ReloadingFileProvider or RefetchingLunarProvider).

  6. Implement a custom configuration provider

    main

    To create a custom source of configuration, you must implement the ConfigProvider protocol. This involves working with several core types:

    • ConfigProvider: The main interface you implement to define how configuration is retrieved.
    • ConfigSnapshot: Represents a point-in-time view of the configuration data.
    • ConfigContent: The raw data structure containing the configuration values.
    • ConfigValue: The individual values stored within the content.
    • ConfigType: Defines the type system used for configuration values.
    • LookupResult: The result of a configuration lookup (e.g., success with a value or a failure).
    • ConfigUpdatesAsyncSequence: Used to provide a stream of configuration updates to consumers.
    • SecretsSpecifier: Used to handle sensitive data within the provider implementation.
  7. How key delimiters work in Swift Configuration

    main

    Swift Configuration uses a fixed dot (.) delimiter for splitting string-based keys into components. This behavior is hardcoded to ensure consistency across different modules and libraries.

    Because the delimiter is fixed, you should always use dots to represent hierarchy in your configuration keys. This prevents breaking changes when passing a ConfigReader from one module (e.g., an App) to another (e.g., a library), as both will interpret the dot delimiter identically.

    // Correct usage: components are split by dots
    let client = Client(config: config.scoped(to: "http.client"))
    
    // Inside the library:
    self.timeout = config.int(forKey: "read.timeout", default: 30)
    // This correctly reads components: ["http", "client", "read", "timeout"]
  8. Monitor file changes with snapshot types

    main

    The ReloadingFileProvider uses specific snapshot types to represent the state of the configuration file. When the file is modified on disk, the provider parses the new content into one of the following snapshot formats:

    • FileConfigSnapshot: The general abstraction for a configuration snapshot.
    • JSONSnapshot: A snapshot parsed from a JSON file.
    • YAMLSnapshot: A snapshot parsed from a YAML file.
    • PropertyListSnapshot: A snapshot parsed from a macOS/iOS Property List (.plist) file.
  9. Handle provider and missing value errors

    main

    Provider errors

    • Required methods (e.g., requiredString): If a provider throws an error during lookup, the method immediately throws that error to the caller.
    • Optional methods (with or without defaults): The library handles provider errors gracefully by returning nil or the provided default value.

    Missing values

    • Methods with defaults: Return the provided default value if no provider has the key.
    • Methods without defaults: Return nil if no provider has the key.
    • Required methods: Throw an error if no provider has the key.