Naming and name resolution. Every change here is a compiler error rather than a silent behaviour
change, so the build says where. See Changes in 2.1.
Removed
#ZerkImport(module:). The generated file now takes its imports from the files it reads:
every module imported by a file that names a type from outside this one. Restating them by hand
was the part that could be forgotten, and forgetting it was a build failure in generated code.
Delete the declarations; nothing replaces them.
Renamed
@ZerkAliasand#ZerkAlias<A, B>()are now@InjectableAliasand
#InjectableAlias<A, B>(). Behaviour is unchanged. No macro carriesZerkin its name any
more, so the whole set reads the same way.- The documented namespace for imported declarations is spelled
ImportedInjectablesrather than
ZerkImportsthroughout the examples. It is a name you choose — Zerk never reads it — so
nothing breaks either way.
Fixed
- A key's name is now read in the scope it was written in. Swift looks a bare type name up
innermost-first, and the generated file lives at file scope, so the two disagreed for anything
nested:init(config: Config)insideLiveFeedemittedconfig: Config, which fails to
compile ascannot find type 'Config' in scope. The name is now qualified —
config: LiveFeed.Config— with only the base of a dotted path moving, since that is the part
Swift looks up. Where the bare name also existed at file scope, Zerk had been matching the
wrong key and running its effect and conditional-compilation checks against the wrong provider. - Two imported keys sharing a bare name stay two keys.
ModuleA.ConfigandModuleB.Config
were merged and then reported as'Config' is imported more than once, against two imports
naming different types. - A local declaration shadows an imported name of the same spelling. A module declaring
Servingand importing aCore.Servingwas told the key wasboth imported and declared—
for source Swift compiles as written, where the local declaration simply shadows the import. - A type named after a module is no longer read as a module qualifier. Declaring
Core
shadows the module everywhere in that module, soCore.Servingnames that type's member;
stripping the qualifier emitted an extension over a different type than the call site used. - A cancelled caller no longer builds a second kept instance.
ZerkAsyncBox's non-throwing
path let a cancelled caller build its own instance, which the box never publishes — correct on
an empty box, wrong while a build is in flight, where it produced a private second instance of
something kept while every other caller shared the first. A cancelled caller now builds its own
only when there is nothing to join. @Injectedon a property of an@Observabletype reporteda global has no such moment,
naming storage the developer never wrote. It now names the problem and the fix: mark the
property@ObservationIgnored, which leaves it stored.@Injectedalongside a property wrapper produced only the compiler's
init accessor cannot refer to property, which mentions neither the wrapper nor Zerk. It now
names the wrapper and the spelling that works —
@StateObject var model = Zerk<Key>.inject().
Added
swift package zerk settings— writesZerkSettings.jsonfrom an Xcode target's build
settings instead of from memory. The plugin API cannot read build settings, which is why the
file exists at all;xcodebuild -showBuildSettingscan, so this maps its answer onto the four
keys that mirror one and leavesvalueInjectionMethod, which mirrors nothing, alone. A setting
the target does not set is left out, so Zerk's default applies. Needs Xcode. See
zerk settings.
Changed
swift-tools-versionis 6.2, up from 6.1. It was never the real floor: a Swift 6.2
toolchain has always been required, because interjection points are named with raw identifiers
(SE-0451) that land in the generated file your toolchain compiles. The manifest now says so, so
an older SwiftPM refuses it up front instead of failing later inside generated code.ZerkSettings.jsonis decoded rather than deserialized and cast. The type guards used to
read aJSONSerializationAny, where a JSON boolean and a JSON number are indistinguishable
in both directions —true as? Intis1and1 as? Boolistrue— so telling them apart
neededCFGetTypeID(value as CFTypeRef) == CFBooleanGetTypeID().JSONDecoderdraws the
distinction in the language instead, and the messages for a malformed key are unchanged.- That was also the one thing in Zerk only Darwin could answer, so the package now has no
Apple-only dependency outside#if canImportguards. CI builds and tests it on Linux.
Documentation
- New SwiftUI and
@Observablepage:@ObservationIgnored, property
wrappers, and registering a view model. - New "Storage a property does not have" section in
Diagnostics, covering all four cases where@Injectedhas no
storage to initialize. - A "Changes in 2.1" section in Migration.
- This file.
Full Changelog: 2.0.0...2.1.0