Skip to content

Commit

Permalink
Update docs for @DependencyClient about default values. (#164)
Browse files Browse the repository at this point in the history
* Update docs for @DependencyClient about default values.

* wip

* wip

* wip

* Apply suggestions from code review

---------

Co-authored-by: Stephen Celis <[email protected]>
  • Loading branch information
mbrandonw and stephencelis committed Dec 15, 2023
1 parent 25c9fe9 commit c31b144
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 3 deletions.
31 changes: 31 additions & 0 deletions Sources/DependenciesMacros/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,37 @@
/// instances of the client. Creating that initializer manually is quite laborious, and you have to
/// update it each time a new endpoint is added to the client.
///
/// ## Restrictions
///
/// Usage of the ``DependencyClient()`` macro does have a restriction to be aware of. If your
/// client has a closure that is non-throwing and non-void returning like below, then you
/// will get a compile-time error letting you know a default must be provided:
///
/// ```swift
/// @DependencyClient
/// struct APIClient {
/// // 🛑 Default value required for non-throwing closure 'isFavorite'
/// var isFavorite: () -> Bool
/// }
/// ```
///
/// The error also comes with a helpful fix-it to let you know what needs to be done:
///
/// ```swift
/// @DependencyClient
/// struct APIClient {
/// var isFavorite: () -> Bool = { <#Bool#> }
/// }
/// ```
///
/// The reason we require a default for these endpoints is so that you immediately get access to
/// a default client via `APIClient()`, which is handy to use in tests and SwiftUI previews. The
/// only way to do this, without crashing at runtime, is if you provide defaults for your endpoints.
///
/// To fix you must supply a closure that returns a default value. 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 `[]`.`
///
/// [designing-dependencies]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/designingdependencies
/// [separating-interface-implementation]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/livepreviewtest#Separating-interface-and-implementation
@attached(member, names: named(init))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public enum DependencyClientMacro: MemberAttributeMacro, MemberMacro {
var unimplementedDefault = functionType.unimplementedDefault
unimplementedDefault.append(placeholder: functionType.returnClause.type.trimmed.description)
context.diagnose(
clientName: declaration.as(StructDeclSyntax.self)?.name.text,
node: binding,
identifier: identifier,
unimplementedDefault: unimplementedDefault
Expand Down
9 changes: 9 additions & 0 deletions Sources/DependenciesMacrosPlugin/Support.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ extension VariableDeclSyntax {

extension MacroExpansionContext {
func diagnose(
clientName: String? = nil,
node: PatternBindingSyntax,
identifier: TokenSyntax,
unimplementedDefault: ClosureExprSyntax
Expand All @@ -188,6 +189,14 @@ extension MacroExpansionContext {
message: MacroExpansionErrorMessage(
"""
Default value required for non-throwing closure '\(identifier)'
Defaults are required so that the macro can generate a default, "unimplemented" version \
of the dependency\(clientName.map { " via '\($0)()'"} ?? ""). 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 '[]'.
See the documentation for @DependencyClient for more information: \
https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
"""
),
fixIt: FixIt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,10 +638,14 @@ final class DependencyClientMacroTests: BaseTestCase {
var endpoint: @Sendable () -> Int
┬────────────────────────────
╰─ 🛑 Default value required for non-throwing closure 'endpoint'
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 '[]'.
See the documentation for @DependencyClient for more information: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
✏️ Insert '= { <#Int#> }'
}
"""
} fixes: {
}fixes: {
"""
@DependencyClient
struct Client: Sendable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,14 @@ final class DependencyEndpointMacroTests: BaseTestCase {
var endpoint: () -> Bool
┬───────────────────
╰─ 🛑 Default value required for non-throwing closure 'endpoint'
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 '[]'.
See the documentation for @DependencyClient for more information: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
✏️ Insert '= { <#Bool#> }'
}
"""
} fixes: {
}fixes: {
"""
struct Client {
@DependencyEndpoint
Expand Down Expand Up @@ -142,10 +146,14 @@ final class DependencyEndpointMacroTests: BaseTestCase {
var endpoint: (Int, Bool, String) -> Bool
┬────────────────────────────────────
╰─ 🛑 Default value required for non-throwing closure 'endpoint'
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 '[]'.
See the documentation for @DependencyClient for more information: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependenciesmacros/dependencyclient()#Restrictions
✏️ Insert '= { _, _, _ in <#Bool#> }'
}
"""
} fixes: {
}fixes: {
"""
struct Client {
@DependencyEndpoint
Expand Down

0 comments on commit c31b144

Please sign in to comment.