@AltDependencyClient serves a similar purpose to Point-Free's @DependencyClient macro, but works in the opposite direction. That is, one of the features of @DependencyClient is the generation of wrapper functions to call the user's defined closure variables, allowing the use of function parameter names. @AltDependencyClient on the other hand generates the closure variables and the wrapper functions from the user-defined methods in a nested Interface protocol, which allows the use of both function parameter labels and parameter names.
If you find this useful, or have suggestions for improvement, please let me know. This is my first Swift macro - be kind! 😃
@AltDependencyClient
struct AudioPlayerClient {
protocol Interface {
func loop(url: URL) async throws -> Void
func play(url: URL) async throws -> Void
func setVolume(volume: Float) async -> Void
func stop() async -> Void
}
}struct AudioPlayerClient {
protocol Interface {
func loop(url: URL) async throws -> Void
func play(url: URL) async throws -> Void
func setVolume(volume: Float) async -> Void
func stop() async -> Void
}
public var impl: Impl
public init(
loop: @escaping (_ url: URL) async throws -> Void,
play: @escaping (_ url: URL) async throws -> Void,
setVolume: @escaping (_ volume: Float) async -> Void,
stop: @escaping () async -> Void
) {
impl = Impl(
loop: loop,
play: play,
setVolume: setVolume,
stop: stop
)
}
@inlinable
@inline(__always)
public
func loop(url: URL) async throws -> Void {
try await impl.loop(url)
}
@inlinable
@inline(__always)
public
func play(url: URL) async throws -> Void {
try await impl.play(url)
}
@inlinable
@inline(__always)
public
func setVolume(volume: Float) async -> Void {
await impl.setVolume(volume)
}
@inlinable
@inline(__always)
public
func stop() async -> Void {
await impl.stop()
}
public struct Impl {
public var loop: (_ url: URL) async throws -> Void
public var play: (_ url: URL) async throws -> Void
public var setVolume: (_ volume: Float) async -> Void
public var stop: () async -> Void
}
}- Allows use of both function parameter labels and parameter names.
- The method implementations can still be replaced via access to the
implproperty.
- Yes, defining the
Interfaceprotocol that is only used by the macro feels a bit strange. - It doesn't support no-argument dependency construction like
@DependencyClient, which generates a default "unimplemented" client. - You can't use "show callers" on any of the methods defined in
Interface, even after expanding the macro and trying it on the generated functions.@DependencyClienthas the same limitation. - Doesn't support default arguments