DependencyEndpoint(method:)

Provides a default, “unimplemented” value to a closure property.

Macros.swift:207
@attached(accessor, names: named(init), named(get), named(set)) @attached(peer, names: arbitrary) macro DependencyEndpoint(method: String = "")

This macro is automatically applied to all closure properties of a struct annotated with DependencyClient.

If an “unimplemented” closure is invoked and not overridden, a test failure will be emitted, and the endpoint will throw an error if it is a throwing closure.

If the closure this macro is applied to provides argument labels for the input tuple, then a corresponding method will also be generated with named labels. For example, this:

@DependencyEndpoint
var fetchUser: (_ id: User.ID) async throws -> User

…expands to this:

var fetchUser: (_ id: User.ID) async throws -> User
func fetchUser(id: User.ID) async throws -> User {
  try await self.fetchUser(id)
}

Now you can use a clearer syntax at the call site of invoking this endpoint:

let client = APIClient()
let user = try await client.fetchUser(id: 42)

You can also modify the name of the generated method, which can be handy for creating overloaded method names:

@DependencyEndpoint(method: "fetchUser")
var fetchUserByID: (_ id: User.ID) async throws -> User
@DependencyEndpoint(method: "fetchUser")
var fetchUserBySubscriptionID: (_ subscriptionID: Subscription.ID) async throws -> User

This expands to:

var fetchUserByID: (_ id: User.ID) async throws -> User
func fetchUser(id: User.ID) async throws -> User {
  self.fetchUserByID(id)
}
var fetchUserBySubscriptionID: (_ subscriptionID: Subscription.ID) async throws -> User
func fetchUser(subscriptionID: Subscription.ID) async throws -> User {
  self.fetchUserBySubscriptionID(subscriptionID)
}

Now you can have an overloaded version of fetchUser that takes different arguments:

let client = APIClient()
let user1 = try await client.fetchUser(id: 42)
let user2 = try await client.fetchUser(subscriptionID: "sub_deadbeef")