This is a major release. The single NerdzUtils module is split into four products behind a
compatibility umbrella, the standalone NerdzDate package is folded in, CocoaPods support is
removed, and thirteen defects are fixed. Several of those fixes change observable behavior, so
read Behavior changes before upgrading.
Breaking changes
- The package now vends
NerdzCore,NerdzDate,NerdzUIKit,NerdzKeychainand the
NerdzUtilsumbrella.import NerdzUtilskeeps working because the umbrella re-exports all
four, so existing code compiles unchanged. New code should depend on the specific products. - The standalone NerdzDate package is discontinued and lives here as the
NerdzDateproduct. Its
consumers change the package URL. A consumer cannot depend on both the old package and this one
at the same time, because Swift Package Manager rejects two packages vending the same product
name in one graph. UIColor.init(hex:alpha:)is now failable and itsalphaparameter is optional:
init?(hex: String, alpha: CGFloat? = nil). UseUIColor.hex(_:alpha:fallback:)for the
non optional path.JSONDecoder.DataDecodingStrategyno longer conforms toNZExtensionCompatible. It declared no
members, so its.nznamespace was empty, and the conformance prevented downstream code from
declaring its own.AtomicAsyncOperation.Action's finish closure,UIViewController.nz.easilyAddChild's
configurationAction, theWindowConfiguratoinActiontypealias and theLoadableImage.fromUrl
completion gain@Sendableor@MainActor, which Swift 6 requires.- CocoaPods support is removed. The podspec is deleted.
swift-tools-versionmoves from 5.2 to 5.9, so building the package requires Xcode 15. Runtime
deployment targets are unchanged.
Behavior changes
Date.nz.start(of:)andend(of:)returned a date in the previous unit for.month,.year,
.quarterand.era, because the implementation wrote zero into one based calendar fields. For
examplestart(of: .month)on 2020-09-13 returned 2020-08-31. Both now use
Calendar.dateInterval(of:for:)and return correct results. Code that compensated for the old
behavior must drop the compensation. The week cases still snap to Sunday and ignore the
calendar'sfirstWeekday, which is unchanged and covered by a test.String.nz.isVersion(...)returned inverted results whenever the two versions had a different
number of components, so"2.0".nz.isVersion(greaterThan: "1.9.9")wasfalse. Comparison is
now component wise and numeric, which also fixes double digit components, so"1.10"is
correctly greater than"1.9".- Assigning
nilto an optionalKeychainPropertynow removes the keychain entry. Previously it
stored the stringnull, becauseJSONEncoderencodes a top levelnilas a valid JSON
fragment. A property with a non nilinitialvalue therefore now reports that initial value
after anilassignment, where it previously read back asnil. Calendar.Component.nz.allComponentsno longer lists.timeZonetwice and now includes
.weekdayOrdinal, plus.isLeapMonthon systems that have it.UIColor(hex:)no longer returns black for unparseable input. It returnsnil.Bundle.nz.appVersionnow reads the bundle it is called on rather than always reading
Bundle.main, so a framework bundle reports its own version.Bundle.main.nz.appVersionis
unchanged.DelayedAction.cancel()now returnsfalsewhen nothing is pending. It previously returned
truefor every call after the firstperform, including after the action had already run,
because the finished work item was never cleared.
Fixed
DispatchQueue.nz.oncedeadlocked when called from more than one thread. It synchronized on a
Swift metatype, which does not bridge to a stable lock object, so the lock was acquired and never
released. It now uses a lock held by a dedicated storage type, and a test drives it from many
threads concurrently.AtomicAsyncOperationwedged permanently when its action called the finish closure
synchronously, because the action ran while the semaphore was held and the finish path waited on
that same semaphore. No completion ever fired. Synchronous and asynchronous actions both work now.JSONDecoder.DateDecodingStrategy.nz.customISO8601was unreachable. The conformance was declared
onDataDecodingStrategywhile the member was declared onDateDecodingStrategy, so the symbol
could not be called at all.UIControl.nz.addAction(for:_:)left a dangling target. The closure holder was associated with
the temporary namespace struct rather than the control, under a key that was only valid for the
duration of the call, so it was released immediately.UIColor(hex:)ignored the result of its scanner, did not support three digit shorthand, and
shifted the colour channels for eight digit notation while discarding the embedded alpha.- Keychain read, write and encode failures were swallowed silently, so a failed write left the
caller with no signal. DefaultsPropertyusedsetValue(_:forKey:), which is key value coding rather than the
UserDefaultsAPI.- Tests covering navigation completions on the non animated path, where the completion runs
without waiting for a transition coordinator. The animated path still has no test, because it
needs a real window and a real transition. - Navigation completions passed to
pushViewController,popViewController,
popToViewControllerandpopToRootViewControllercould silently never run. They were attached
withCATransaction.setCompletionBlock, but a navigation transition is driven by the controller's
transition coordinator rather than by the surrounding transaction. They now run through the
transition coordinator, and run immediately when there is no transition in flight.
Added
- Swift Testing suites for all four products: 278 tests on macOS and 173 on an iOS simulator. Line
coverage is 98.05% for NerdzCore, 97.88% for NerdzDate and 100% for NerdzKeychain. KeychainPropertyError,KeychainPropertyErrorHandlerand anonError:parameter on
KeychainProperty.init, so failures are observable. The default handler does nothing, so
existing call sites are unaffected.UIColor.hex(_:alpha:fallback:), plus support for three and four digit shorthand, eight digit
notation with an embedded alpha, a leading#, and surrounding whitespace.- DocC catalogs for NerdzCore and NerdzDate, published to GitHub Pages on release.
- GitHub Actions CI covering Linux, macOS and an iOS simulator, plus a tag driven release workflow.
- Platform declarations for iOS, macOS, tvOS, watchOS and visionOS, where the package previously
declared iOS only. - Rewritten README documenting the product layout, with every snippet compiled against the real API.
Removed
- The CocoaPods podspec.
- A committed
docs/directory of generated documentation, its Firebase hosting configuration and
workflows, the Xcode project,Package.resolvedand.swiftpm.
Notes
NerdzKeychaincannot build on Linux, because KeychainAccess imports Security.NerdzCoreand
NerdzDatedo build there, with the exception ofDispatchQueue.nz.once(per:token:action:),
which needs the Objective C runtime and is compiled out.Datevalues stored throughKeychainPropertyandDefaultsPropertyare encoded as ISO8601 and
lose sub second precision. This is unchanged, and it is not corrected here because a decoder
configured for fractional seconds rejects timestamps that lack them, which would break reading
values already stored by 1.x.