Introducing Swift Configuration

Indeed, this is a space I'd like us to explore as well - can you link you prototype in the tracking issue please? Macro-based declarative config · Issue #31 · apple/swift-configuration · GitHub

Why would you want to use this in a native app though?

The library was primarily designed for Swift Server use cases, but I could see someone potentially using it in apps for hot reloading feature flags for A/B testing and other server-driven configuration, or when using existing libraries that natively integrate with Swift Configuration.

I was curious after your talk at Server Side Swift! Unfortunately wasn’t able to catch you before the end of the conference.
From what I’ve seen the keys will be strings. Is there a way to do discovery on the keys being used? In order to find what I can configure when using a certain library for example?
Technically a library could expose an enum with config keys of course but that’s up to them.
Could there be a way to enforce this/make it slightly “safer”?

1 Like

That's an open question. Currently, we recommend libraries document their keys in the doc comments of the method that takes a config reader: https://swiftpackageindex.com/apple/swift-configuration/0.1.1/documentation/configuration/best-practices#Document-configuration-keys

Separately, enabling access reporting allows you to see exactly which config keys your whole application is reading, and from where in your code: https://swiftpackageindex.com/apple/swift-configuration/0.1.1/documentation/configuration/troubleshooting#Access-reporting

These work today. For the future, we’re also discussing some macro-based approach for annotating types that synthesize such an initializer themselves. That could make things clearer too: Macro-based declarative config · Issue #31 · apple/swift-configuration · GitHub

But none of these will be perfectly reliable, as at the end of the day, a library can make dynamic decisions at runtime about which keys to read, and only read specific keys if a different key has a specific value. So for a user of a library that uses Swift Configuration, I’d recommend: read the doc comments of the library’s convenience method that takes a config reader. And if you’re seeing unexpected behavior, enable access reporting to know exactly what is being read and from where.

1 Like

That’s awesome — a unified configuration system is something Swift really needed.

1 Like

Is there a plan to add a UserDefaults provider? If so, how will Swift Configuration and UserDefaults decide which one parses the command line arguments? Relatedly, is there any advice for how to use both Swift Configuration and ArgumentParser in the same app?

We haven’t been asked for it yet, so if you’d like us to have one, please file an issue and we’d also welcome a PR :slight_smile: Duplicating an existing one (like the JSONProvider) and making it work for UserDefaults shouldn’t be too much work.

That depends on which providers you give your ConfigReader and in what order. You can use CommandLineArgumentsProvider where you need it in the priority list, as well as the hypothetical UserDefaults one. The former looks for --foo value and the latter for -foo value so they shouldn’t clash if you use both.

Yeah this has come up when discussing it with @nnnnnnnn. My current recommendation would be: if you only need to read command-line arguments in an executable, and know all the options you’ll want to read at compile time, Swift Argument Parser is a better experience. If you instead need to run from different sources, read configuration from libraries, or don’t know all option names you’ll read at compile time, Swift Configuration might work better for you.

Swift Configuration is an abstraction that provides a lowest common denominator feature set, which is useful if you have multiple different sources. If you just need one, it’s probably better to use the specialized library for that source.

1 Like

An interesting wrinkle about UserDefaults is its support for “forced” settings that can be specified by an MDM administrator. Without a way for UserDefaults to override values from any other provider, refactoring from using UserDefaults directly to using a UserDefaults provider with Swift Configuration might lead to security issues for enterprise users.

As a framework engineer, I’m sensing some fun design work to be had in this space. :smiley: But I’ll curtail my runaway brain for now.

2 Likes

Since the precedence of config providers is always controlled by the operator, they can ensure to put a UserDefaults as the first provider in the list, guaranteeing that it’ll always win out over any other provider.

But I don’t think we have a way to ensure that statically, and config providers don’t have a way to inspect or influence other config providers used by their common parent config reader.