Typealias resolving can't figure this out

I posted this issue on the Feedback Assistant at Apple... but this really seems to be on Swift's part.

// MARK: - Models
​
public struct Donut {
    public var flavor: String
    public var isGlutenFree: Bool
    public var isVegan: Bool
}
​
// MARK: - Endpoint Protocol
​
public protocol Endpoint {
    associatedtype Result
}
​
extension Endpoint {
    public typealias donuts = MyEndpoints.GetDonuts
}
​
// MARK: - Endpoints
​
public enum MyEndpoints {
    public struct GetDonuts: Endpoint {
        public typealias Result = [Donut]
​
        var glutenFree: Bool = false
        var vegan: Bool = false
​
        public init(glutenFree: Bool = false, vegan: Bool = false) {
            self.glutenFree = glutenFree
            self.vegan = vegan
        }
    }
}
​
// MARK: - Client Protocol
​
public protocol ClientProtocol {
    func get<T: Endpoint>(_ endpoint: T) async throws -> T.Result
}
​
// MARK: - Example
​
func someExample(_ client: ClientProtocol) async throws {
    // Using non-typealias syntax works fine
    let result0 = try await client.get(MyEndpoints.GetDonuts(glutenFree: true))
    // result0 is of type Array<Donut> as expected
​
    // Using typelias `donuts` in protocol Endpoint extension
    let result1 = try await client.get(.donuts())
    // doesn't work compile
}

Why would I expose my endpoints like this ?
Because it's a nice syntax,

  • autocompletion of optional values is lovely with Xcode 14 beta
  • autocompletion of available typealiases exists as well ...

Nothing prevents me to write the typealias and the type alias is correct.

1 Like

It doesn't work because .donuts does not return an instance of Endpoint. You have two options that I know of:

  1. Write Endpoint.donuts.
  2. Write the boilerplate to forward the GetDonuts initializer, which is really what you want, not a typealias. There was a big thread about making this unnecessary, but it didn't go anywhere.

But donuts should be equal to MyEndpoints.GetDonuts in my opinion, its a type alias it's like replacing it in code. And MyEndpoints.GetDonuts is a struct that responds to protocol Endpoint

No, if you actually wanted a type alias, you would capitalize it. Instead, you're trying to forward an initializer along so that it can be used with static member lookup:

extension Endpoint where Self == MyEndpoints.GetDonuts {
  static func donuts(glutenFree: Bool = false, vegan: Bool = false) -> Self {
    .init(glutenFree: glutenFree, vegan: vegan)
  }
}

I agree that it should be easier to write that sort of code, and I suspect that as more people come across the pain of the boilerplate, we'll at least get better tooling support for generating it.

Found it.