Compile-time generation of synchronous overloads from asynchronous function declarations.
Libraries that provide both synchronous and asynchronous variants of the same
function must maintain two nearly-identical declarations that typically differ
only in their use of async/await keywords. swift-reasync eliminates this
duplication with a macro that generates a synchronous peer overload at compile
time.
The transformation is purely syntactic, removing the async and await
keywords wherever they appear, along with the related concurrency annotations
that are either invalid on synchronous forms or that the peer's synchronous body
can no longer support. All other syntax is preserved.
The generated function must be valid in a synchronous context without further modification. If the function body contains operations that are inherently asynchronous, such as calls to actor-isolated methods or async-only APIs, the generated synchronous overload will not compile.
@Reasync
func double(
_ value: Int
) async throws -> Int
{
return value * 2
}
// Generated by @Reasync:
//
// func double(
// _ value: Int
// ) throws -> Int
// {
// return value * 2
// }await expressions, async let bindings, and for await loops in the
function body are transformed to their synchronous equivalents.
@Reasync
func doubleThenAdd(
_ a: Int,
_ b: Int
) async -> Int
{
async let x : Int = double(a)
async let y : Int = double(b)
return await x + y
}
// Generated by @Reasync:
//
// func doubleThenAdd(
// _ a: Int,
// _ b: Int
// ) -> Int
// {
// let x : Int = double(a)
// let y : Int = double(b)
//
// return x + y
// }The macro's transformation removes async and await from the function
declaration, but async functions in Swift 6 frequently carry additional
annotations. The macro handles these as follows:
| Annotation | Rule | Removal Scope |
|---|---|---|
async, await |
Remove | Everywhere |
@isolated(any) |
Remove | Closure parameter types (at any nesting depth) |
nonisolated(nonsending) |
Remove | Everywhere |
@concurrent |
Remove | Everywhere |
@Sendable |
Remove | Closure parameter types (at any nesting depth) |
sending |
Preserve | |
| Global actors | Preserve | |
isolated parameters |
Preserve | |
Bare nonisolated |
Preserve |
These rules are designed to produce a synchronous peer that is type-correct under Swift 6 strict concurrency, without silently changing the meaning of annotations that are unrelated to concurrent execution.
swift-reasync may be installed using Swift Package Manager.
// Import swift-reasync.
import ReasyncSee Xcode documentation for instructions on how to add package dependencies.
| Platform | Minimum Version |
|---|---|
| Swift | 6.3 |
| iOS | 13.0 |
| iPadOS | 13.0 |
| Mac Catalyst | 13.0 |
| macOS | 10.15 |
| tvOS | 13.0 |
| watchOS | 6.0 |
swift-reasync is licensed under the Apache License, Version 2.0.
See LICENSE for the complete license terms.