You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -310,6 +310,62 @@ public struct EditProfileView: View, Instantiable {
310
310
}
311
311
```
312
312
313
+
#### Conditionally receiving dependencies
314
+
315
+
It is possible to receive an optional dependency only when that dependency has been `@Instantiated` or `@Forwarded` by an object higher up in the dependency tree with the `@Received(onlyIfAvailable: true)` macro. This functionality is particularly useful when `@Instantiable` types are created by multiple `@Instantiable` parents with different available dependencies.
316
+
317
+
Here’s an example of a feed view in a social app that optionally receives a `user` object:
When you want to instantiate a dependency after `init(…)`, you need to declare an `Instantiator<Dependency>`-typed property as `@Instantiated` or `@Received`. Deferred instantiation is useful in situations where a dependency is expensive to create or only required under certain conditions (e.g., creating a detailed view for a selected item in a list).
Copy file name to clipboardExpand all lines: Sources/SafeDI/Decorators/Received.swift
+7-2Lines changed: 7 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -25,8 +25,11 @@
25
25
/// @Received private let dependency: DependencyType
26
26
///
27
27
/// Note that the access level of the dependency in the above example does not affect the dependency tree – a `private` dependency can still be `@Received` by `@Instantiable`-decorated types further down the dependency tree.
28
+
///
29
+
/// - Parameters:
30
+
/// - onlyIfAvailable: Whether to allow the dependency to be received only when a parent has made it available.
28
31
@attached(peer)
29
-
public macro Received()= #externalMacro(module:"SafeDIMacros", type:"InjectableMacro")
32
+
public macro Received(onlyIfAvailable:Bool=false)= #externalMacro(module:"SafeDIMacros", type:"InjectableMacro")
30
33
31
34
/// Marks a SafeDI dependency that is instantiated or forwarded by an `@Instantiable` instance higher up in the dependency tree whose name and/or type is being changed from the dependency‘s initial declaration.
/// - fulfilledByDependencyNamed: The name of the property belonging to an `@Instantiable` instance higher up in the dependency tree whose name and/or type is being changed from the dependency‘s initial declaration.
45
48
/// - concreteType: The type of the property belonging to an `@Instantiable` instance higher up in the dependency tree whose name and/or type is being changed from the dependency‘s initial declaration.
46
49
/// - erasedToConcreteExistential: Whether the concrete type is being erased to a concrete existential type – a type that encapsulates a value conforming to a protocol without revealing the underlying type. Set this parameter to `true` to encapsulate the renamed or retyped instance of `concreteType` in a no-label initializer of the property’s type (e.g. `AnyDependencyType(dependency)`).
50
+
/// - onlyIfAvailable: Whether to allow the dependency to be received only when a parent has made it available.
47
51
///
48
52
/// - SeeAlso: [The Swift Programming Lanaguage’s explanation of existential types](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes/#Boxed-Protocol-Types)
Copy file name to clipboardExpand all lines: Sources/SafeDICore/Errors/FixableInjectableError.swift
+7-1Lines changed: 7 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -22,11 +22,14 @@ import SwiftDiagnostics
22
22
23
23
publicenumFixableInjectableError:DiagnosticError{
24
24
case unexpectedMutable
25
+
case onlyIfAvailableNotOptionalSpelledWithQuestionMark
25
26
26
27
publicvardescription:String{
27
28
switchself{
28
29
case.unexpectedMutable:
29
30
"Dependency can not be mutable unless it is decorated with a property wrapper. Mutations to a dependency are not propagated through the dependency tree."
@\(Dependency.Source.receivedRawValue) property `\($0.property.asSource)` is not @\(Dependency.Source.instantiatedRawValue) or @\(Dependency.Source.forwardedRawValue) in chain:\n\t\(([$0.instantiable.concreteInstantiable]+ $0.parentStack)
104
-
.reversed()
105
-
.map(\.asSource)
106
-
.joined(separator:" -> "))\($0.suggestedAlternatives.isEmpty ?"":"\n\nDid you mean one of the following available properties?\n\($0.suggestedAlternatives.map{"\t`\($0.asSource)`"}.joined(separator:"\n"))")
107
-
"""
102
+
if $0.property.typeDescription.isOptional,
103
+
let nonOptionalAlternative = $0.suggestedAlternatives.first(where:{[unfulfilledProperty = $0.property] alternative in
@\(Dependency.Source.receivedRawValue) property `\($0.property.asSource)` is not @\(Dependency.Source.instantiatedRawValue) or @\(Dependency.Source.forwardedRawValue) in chain:\n\t\(([$0.instantiable.concreteInstantiable]+ $0.parentStack)
110
+
.reversed()
111
+
.map(\.asSource)
112
+
.joined(separator:" -> "))\($0.suggestedAlternatives.isEmpty ?"":"\n\nThe non-optional `\(nonOptionalAlternative.asSource)` is available in chain. Did you mean to decorate this property with `@\(Dependency.Source.receivedRawValue)(onlyIfAvailable: true)`?")
113
+
"""
114
+
}else{
115
+
"""
116
+
@\(Dependency.Source.receivedRawValue) property `\($0.property.asSource)` is not @\(Dependency.Source.instantiatedRawValue) or @\(Dependency.Source.forwardedRawValue) in chain:\n\t\(([$0.instantiable.concreteInstantiable]+ $0.parentStack)
117
+
.reversed()
118
+
.map(\.asSource)
119
+
.joined(separator:" -> "))\($0.suggestedAlternatives.isEmpty ?"":"\n\nDid you mean one of the following available properties?\n\($0.suggestedAlternatives.map{"\t`\($0.asSource)`"}.joined(separator:"\n"))")
120
+
"""
121
+
}
108
122
}
109
123
.sorted()
110
124
.joined(separator:"\n\n"))
@@ -163,6 +177,7 @@ public actor DependencyTreeGenerator {
0 commit comments