Replies: 1 comment
|
Implemented as The final shape composes existing properties rather than reimplementing their logic, so validation stays with whichever property already owns it — matching the design direction laid out above. Supported configuration keys:
Every key is read independently and is optional — a missing key contributes nothing. A handful of scoped keys with genuinely invalid combinations throw a new A couple of things that came up along the way, in case they're useful for anyone tracking this:
Also added a Closing this out now that the PR is up and CI is green (all platforms + the Linux Foundation-linkage check). |
Uh oh!
There was an error while loading. Please reload this page.
Overview
Adopt
apple/swift-configurationto let a request's declarative properties be driven by external configuration (environment variables, a JSON file, in-memory defaults, etc.) instead of hardcoded Swift literals — via a newConfiguredproperty.It's already in the dependency graph
swift-configurationisn't a new dependency to evaluate in the abstract — it's already pinned transitively in this repo's ownPackage.resolved(swift-configuration 1.2.0, viaasync-http-client 1.37.0-beta.1). Worth separately confirming thatasync-http-clientbeing resolved at a-beta.1pre-release (whilePackage.swiftdeclaresfrom: "1.30.2") is intentional, independent of this proposal.Platform note
Every public symbol in the
Configurationmodule (ConfigReader,ConfigProvider,ConfigKey, ...) carries@available(Configuration 1.0, *), which expands tomacOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0. This is not a package-manifest platform floor — there's noplatforms:array inswift-configuration's ownPackage.swiftat all — it's a per-symbol availability gate.async-http-clientalready has direct precedent for handling this cleanly:HTTPClientConfiguration+SwiftConfiguration.swiftgates just its ownConfigReader-consuming extension with@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *), leaving the rest ofHTTPClient.Configurationon its normal, much older floor.Configureddoes the same: only it carries that availability annotation. Zero change to this package's ownplatforms:array, zero effect on any other type.API
Design decisions worth calling out:
bodycomposes existing properties (BaseURL,RequestMethod,Timeout,HeaderGroup,QueryGroup) rather than a customPropertyNodewriting intoMakedirectly. APropertyNode-based version, and later anAsyncProperty-wrapped version supporting a throwing/requiredbaseURLlookup, were both considered and rejected in favor of this. ThePropertyNodeversion would mean re-implementing each field's own validation (e.g.BaseURL's scheme/host checks) insideConfiguredinstead of reusing it, and every future field would need hand-writtenMake-mutation logic rather than delegating to whateverPropertyalready exists for it — composing existing properties scales better, since supporting a new field later is one moreif letbranch, not a reimplementation. ForcingbaseURLspecifically to be a required, throwing lookup was also considered (viaAsyncProperty, which supports async/throwing content) and dropped as unnecessary: every field is treated identically — ifbaseURLis missing,Configuredsimply contributes nothing for it, the same as any other absent key, andBaseURL's own existing requirement is what surfaces a genuinely missing base URL, not a special case invented here.Headers/Queryare read asstringArraypair lists ("Name: Value"/"name=value"), parsed the same wayasync-http-clientalready parsesdnsOverrides(colon-separated pairs) from aConfigReader—ConfigReaderhas no generic "read this whole[String: String]" accessor, only scalars and arrays of scalars, so this is the same precedented shape rather than an invented one.Each field is read with
if letand only included when present, soConfiguredcomposes naturally alongside explicit properties declared elsewhere in the same@PropertyBuilderblock — an explicitHeaders(...)declared after it still wins, same "last one wins" precedentBaseURL/DNSOverridealready establish.ConfigReader.scoped(to:)lets multiple endpoints share one reader under different key prefixes:Configured(appConfig.scoped(to: "myAPI")).Out of scope (v1)
Propertytype — v1 coversbaseURL/method/timeout/headers/queries; extending to others (cache policy, secure connection, proxy, etc.) is additionalif letbranches on top of the same shape, not a redesign.watchingaccess pattern (live config reloads) —Configuredreads a snapshot once per resolution, matching how every other property in this pipeline works today.Configured; every field is optional and independently absent-tolerant.Milestone: 4.2.0
All reactions