# \[Pitch\] @completionHandlerAsync attribute

**URL:** <https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004>\
**Category:** Pitches\
**Tags:** asynchronous\
**Created:** [May 27, 2021, 11:55pm UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004 "2021-05-27T23:55:22Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![bnbarham](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bnbarham/32/17708_2.png) [@bnbarham](https://forums.swift.org/u/bnbarham)\
**Post date:** [May 27, 2021, 11:55pm UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/1 "2021-05-27T23:55:22Z")

</div>

Hi all,

Looking for feedback on a `@completionHandlerAsync` attribute.

## Introduction

Add a new `@completionHandlerAsync` attribute to designate a synchronous function as having an asynchronous alternative, ie. one that uses the new `async` language feature ([SE-0296](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md)) but otherwise provides “equivalent” functionality to the attributed function.

## Motivation

As projects migrate towards the new concurrency language features, it could be helpful to both users and tooling to mark non-async and async function pairs. The presence of this attribute would enable tooling to provide functionality to assist with the migration effort:

1. Output a compiler warning (and possibly fixit) for any call to an `@completionHandlerAsync` attributed function in an asynchronous context
2. Aid in refactorings to replace calls to completion-handler functions with their async alternatives

## Proposed solution

A new function attribute `@completionHandlerAsync` that takes the name of the async alternative function and (optionally) the position of the completion handler parameter, assumed to be the last parameter if not given.

```swift
@completionHandlerAsync("processImplicit(data:)")
func processImplicit(data: Data, completion: @escaping (ProcessedData) -> Void) { ... }
func processImplicit(data: Data) async -> ProcessedData { ... }

@completionHandlerAsync("processExplicit(data:)", completionHandlerIndex: 1)
func processExplicit(data: Data, completion: @escaping (ProcessedData) -> Void) { ... }
func processExplicit(data: Data) async -> ProcessedData { ... }

@completionHandlerAsync("processNonTrailing(data:)", completionHandlerIndex: 0)
func processNonTrailing(completion: @escaping (ProcessedData) -> Void, data: Data) { ... }
func processNonTrailing(data: Data) async -> ProcessedData { ... }

```

## Detailed design

An attribute of the form `@completionHandlerAsync(<name>[, completionHandlerIndex: <n>])` , permitted where:

- it’s attached to a non- `async` , `Void` -returning function with at least `n - 1` parameters (where `n` is the index of the last parameter if not given),
- `name` is a string literal containing the non-ambiguous compound name of the async alternative function that must be located in the same context as the attributed function,
- the completion handler argument referenced by `completionHandlerIndex` is an `@escaping` and non- `@autoclosure` `Void` -returning function type.

This attribute should be implicitly added to Objective-C completion-handler methods that are also imported as `async` . See the Concurrency Interoperability with Objective-C proposal for more details on those ([SE-0297](https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md)).

Attributes on protocol or class methods should be inherited by their implementation and overrides. Attributing the implementation/override is an error if one is already specified by the parent, ie.

```swift
protocol MyProto {
  @completionHandlerAsync("process(data:)", completionHandlerIndex: 1)
  func process(data: Data, completion: (ProcessedData) -> Void)
  func process(data: Data) async -> ProcessedData
}

struct MyStruct: MyProto {
  @completionHandlerAsync("process(data:)", completionHandlerIndex: 1) // error
  func process(data: Data, completion: (ProcessedData) -> Void) { ... }
  func process(data: Data) async -> ProcessedData { ... }
}

```

A partial implementation of `@completionHandlerAsync` has been implemented under the `-enable-experimental-concurrency` flag (thanks @etcwilde). Inheriting the attribute on implementations and overrides is yet to be implemented.

## Source compatibility and effect ABI

The proposed solution is purely additive and has no affect on the ABI.

## Alternatives considered

### Defaults

The completion handler will almost always be the last parameter of a function to take advantage of Swift’s trailing closure syntax. Given that, an implicit default seems ideal. It wouldn’t be unusual to have overloaded functions, or to want a separate async function name - hence the explicit function name.

### Implicit Pairing

Instead of an attribute, the compiler could implicitly pair functions where one “looks like” the async alternative. This could never be perfect though, so could result in both spurious and missed warnings and refactorings.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [May 28, 2021, 12:31am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/2 "2021-05-28T00:31:02Z")

</div>

This sounds great and should be very useful 👍

So the `"process(data:)"` is a string, but really it is going to be typechecked for existence of the function anyway. I wonder if we could skip the `""` and just have it be `@...(process(data:), ...)` if that is possible? I'm not sure if that would cause hell for parsing those or not as much.

We discussed with Ben and perhaps the name may use some iteration still? Originally this was discussed as `@asyncAlternative` which sounds good as but had the issue that it was confusing to some if "this" function is the async alternative, or the one pointed at is the alternative.

Perhaps we can find some good name for this still though, like `@alternative(async: hello(hi:))` or `@preferredAlternative(async: hello(hi:))`? Not sure if taking up the alternative name is better or worse than taking the very specific use-case to heart and saying `preferredAsyncAlternative(hello(hi:))` though 🤔

It might be easier to say this is the `@asyncAlternative` after all...?

---

<div class="post-metadata">

**Author:** ![bnbarham](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bnbarham/32/17708_2.png) [@bnbarham](https://forums.swift.org/u/bnbarham)\
**Post date:** [May 28, 2021, 12:56am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/3 "2021-05-28T00:56:38Z")

</div>

> [@ktoso](#):
>
> This sounds great and should be very useful 👍

Thanks 🙂

> [@ktoso](#):
>
> So the `"process(data:)"` is a string, but really it is going to be typechecked for existence of the function anyway. I wonder if we could skip the `""` and just have it be `@...(process(data:), ...)` if that is possible? I'm not sure if that would cause hell for parsing those or not as much.

Yep, type checking will perform a name lookup and fail if it doesn't exist/is ambiguous. It's definitely possible to not require a string, feedback on that would be helpful. There's some precedence for it not being a string, ie. `@objc` takes a selector.

> [@ktoso](#):
>
> We discussed with Ben and perhaps the name may use some iteration still? Originally this was discussed as `@asyncAlternative` which sounds good as but had the issue that it was confusing to some if "this" function is the async alternative, or the one pointed at is the alternative.
> 
> Perhaps we can find some good name for this still though, like `@alternative(async: hello(hi:))` or `@preferredAlternative(async: hello(hi:))` ? Not sure if taking up the alternative name is better or worse than taking the very specific use-case to heart and saying `preferredAsyncAlternative(hello(hi:))` though 🤔
> 
> It might be easier to say this is the `@asyncAlternative` after all...?

Other suggestions were adding `has` and `Version` rather than `Alternative`, ie. `@hasAsyncAlternative` or `@hasAsyncVersion`.

---

<div class="post-metadata">

**Author:** ![SwiftUI](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftui/32/24361_2.png) [@SwiftUI](https://forums.swift.org/u/SwiftUI)\
**Post date:** [May 28, 2021, 12:57am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/4 "2021-05-28T00:57:15Z")

</div>

How about  
`@callback(f(a:b:), at: #)`  
or  
`@callback(handler: f(a:b:), at: #)`  
?

---

<div class="post-metadata">

**Author:** ![rintaro](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rintaro/32/568_2.png) [@rintaro](https://forums.swift.org/u/rintaro)\
**Post date:** [May 28, 2021, 1:17am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/5 "2021-05-28T01:17:37Z")

</div>

> [@ktoso](#):
>
> So the `"process(data:)"` is a string, but really it is going to be typechecked for existence of the function anyway. I wonder if we could skip the `""` and just have it be `@...(process(data:), ...)` if that is possible? I'm not sure if that would cause hell for parsing those or not as much.

FWIW, string name is used `@available(..., renamed:)` as well. It's string because it needs to support renaming a function to a getter. For example:

```swift
class C {
  var value: Int { 42 }

  @available(*, deprecated, renamed: "getter:value(self:)")
  func getValue() -> Int { self.value }

  func foo() {
    _ = self.getValue()
  }
}

```

The syntax is bizarre, but anyway, this code emits a warning with a fix-it rewriting `self.getValue()` to `self.value`.

So `@completionHandlerAsync` (or whatever) should also support `getter:`

```swift
  @completionHandlerAsync("getter:value(self:)")
  func getValueAsync(callback: (Int) -> Void ) { ... }

  var value: Int { get async { ... } }

```

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [May 28, 2021, 1:18am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/6 "2021-05-28T01:18:26Z")

</div>

> [@rintaro](#):
>
> FWIW, string name is used `@available(..., renamed:)` as well. It's string because it needs to support renaming a function to a getter. For example:

I see, thanks for clarifying!

---

<div class="post-metadata">

**Author:** ![rxwei](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rxwei/32/664_2.png) [@rxwei](https://forums.swift.org/u/rxwei)\
**Post date:** [May 28, 2021, 3:28am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/7 "2021-05-28T03:28:55Z")

</div>

> [@rintaro](#):
>
> So `@completionHandlerAsync` (or whatever) should also support `getter:`
> 
> ```swift
> @completionHandlerAsync("getter:value(self:)")
> func getValueAsync(callback: (Int) -> Void ) { ... }
> 
> var value: Int { get async { ... } }
> 
> ```

Can `getter` be expressed without using a string literal?

```swift
  @completionHandlerAsync(value.get)
  func getValueAsync(callback: (Int) -> Void ) { ... }

```

`#selector(...)` is a precedent that does not use a string literal to refer to a declaration. Similarly, `@derivative(of: ...)` also does not use a string literal, although the feature is not official.

IMHO, a string literal just makes it look less type-checked.

---

<div class="post-metadata">

**Author:** ![rintaro](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rintaro/32/568_2.png) [@rintaro](https://forums.swift.org/u/rintaro)\
**Post date:** [May 28, 2021, 4:52am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/8 "2021-05-28T04:52:12Z")

</div>

(To clarify, I'm not suggesting we should use string literals)

> [@rxwei](#):
>
> Can `getter` be expressed without using a string literal?
> 
> ```swift
> @completionHandlerAsync(value.get)
> func getValueAsync(callback: (Int) -> Void ) { ... }
> 
> ```

It's probably possible to express a getter without a string literal. Also, if we type check it, we should be able to infer that it's a getter because we know `value` is `var` or `func`.

(One interesting fact about `@available(renamed:)` is that it doesn't type check the new name. i.e. `@available(*, deprecated, renamed: "foobar()")` is not an error and emits fix-it even without actual `foobar` declaration.)

I _guess_ another reason of using a string literal was to have a common logic to handle objc ` __attribute__ ((swift_name("...")))` attribute and/or apinotes. But I'm not sure. Perhaps @jrose knows the history?

> [@rxwei](#):
>
> `#selector(...)` is a precedent that does not use a string literal to refer to a declaration. Similarly, `@derivative(of: ...)` also does not use a string literal, although the feature is not official.

`#selector` uses `getter:`/`setter:` prefix. But `@completionHandlerAsync(getter:valueName)` is not ideal as it's confusable with a attribute argument label...

`@derivative(of: )` uses postfix `.get`/`.set`?

Related discussion for syntax of referencing getter/setters:  
[https://github.com/apple/swift-evolution/blob/main/proposals/0021-generalized-naming.md](https://github.com/apple/swift-evolution/blob/main/proposals/0021-generalized-naming.md)  
[https://forums.swift.org/t/proposal-expose-getter-setters-in-the-same-way-as-regular-methods/501](https://forums.swift.org/t/proposal-expose-getter-setters-in-the-same-way-as-regular-methods/501)  
[https://forums.swift.org/t/review-se-0044-import-as-member/1805/5](https://forums.swift.org/t/review-se-0044-import-as-member/1805/5)

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [May 28, 2021, 4:55am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/9 "2021-05-28T04:55:11Z")

</div>

Another syntax that I just remembered is “@dynamicReplacement(for: hello(\_:))”.

Fairly rarely used but another point for trying to make it work without string literals.

I’ll check in an hour or how getters are expressed there (if at all possible!).

---

<div class="post-metadata">

**Author:** ![rxwei](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rxwei/32/664_2.png) [@rxwei](https://forums.swift.org/u/rxwei)\
**Post date:** [May 28, 2021, 7:03am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/10 "2021-05-28T07:03:23Z")

</div>

> [@rintaro](#):
>
> `@derivative(of: )` uses postfix `.get` / `.set` ?

Yes, it does today. It is handled here as [`parseQualifiedDeclName`](https://github.com/apple/swift/blob/da880d9c865a93228f8b1b29538712762868d453/lib/Parse/ParseDecl.cpp#L1180).

---

<div class="post-metadata">

**Author:** ![bnbarham](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bnbarham/32/17708_2.png) [@bnbarham](https://forums.swift.org/u/bnbarham)\
**Post date:** [May 29, 2021, 12:43am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/11 "2021-05-29T00:43:24Z")

</div>

The handling in `@derivative` seems the most reasonable to me, though it also allows a base type which `@completionHandlerAsync` shouldn't (?). I could be missing something, but it seems a little weird to allow it in the `@derivative` can anyway, it looks like it enforces the two functions to have the same parents as well.

`@dynamicReplacement` doesn't handle accessors from what I can see.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [May 31, 2021, 12:44am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/12 "2021-05-31T00:44:56Z")

</div>

> [@bnbarham](#):
>
> `@dynamicReplacement` doesn't handle accessors from what I can see.

Yeah, doesn't seem to do so -- it can replace an entire property, but can't specify just a getter.

---

<div class="post-metadata">

**Author:** ![Raybo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/raybo/32/3290_2.png) [@Raybo](https://forums.swift.org/u/Raybo)\
**Post date:** [May 31, 2021, 1:36am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/13 "2021-05-31T01:36:32Z")

</div>

The method signatures of asynchronous vs synchronous functions will often use a predictable pattern (same name, inputs, and outputs). Considering that, I’d like to see the name parameter to `@completionHandlerAsync` be optional (when possible).

Example:

```swift
@completionHandlerAsync
func processImplicit(data: Data, completion: @escaping (ProcessedData) -> Void) { ... }
func processImplicit(data: Data) async -> ProcessedData { ... }

```

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [May 31, 2021, 6:20am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/14 "2021-05-31T06:20:45Z")

</div>

I’m not a big fan of this being implicit for what it’s worth. It invites being lazy about it, doesn’t it?

We found that often functions get renamed because “getThing” is nicer as “await x.thing” etc. Someone has to manually audit those alternatives anyway in normal code, and in imported code it is done automatically by the importer so it being a bit more verbose does not really matter.

The spelling you propose is also a bit backwards… we’d like to say this method has an alternative, not that it is one — but this spelling makes is sound like it IS the “completion handler async”. In some internal discussions this did come up too and was proposed to be solved by “hasAsyncAlternative” which IMHO sounded pretty inelegant.

Just a personal thought though.

---

<div class="post-metadata">

**Author:** ![bnbarham](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bnbarham/32/17708_2.png) [@bnbarham](https://forums.swift.org/u/bnbarham)\
**Post date:** [June 1, 2021, 12:35am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/15 "2021-06-01T00:35:13Z")

</div>

> [@Raybo](#):
>
> The method signatures of asynchronous vs synchronous functions will often use a predictable pattern (same name, inputs, and outputs). Considering that, I’d like to see the name parameter to `@completionHandlerAsync` be optional (when possible).

The hope is that including the name adds a moment of "should this really be the name?". As an example, let's say that the callback-based function was `func whenReady(data: Data, completion: @escaping (ProcessedData) -> Void) { ... }` - perhaps that would now be better as `var data: ProcessedData { get async { ... } }`.

> [@ktoso](#):
>
> The spelling you propose is also a bit backwards… we’d like to say this method has an alternative, not that it is one — but this spelling makes is sound like it IS the “completion handler async”.

I believe this was in reply to Raybo, but note that they were just using the originally proposed name 😆. Note that the spelling is indeed correct there, ie. the attributed function _is_ completion handler async and has the alternative spelled in the parameter. Part of what makes this name hard is that the name is of the alternative and the index is of the attributed function. Perhaps we need a name where it then makes sense to name both parameters, ie. `@something(alternative: <func>, completionHandlerIndex: <index>)`.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [June 1, 2021, 1:24am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/16 "2021-06-01T01:24:13Z")

</div>

Right sorry, I guess I was voicing the general opinion against implicitly inferring the alternative. It also ends up very confusing (to me at least, as one datapoint), what is the preferred alternative and what is the old one if just the existing name were adopted, and we made the alternative implicit even.

To me, it's a valuable moment when one has to write that alternative pointed at -- it may be a good chance to realize the alternative API could be a bit nicer, say, using an async property getter or similar. I've seen a bunch of APIs would could benefit from such treatment.

---

<div class="post-metadata">

**Author:** ![masters3d](https://avatars.discourse-cdn.com/v4/letter/m/c4cdca/32.png) [@masters3d](https://forums.swift.org/u/masters3d)\
**Post date:** [June 1, 2021, 1:55am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/17 "2021-06-01T01:55:17Z")

</div>

The "having an alternative API that is more favorable" problem also shows up in other places. [Returning to an old hobbyhorse: Migrating higher order function names to comply with API guidelines - #27 by masters3d](https://forums.swift.org/t/returning-to-an-old-hobbyhorse-migrating-higher-order-function-names-to-comply-with-api-guidelines/37728/27)

It seems to me that one of the main use case is migration where we want folks to move to asynchronous versions of an older synchronous API. I see this as the same problem as "deprecate but never delete" older API.

Can this feature be more engineered to be more general than just async/sync API?

Thanks,  
Chéyo

---

<div class="post-metadata">

**Author:** ![woolsweater](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/woolsweater/32/2402_2.png) [@woolsweater](https://forums.swift.org/u/woolsweater)\
**Post date:** [June 1, 2021, 3:51am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/18 "2021-06-01T03:51:52Z")

</div>

> [@bnbarham](#):
>
> As projects migrate...

What happens to this very specialized new language feature when everyone is done migrating? Why cannot this be an extension of the existing `@available` mini-language, which is already used for this kind of redirection, via `deprecated`?

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [June 1, 2021, 5:24am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/19 "2021-06-01T05:24:43Z")

</div>

> [@rintaro](#):
>
> I _guess_ another reason of using a string literal was to have a common logic to handle objc ` __attribute__ ((swift_name("...")))` attribute and/or apinotes. But I'm not sure. Perhaps @jrose knows the history?

I don’t remember for sure, but my best guess is this is an accident of incremental development: first the “renamed” in Clang’s availability attribute got copied over to Swift verbatim, and later support got added to actually use the Swift name of a Clang-renamed declaration. By then, though, it was already too late to make it not accept arbitrary strings. We could type-check it regardless, though, deprecating the use of `renamed` _in Swift_ to reference anything but a Swift declaration in scope. We could even accept both quoted and non-quoted syntax for a while if we wanted.

---

<div class="post-metadata">

**Author:** ![tali](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tali/32/11346_2.png) [@tali](https://forums.swift.org/u/tali)\
**Post date:** [June 1, 2021, 6:27am UTC](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004/20 "2021-06-01T06:27:24Z")

</div>

Why not reuse the existing `@available` mechanism?  
E.g. `@available(*, async: "processImplicit(data:)")`

[Next page](https://forums.swift.org/t/pitch-completionhandlerasync-attribute/49004.md?page=2)
