Once I had moved most of my code over to structured concurrency and the Observation framework, I kept finding the same handful of small patterns missing from the standard library. SwiftAsyncEx is the bundle of helpers I ended up writing; none of them particularly clever, but all of them things I didn’t want to re-derive in every project.
Some noteable components:
Task.observe(...) — runs a closure whenever an @Observable
expression changes. The standard withObservationTracking API only
re-arms once; this hides the re-arm loop and the willSet / Task.yield()
ordering pitfall.SerialTask — guarantees that only one execution of an async
operation runs at a time. Concurrent callers share the in-flight task
rather than starting their own. There are variants for “throw if busy”,
“fire-and-forget for buttons”, and weak-owner factories that no-op after
deallocation. Tracks execution status; extremely useful when tied to UI events.Task.bound(to:) / TaskBag — binds a task’s lifetime to an owner.
When the owner deallocates, bound tasks are cancelled. This is the
structured-concurrency analogue of Set<AnyCancellable>, which is the
thing I missed most after moving off Combine.LoadableResult<T> — an enum with four cases (idle, loading, loaded,
failed) so UI code can switch on a single value instead of juggling
separate booleans and optionals.Property / MutableProperty / PersistentProperty — observable
value handles that let modules expose read-only state to consumers
without leaking the storage type. PersistentProperty adds synchronous
reads of Codable values backed by pluggable storage (UserDefaults,
files, keychain), with async writes that flush in the background.AsyncDemuxer / KeyedAsyncDemuxer — single-flight coalescing. Many
callers requesting the same async result concurrently all wait on one
execution.The design rule for the whole library is “no new paradigms.” There are no scheduler types, no stream abstractions, no opinionated runtimes. Just thin helpers that should be mechanically deletable if the standard library ever provides equivalents.