✨ New: SpellbookCrash
A new library product that attaches arbitrary application context to macOS/iOS crash reports via the __crash_info section, so a crash report tells not only where the process died, but also what it was doing.
CrashReportAugmentation.withMessage("Handling AUTH_OPEN pid=(pid) path=(path)") {
handle(message)
}
- CrashInfo — Swift-side mapping of the __DATA_DIRTY,__crash_info section.
- CrashReportAugmentation — thread-safe scoped/manual messages routed to the Application Specific Signature or Application Specific Backtrace block (withMessage, addMessage, removeMessage, removeAll, updateCrashInfo).
- Optional execution-context tagging (includeThreadInfo): task name, queue label, thread name, pthread id.
- Toggles: isEnabled, defaultTarget, updateCrashInfoAutomatically, injectable crashInfo for tests.
🚀 Additions
Collections & sequences
- Sequence.recursiveMap(at::) / recursiveCompactMap(at::) for tree-shaped data.
- stableSorted(by:) / stableSort(by:), by closure or KeyPath.
- rotate(shift:) / rotated(shift:).
- removeDuplicates() / removingDuplicates() — by Equatable, Hashable (O(n)) or custom isEqual.
- firstBefore(:) / firstAfter(:), with Equatable and custom-comparator overloads.
- BidirectionalCollection.lastMapped(where:) — the reversed + firstMapped combo.
- Sequence.min(by:) / max(by:) taking a KeyPath.
- Dictionary[key, create:] subscript and createValue(create:forKey:) — get-or-insert.
Types & protocols
- EmptyInitializable, conformed by standard collections, String, integer/floating-point types (incl. Float16, Int128/UInt128 where available), plus Pair and KeyValue.
- SelfIdentifiable — Identifiable where id is the value itself.
- Clamped now works with partial ranges via the new ClampingRange protocol (ClosedRange, PartialRangeFrom, PartialRangeThrough); Clamped is Sendable when Value is.
- DictionaryCoder — encode/decode Codable values to and from dictionaries via a JSON or plist proxy.
- TimeInterval.milliseconds/seconds/minutes/hours/days; URLResponse.asHTTP().
- RunEnvironment.flags as a Set; RunEnvironment/BuildEnvironment are now Sendable.
Observing / Logging / Graphics
- ValueObservable.ObservableObject and .observableObject, mirroring ValueStore.
- SpellbookLogDestination.print(minLevel:transform:) / .nslog(minLevel:transform:) take a custom record-to-string transform.
- CGPoint ± CGSize operators; CGSize.square(_:), CGSize.min/.max, CGRect.square(offsets:size:).
🐞 Fixes
- ProgressValue.ratio returns 0 instead of NaN when total is 0.
- Task.sleep(forTimeInterval:) no longer hangs on cancellation on older OSes — uses sleep(for:) where available, otherwise a cancellation-handler workaround for swiftlang/swift#88259 (swiftlang/swift#88259).
- Fixed Intel builds: Float16 conformance gated behind #if arch(arm64).
- _SpellbookFoundationObjC is now exposed in the SpellbookFoundation product, fixing client linkage; restored @_implementationOnly imports.
- Collection[safe:] moved from RangeReplaceableCollection to Collection and checks indices — correct for non-integer/non-contiguous indices.
- removeRandom() / popRandom() no longer require Index: FixedWidthInteger.
- Fixed an Xattr test and a Swift 6.2 weak let compiler-version check.
⚡ Performance
- DataProtocol.hexString rewritten without String(format:).
💥 Breaking changes
- SBPredicate removed. APIs that took one now take a plain closure: popAll(where:), filter(remaining::), removeFirst(where:), updateFirst(:where:). The firstIndex(:) / indices(:) predicate overloads are gone — use the stdlib firstIndex(where:) / indices(where:).
- rethrows → typed throws (throws(E)) across the library: Benchmark.measure, Synchronized.read/write/…/initialize, NSLocking.withLock, DispatchQueue.syncOnMain, Resource.withValue, Change.map/mapUnchecked, Pair/KeyValue/Either map methods, Sequence.mutatingMap/firstMapped/reduce(into:keyedBy:), Array(count:create:), XCTestCase.withScope.
- Deprecated Task.sleep(seconds:) / sleep(milliseconds:) removed — use Task.sleep(for:).
- Sequence.reduce(into:keyedBy:) KeyPath overloads removed; use the closure overload.
- Array(count:create:) @autoclosure overload removed.
- Optional.coalesce split into value-taking and closure-taking overloads.
- popAll(where:) on RangeReplaceableCollection is now throws.
- ValueStore.ObservableObject / ValueObservable.ObservableObject are final.