# Async: A Swift wrapper around GCD that trades modern syntax for chainable blocks

> duemunk/Async is an unmaintained Swift library that turns GCD's nested closures into a flat, chainable API. It solves indentation and scope pollution, but its age and the arrival of Swift's own concurrency features make it a niche choice for legacy codebases.

**duemunk/Async** — GitHub describes it as Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch. The repository metadata lists Swift as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

- Repository: https://github.com/duemunk/Async
- Stars: 4,548 · Forks: 311
- Language: Swift
- License: MIT
- Published: 2026-08-13 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/duemunk-async

## What Async actually fixes: indentation and scope pollution in GCD

The README is blunt about the problem: GCD's native syntax, even after Swift 3.0, nests closures. Each async dispatch adds a level of indentation, and values computed in one block must be captured in a local variable to be used in the next. Async replaces that with a chain of method calls. The example in the README shows a three-step pipeline: compute a value on a userInitiated queue, transform it on a background queue, then update a UI label on the main queue. With raw GCD that is three nested closures and one intermediate variable. With Async it is a single expression: Async.userInitiated { 10 }.background { "Score: \($0)" }.main { label.text = $0 }. The library is for Swift developers who find that nesting hard to read or who want to avoid capturing variables in a polluted scope. It does not add new concurrency primitives; it repackages existing GCD calls.

## The mechanism: dispatch_block_notify and a struct wrapper

The README explains the core trick. Each chaining block is not executed directly. Instead, Async uses GCD's notification API, introduced in OS X 10.10 and iOS 8. For a previous block and a chaining block, the library creates dispatch_block_t objects with DISPATCH_BLOCK_INHERIT_QOS_CLASS, then calls dispatch_block_notify on the previous block, passing the target queue and the chaining block. That means the chaining block runs only after the previous block finishes. The syntax is achieved by having class methods on the Async object, like Async.main {}, which return a struct. That struct has matching methods, so you can call .background {} on the result. The struct wraps the dispatch_block_t because, as the README notes, dispatch_block_t cannot be extended directly. This is a clean design, but it has a consequence: the chain is only as reliable as the notification API, and the README documents that modern GCD queues misbehave in the iOS Simulator.

## Installation and basic usage: three package managers, one API

You can install Async via Swift Package Manager, CocoaPods, or Carthage. For SPM, the README shows adding a dependency in Package.swift with .package(url: "https://github.com/duemunk/Async", from: "2.1.0"). In Xcode 11 you can also use File > Swift Packages > Add Package Dependency. CocoaPods requires use_frameworks! and pod "AsyncSwift". Carthage is just github "duemunk/Async". The API surface is small. You get class methods for each QoS class: Async.main, Async.userInteractive, Async.userInitiated, Async.utility, and Async.background. Each returns a struct you can chain further. You can also pass a custom DispatchQueue via Async.custom(queue:). Delays are supported with an after parameter: Async.main(after: 0.5) { ... }. Cancellation is partial: cancel() on a block prevents chained blocks that have not yet been dispatched from running, but the first block itself is not cancelled, as the README's example shows. There is also a wait() method to block the current queue until the block finishes, and an Apply.background(100) { i in ... } wrapper for parallel for-loops, which is synchronous by design.

## The known bugs: simulator issues and the workaround for block extension

The README lists two known problems. First, modern GCD queues do not work as expected in the iOS Simulator, referencing issues 13 and 22. This is a serious caveat: if you develop on a simulator, chained blocks may not run in the correct order or may not run at all. The README does not give a workaround. Second, dispatch_block_t cannot be extended, so the library wraps it in a struct. This is an internal implementation detail, but it means you cannot use Async as a drop-in replacement for a raw block variable in all contexts. These are not hypothetical edge cases; they are documented in the project. For a library that is no longer maintained, these bugs are unlikely to be fixed. If you target a simulator, you should test thoroughly or avoid the library.

## AsyncGroup: a separate tool for parallel blocks and waiting

Beyond chaining, Async provides AsyncGroup, a wrapper around GCD groups. You create a group, add blocks with methods like group.background {} or group.utility {}, and they run in parallel on their respective queues. Calling group.wait() blocks until all blocks finish. The README also shows how to integrate custom asynchronous operations: you call group.enter(), dispatch your own work, and call group.leave() when done. This is useful for coordinating multiple independent tasks. The API mirrors the Async class methods, so you get the same QoS classes and custom queues. However, AsyncGroup is a separate abstraction; it does not chain. You cannot do group.background {}.main {}. You must use wait() and then continue. This is a reasonable design, but it means you have two different mental models in one library: chaining for sequential work, groups for parallel work.

## Maintenance status and licence: a frozen project

The README opens with the sentence: "This project is no longer maintained." The last release, 2.1.0, was pushed on 2019-10-31. The repository is not archived, but the author has explicitly stopped development. The licence is MIT, which permits free use, modification, and redistribution, but the copyright notice is from 2016. There is no indication of ongoing security or compatibility fixes. For a library that depends on a system API (GCD), this means it may break on future OS versions or Swift versions. The README mentions support for Swift 2.0 in an earlier release, and 2.1.0 is likely Swift 5-compatible, but there is no guarantee. If you adopt it, you take on the maintenance burden yourself. The MIT licence makes that legal, but you must be prepared to fork it if a bug appears.

## The real alternative: Swift's native concurrency and modern GCD

The README itself contrasts Async with raw GCD. The comparison is honest: for a three-step chain, raw GCD requires nested closures and a captured variable. But that comparison is from the Swift 3 era. Since then, Swift has introduced async/await, which solves the same problem without a third-party dependency. With async/await, you can write sequential code that looks synchronous, and the compiler handles the dispatch. That is a fundamentally different approach: Async chains closures at runtime using dispatch_block_notify, while async/await is a language feature with structured concurrency. If you cannot use async/await because you target an older Swift version, you could still use GCD directly and accept the nesting. The library's only advantage is the chainable syntax. For a new project, the cost of an unmaintained dependency outweighs that syntactic convenience.

## Conclusion

Adopt Async if you maintain a Swift 4 or 5 codebase that still relies on GCD and you want to reduce nested closures without migrating to async/await. Do not adopt it for new projects: the library is unmaintained, the last release is from 2019, and the iOS Simulator has known bugs with modern GCD queues. Before using it, verify that your deployment targets (iOS 8+, macOS 10.10+) match the notification API requirements, and check that the cancellation and wait semantics work as expected in your specific use case, especially on simulators. The library's value is purely syntactic; if you can use Swift's native async/await or the built-in DispatchQueue API, you should skip this dependency entirely.

## FAQ

### Is Async still maintained?

The README states that the project is no longer maintained. The last push to the repository is dated 2024-04-11, and the most recent release is 2.1.0 from 2019-10-31.

### How do I install Async in a Swift project?

With Swift Package Manager, add `.package(url: "https://github.com/duemunk/Async", from: "2.1.0")` to your dependencies or use the package menu in Xcode 11. CocoaPods uses `pod "AsyncSwift"` alongside `use_frameworks!`, and Carthage uses `github "duemunk/Async"`.

### How does Async chain blocks together?

It uses the GCD notification API introduced in OS X 10.10 and iOS 8, creating blocks with `dispatch_block_create` and `DISPATCH_BLOCK_INHERIT_QOS_CLASS` and firing the next one with `dispatch_block_notify`. The syntax comes from class methods on `Async` that return a struct with matching methods, because `dispatch_block_t` cannot be extended.

### Can Async cancel work that has already started?

No. It cancels blocks that have not been dispatched yet. In the README's example the first block is not cancelled and only the second, still queued, one is.

### Is the Apply helper in Async asynchronous?

No. It wraps `dispatch_apply` and returns after the block has run all 100 times, so it is synchronous. The README says to wrap it inside an `Async` block if you need asynchronous behaviour.

### Does Async work in the iOS Simulator?

Not reliably. The known bugs section says modern GCD queues do not work as expected in the iOS Simulator, and points at issues 13 and 22.

## Sources

- [Official README](https://github.com/duemunk/Async#readme)
- [Project repository](https://github.com/duemunk/Async)
- [Release notes](https://github.com/duemunk/Async/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/duemunk-async
