Skip to content

Commit c31b144

Browse files
Update docs for @DependencyClient about default values. (#164)
* Update docs for @DependencyClient about default values. * wip * wip * wip * Apply suggestions from code review --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 25c9fe9 commit c31b144

File tree

5 files changed

+56
-3
lines changed

5 files changed

+56
-3
lines changed

Sources/DependenciesMacros/Macros.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,37 @@
8080
/// instances of the client. Creating that initializer manually is quite laborious, and you have to
8181
/// update it each time a new endpoint is added to the client.
8282
///
83+
/// ## Restrictions
84+
///
85+
/// Usage of the ``DependencyClient()`` macro does have a restriction to be aware of. If your
86+
/// client has a closure that is non-throwing and non-void returning like below, then you
87+
/// will get a compile-time error letting you know a default must be provided:
88+
///
89+
/// ```swift
90+
/// @DependencyClient
91+
/// struct APIClient {
92+
/// // 🛑 Default value required for non-throwing closure 'isFavorite'
93+
/// var isFavorite: () -> Bool
94+
/// }
95+
/// ```
96+
///
97+
/// The error also comes with a helpful fix-it to let you know what needs to be done:
98+
///
99+
/// ```swift
100+
/// @DependencyClient
101+
/// struct APIClient {
102+
/// var isFavorite: () -> Bool = { <#Bool#> }
103+
/// }
104+
/// ```
105+
///
106+
/// The reason we require a default for these endpoints is so that you immediately get access to
107+
/// a default client via `APIClient()`, which is handy to use in tests and SwiftUI previews. The
108+
/// only way to do this, without crashing at runtime, is if you provide defaults for your endpoints.
109+
///
110+
/// To fix you must supply a closure that returns a default value. The default value can be anything
111+
/// and does not need to signify a real value. For example, if the endpoint returns a boolean, you
112+
/// can return `false`, or if it returns an array, you can return `[]`.`
113+
///
83114
/// [designing-dependencies]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/designingdependencies
84115
/// [separating-interface-implementation]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/livepreviewtest#Separating-interface-and-implementation
85116
@attached(member, names: named(init))

Sources/DependenciesMacrosPlugin/DependencyClientMacro.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public enum DependencyClientMacro: MemberAttributeMacro, MemberMacro {
3333
var unimplementedDefault = functionType.unimplementedDefault
3434
unimplementedDefault.append(placeholder: functionType.returnClause.type.trimmed.description)
3535
context.diagnose(
36+
clientName: declaration.as(StructDeclSyntax.self)?.name.text,
3637
node: binding,
3738
identifier: identifier,
3839
unimplementedDefault: unimplementedDefault

Sources/DependenciesMacrosPlugin/Support.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ extension VariableDeclSyntax {
178178

179179
extension MacroExpansionContext {
180180
func diagnose(
181+
clientName: String? = nil,
181182
node: PatternBindingSyntax,
182183
identifier: TokenSyntax,
183184
unimplementedDefault: ClosureExprSyntax
@@ -188,6 +189,14 @@ extension MacroExpansionContext {
188189
message: MacroExpansionErrorMessage(
189190
"""
190191
Default value required for non-throwing closure '\(identifier)'
192+
193+
Defaults are required so that the macro can generate a default, "unimplemented" version \
194+
of the dependency\(clientName.map { " via '\($0)()'"} ?? ""). The default value can be \
195+
anything and does not need to signify a real value. For example, if the endpoint returns \
196+
a boolean, you can return 'false', or if it returns an array, you can return '[]'.
197+
198+
See the documentation for @DependencyClient for more information: \
199+
https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
191200
"""
192201
),
193202
fixIt: FixIt(

Tests/DependenciesMacrosPluginTests/DependencyClientMacroTests.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,10 +638,14 @@ final class DependencyClientMacroTests: BaseTestCase {
638638
var endpoint: @Sendable () -> Int
639639
┬────────────────────────────
640640
╰─ 🛑 Default value required for non-throwing closure 'endpoint'
641+
642+
Defaults are required so that the macro can generate a default, "unimplemented" version of the dependency via 'Client()'. The default value can be anything and does not need to signify a real value. For example, if the endpoint returns a boolean, you can return 'false', or if it returns an array, you can return '[]'.
643+
644+
See the documentation for @DependencyClient for more information: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
641645
✏️ Insert '= { <#Int#> }'
642646
}
643647
"""
644-
} fixes: {
648+
}fixes: {
645649
"""
646650
@DependencyClient
647651
struct Client: Sendable {

Tests/DependenciesMacrosPluginTests/DependencyEndpointMacroTests.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,14 @@ final class DependencyEndpointMacroTests: BaseTestCase {
9292
var endpoint: () -> Bool
9393
┬───────────────────
9494
╰─ 🛑 Default value required for non-throwing closure 'endpoint'
95+
96+
Defaults are required so that the macro can generate a default, "unimplemented" version of the dependency. The default value can be anything and does not need to signify a real value. For example, if the endpoint returns a boolean, you can return 'false', or if it returns an array, you can return '[]'.
97+
98+
See the documentation for @DependencyClient for more information: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
9599
✏️ Insert '= { <#Bool#> }'
96100
}
97101
"""
98-
} fixes: {
102+
}fixes: {
99103
"""
100104
struct Client {
101105
@DependencyEndpoint
@@ -142,10 +146,14 @@ final class DependencyEndpointMacroTests: BaseTestCase {
142146
var endpoint: (Int, Bool, String) -> Bool
143147
┬────────────────────────────────────
144148
╰─ 🛑 Default value required for non-throwing closure 'endpoint'
149+
150+
Defaults are required so that the macro can generate a default, "unimplemented" version of the dependency. The default value can be anything and does not need to signify a real value. For example, if the endpoint returns a boolean, you can return 'false', or if it returns an array, you can return '[]'.
151+
152+
See the documentation for @DependencyClient for more information: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
145153
✏️ Insert '= { _, _, _ in <#Bool#> }'
146154
}
147155
"""
148-
} fixes: {
156+
}fixes: {
149157
"""
150158
struct Client {
151159
@DependencyEndpoint

0 commit comments

Comments
 (0)