Overloading async methods and multiple protocol conformance - unexpected behaviour

I'm implementing some kind of request manager, based on protocols conformance and I faced with unexpected behaviour:

import Foundation

protocol Foosyncable: AnyObject {
    func request()
}

protocol Fooasyncable: AnyObject {
    func request() async
}

protocol Fooable: Foosyncable, Fooasyncable {}

class Foo: Fooable {
    func request() {
        print("request")
    }

    func request() async {
        print("request async")
    }
}

class Fooooooo {
    let f: Fooable
    init() {
        f = Foo()
    }

    func boo() async throws {
        await f.request()
    }
}

let f = Fooooooo()
Task {
    try await f.boo() 
}

Output of code above is 'request', instead expected 'request async'. I try to figure out why async method isn't call in that case.

If I change let f: Fooable to let f: Foo, it works as expected.

In other hand, when I remove await from call of f.boo(), I got error: Expression is ‘async’ but is not marked with ‘await’, which is more weird.

1 Like

While I cannot say what causes this behaviour, and if it is intended or not (IMO there should be some warning at least), you can fix this using @_implements attribute:

protocol Foosyncable: AnyObject {
    func request()
}

protocol Fooasyncable: AnyObject {
    func request() async
}

protocol Fooable: Foosyncable, Fooasyncable {}

class Foo: Fooable {
    @_implements(Foosyncable, request())
    func request_sync() {
        print("request")
    }

    @_implements(Fooasyncable, request())
    func request_async() async {
        print("request async")
    }
}

class Fooooooo {
    let f: Fooable
    init() {
        f = Foo()
    }

    func boo() async throws {
        await f.request()
    }
}

let f = Fooooooo()
try await f.boo()  // will call correct method