Skip to content

Updated & Improvements

Latest

Choose a tag to compare

@Alkenso Alkenso released this 27 Jul 10:23

✨ 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.