Contents

swift-developer-tools/swift-reasync

Compile-time generation of synchronous overloads from asynchronous function

Overview

Libraries that provide both synchronous and asynchronous variants of the same function must maintain two nearly-identical declarations that differ only in their use of async/await keywords. swift-reasync eliminates this duplication with two macros that generate synchronous overloads at compile time.

The transformation is purely syntactic, removing async and await keywords. async let bindings become let bindings, for await loops become for loops, and async closure parameters become synchronous. All other attributes, modifiers, trivia, and documentation comments are preserved in the generated declaration.

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`

@Reasync is a peer macro that generates a synchronous overload of an asynchronous function.

@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
// }

[!NOTE] @Reasync can only be applied to asynchronous function declarations.

`@ReasyncMembers`

@ReasyncMembers is a member macro that generates synchronous overloads for all asynchronous function declarations within a type or extension. Synchronous members are not affected. Members that are already annotated with @Reasync are skipped, since @Reasync generates its own overload.

@ReasyncMembers
struct Operations
{
    func double(
        _ value: Int
    ) async -> Int
    {
        return value * 2
    }
    
    // Generated by @ReasyncMembers:
    //
    // func double(
    //     _ value: Int
    // ) -> Int
    // {
    //     return value * 2
    // }
    
    // Already synchronous. No overload generated.
    func increment(
        _ value: Int
    ) -> Int
    {
        return value + 1
    }
    
    @Reasync
    func negate(
        _ value: Int
    ) async -> Int
    {
        return -value
    }
    
    // Generated by @Reasync:
    // 
    // func negate(
    //     _ value: Int
    // ) -> Int
    // {
    //     return -value
    // }
}

[!NOTE] @ReasyncMembers can only be applied to structs, classes, enums, actors, and extensions.

Installation

Swift Package Manager

swift-reasync may be installed using Swift Package Manager.

// Import swift-reasync.
import Reasync

See Xcode documentation for instructions on how to add package dependencies.

Requirements

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

License

swift-reasync is licensed under the Apache License, Version 2.0.

See LICENSE for the complete license terms.

Package Metadata

Repository: swift-developer-tools/swift-reasync

Default branch: main

README: README.md