Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate preview dependencies trait #323

Merged
merged 15 commits into from
Jan 8, 2025
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,15 @@ and `UUID()`, and we'd have to wait for real world time to pass, making the test
But, controllable dependencies aren't only useful for tests. They can also be used in Xcode
previews. Suppose the feature above makes use of a clock to sleep for an amount of time before
something happens in the view. If you don't want to literally wait for time to pass in order to see
how the view changes, you can override the clock dependency to be an "immediate" clock using the
`.dependencies` preview trait:
how the view changes, you can override the clock dependency to be an "immediate" clock using
`prepareDependencies`:

```swift
#Preview(
traits: .dependencies {
#Preview {
let _ = prepareDependencies {
$0.continuousClock = .immediate
}
) {

// All access of '@Dependency(\.continuousClock)' in this preview will
// use an immediate clock.
FeatureView(model: FeatureModel())
Expand Down Expand Up @@ -267,7 +267,8 @@ If you want to discuss this library or have a question about how to use it to so
a particular problem, there are a number of places you can discuss with fellow
[Point-Free](http://www.pointfree.co) enthusiasts:

* For long-form discussions, we recommend the [discussions](http://github.com/pointfreeco/swift-dependencies/discussions) tab of this repo.
* For long-form discussions, we recommend the
[discussions](http://github.com/pointfreeco/swift-dependencies/discussions) tab of this repo.
* For casual chat, we recommend the [Point-Free Community Slack](http://pointfree.co/slack-invite).

## Extensions
Expand All @@ -277,7 +278,8 @@ following projects all build on top of Dependencies:

* [Dependencies Additions](https://github.com/tgrapperon/swift-dependencies-additions): A
companion library that provides higher-level dependencies.
* [Dependencies Protocol Extras](https://github.com/arasan01/swift-dependencies-extras): Library to make swift-dependencies even more useful when using Protocol
* [Dependencies Protocol Extras](https://github.com/arasan01/swift-dependencies-extras): A library
to make swift-dependencies even more useful when using protocols.

## Alternatives

Expand Down Expand Up @@ -310,5 +312,5 @@ This library is released under the MIT license. See [LICENSE](LICENSE) for detai
[syncups-demo]: https://github.com/pointfreeco/syncups
[swiftui-nav-gh]: http://github.com/pointfreeco/swiftui-navigation
[dep-values-docs]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/dependencyvalues#dependency-values
[withdependencies-docs]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/withdependencies(_:operation:)-4uz6m
[withdependencies-docs]: https://swiftpackageindex.com/pointfreeco/swift-dependencies/main/documentation/dependencies/withdependencies(isolation:_:operation:)
[immediate-clock-docs]: https://pointfreeco.github.io/swift-clocks/main/documentation/clocks/immediateclock
30 changes: 20 additions & 10 deletions Sources/Dependencies/Dependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@
/// }
/// ```
///
/// - Parameter keyPath: A key path to a specific resulting value.
/// - Parameters
/// - keyPath: A key path to a specific resulting value.
/// - fileID: The source `#fileID` associated with the dependency.
/// - filePath: The source `#filePath` associated with the dependency.
/// - line: The source `#line` associated with the dependency.
/// - column: The source `#column` associated with the dependency.
public init(
_ keyPath: KeyPath<DependencyValues, Value> & Sendable,
fileID: StaticString = #fileID,
Expand Down Expand Up @@ -174,7 +179,12 @@ extension Dependency {
/// }
/// ```
///
/// - Parameter key: A dependency key to a specific resulting value.
/// - Parameters
/// - key: A dependency key to a specific resulting value.
/// - fileID: The source `#fileID` associated with the dependency.
/// - filePath: The source `#filePath` associated with the dependency.
/// - line: The source `#line` associated with the dependency.
/// - column: The source `#column` associated with the dependency.
public init<Key: TestDependencyKey>(
_ key: Key.Type,
fileID: StaticString = #fileID,
Expand Down Expand Up @@ -233,19 +243,19 @@ extension DependencyValues {
get {
self[
Key.self,
key.fileID,
key.filePath,
key.line,
key.column
fileID: key.fileID,
filePath: key.filePath,
line: key.line,
column: key.column
Comment on lines +246 to +249
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed the lack of labels when fixing docs. This is technically breaking but I don't think anyone should be invoking these arguments directly outside the library and there's a fix-it to add the labels, but if we're worried I can undo this.

]
}
set {
self[
Key.self,
key.fileID,
key.filePath,
key.line,
key.column
fileID: key.fileID,
filePath: key.filePath,
line: key.line,
column: key.column
] = newValue
}
}
Expand Down
10 changes: 5 additions & 5 deletions Sources/Dependencies/DependencyValues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,11 @@ public struct DependencyValues: Sendable {
/// property wrapper.
public subscript<Key: TestDependencyKey>(
key: Key.Type,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #line,
function: StaticString = #function
fileID fileID: StaticString = #fileID,
filePath filePath: StaticString = #filePath,
line line: UInt = #line,
column column: UInt = #line,
function function: StaticString = #function
) -> Key.Value {
get {
guard let base = self.storage[ObjectIdentifier(key)], let dependency = base as? Key.Value
Expand Down
9 changes: 4 additions & 5 deletions Sources/Dependencies/Documentation.docc/Articles/Lifetimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ the scope of the `operation` closure.

However, care must be taken when creating a child model from a parent model. In order for the
child's dependencies to inherit from the parent's dependencies, you must make use of
``withDependencies(from:operation:file:line:)-8e74m`` when creating the child model:
``withDependencies(from:operation:fileID:filePath:line:column:)`` when creating the child model:

```swift
let onboardingModel = withDependencies(from: self) {
Expand All @@ -205,7 +205,7 @@ override any additional dependencies you want.

In general, if you want dependencies to be properly inherited through every layer of feature in your
application, you should make sure to create any observable models inside a
``withDependencies(from:operation:file:line:)-8e74m`` scope.
``withDependencies(from:operation:fileID:filePath:line:column:)`` scope.

If you do this, it also allows you to run previews in a very specific environment. Dependencies
already support the concept of a ``TestDependencyKey/previewValue-8u2sy``, which is an
Expand All @@ -218,11 +218,10 @@ feature behaves in very specific states. For example, if you wanted to see how y
when the `fetchUser` endpoint throws an error, you can update the preview like so:

```swift
#Preview(
traits: .dependencies {
#Preview {
let _ = prepareDependencies {
$0.apiClient.fetchUser = { _ in throw SomeError() }
}
) {
FeatureView(model: FeatureModel())
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ be written to disk, or user defaults to be written, or any number of things. It
use mock versions of those dependencies so that the user can interact with your feature in a fully
controlled environment.

To do this you need to make use of the ``withDependencies(from:_:operation:file:line:)-2y5dq``
method, which allows you to inherit the dependencies from an existing object _and_ additionally
override some of those dependencies:
To do this you need to make use of the
``withDependencies(from:operation:fileID:filePath:line:column:)`` function, which allows you to
inherit the dependencies from an existing object _and_ additionally override some of those
dependencies:

```swift
@Observable
Expand Down Expand Up @@ -48,8 +49,8 @@ interact with the outside world. This way you can be sure that while the user is
the tutorial sandbox they are not accidentally making network requests, saving data to disk or
overwriting settings in user defaults.

> Note: The method ``withDependencies(from:_:operation:file:line:)-262kg`` used in the code snippet
> above is subtly different from ``withDependencies(_:operation:)-3vrqy``. It takes an extra
> Note: The method ``withDependencies(from:operation:fileID:filePath:line:column:)`` used in the
> code snippet above is subtly different from ``withDependencies(_:operation:)``. It takes an extra
> argument, `from`, which is the object from which we propagate the dependencies before overriding
> some. This allows you to propagate dependencies from object to object.
>
Expand All @@ -60,7 +61,7 @@ overwriting settings in user defaults.

Extra care must be taken when overriding dependencies in order for the new dependencies to propagate
down to child models, and grandchild models, and on and on. All child models constructed should be
done so inside an invocation of ``withDependencies(from:operation:file:line:)-8e74m`` so
done so inside an invocation of ``withDependencies(from:operation:fileID:filePath:line:column:)`` so
that the child model picks up the exact dependencies the parent is using.

For example, taking the code sample from above, suppose that the `TodosModel` could drill down to an
Expand Down Expand Up @@ -94,7 +95,7 @@ any of the overridden dependencies from when the `TodosModel` was created.

In order to make sure the overridden dependencies continue to propagate to the child feature, you
must wrap the creation of the child model in
``withDependencies(from:operation:file:line:)-8e74m``:
``withDependencies(from:operation:fileID:filePath:line:column:)``:

```swift
func tappedTodo(_ todo: Todo) {
Expand Down
20 changes: 9 additions & 11 deletions Sources/Dependencies/Documentation.docc/Articles/QuickStart.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,18 @@ and `UUID()`, and we'd have to wait for real world time to pass, making the test
But, controllable dependencies aren't only useful for tests. They can also be used in Xcode
previews. Suppose the feature above makes use of a clock to sleep for an amount of time before
something happens in the view. If you don't want to literally wait for time to pass in order to see
how the view changes, you can override the clock dependency to be an "immediate" clock using the
``withDependencies(_:operation:)-4uz6m`` helper:
how the view changes, you can override the clock dependency to be an "immediate" clock using
``prepareDependencies(_:)``:

```swift
struct Feature_Previews: PreviewProvider {
static var previews: some View {
FeatureView(
model: withDependencies {
$0.clock = ImmediateClock()
} operation: {
FeatureModel()
}
)
#Preview {
let _ = prepareDependencies {
$0.continuousClock = .immediate
}

// All access of '@Dependency(\.continuousClock)' in this preview will
// use an immediate clock.
FeatureView(model: FeatureModel())
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extension APIClient: DependencyKey {
```

> Tip: There are two other values you can provide for a dependency. If you implement
> ``DependencyKey/testValue-5v726`` it will be used when running features in tests, and if you
> ``DependencyKey/testValue`` it will be used when running features in tests, and if you
> implement `previewValue` it will be used while running features in an Xcode preview. You don't
> need to worry about those values when you are just getting started, and instead can add them
> later. See <Doc:LivePreviewTest> for more information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ struct Feature {
enum Action {
// ...
}

func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
var body: some Reducer<State, Action> {
// All of the feature's logic and behavior is implemented here...
}
}
Expand Down Expand Up @@ -132,8 +131,7 @@ struct Feature {
enum Action {
// ...
}

var body: some ReducerOf<Self> {
var body: some Reducer<State, Action> {
Header()
.dependency(\.fileManager, .mock)
.dependency(\.userDefaults, .mock)
Expand Down Expand Up @@ -193,7 +191,7 @@ to the child.

For example, if your SwiftUI model holds a piece of optional state that drives a sheet, then when
hydrating that state you will want to wrap it in
``withDependencies(from:operation:file:line:)-8e74m``:
``withDependencies(from:operation:fileID:filePath:line:column:)``:

```swift
@Observable
Expand All @@ -218,7 +216,7 @@ This makes it so that if `FeatureModel` were constructed with some of its depend

The same principle holds for UIKit. When constructing a child view controller to be presented,
be sure to wrap its construction in
``withDependencies(from:operation:file:line:)-8e74m``:
``withDependencies(from:operation:fileID:filePath:line:column:)``:

```swift
final class FeatureViewController: UIViewController {
Expand All @@ -234,7 +232,7 @@ final class FeatureViewController: UIViewController {
}
```

If you make sure to always use ``withDependencies(from:operation:file:line:)-8e74m``
If you make sure to always use ``withDependencies(from:operation:fileID:filePath:line:column:)``
when constructing child models and controllers you can be sure that changes to dependencies at
any layer of your application will be visible at any layer below it. See <doc:Lifetimes> for
more information on how dependency lifetimes work.
Expand Down
9 changes: 5 additions & 4 deletions Sources/Dependencies/Documentation.docc/Articles/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ great for tests. It means your feature doesn't need to actually make network req
how your feature deals with data returned from an API, and your feature doesn't need to interact
with the file system just to test how data gets loaded or persisted.

The tool for doing this is ``withDependencies(_:operation:)-3vrqy``, which allows you to specify
The tool for doing this is ``withDependencies(_:operation:)``, which allows you to specify
which dependencies should be overridden for the test, and then construct your feature's model in
that context:

Expand All @@ -49,7 +49,8 @@ the date is "Feb 13, 2009 at 3:31 PM".
> models inside its methods, then it has to be careful about how it does so. In order for
> `FeatureModel`'s dependencies to propagate to the new child model, it must construct the child
> model in an altered execution context that passes along the dependencies. The tool for this is
> ``withDependencies(from:operation:file:line:)-2qx0c`` and can be used simply like this:
> ``withDependencies(from:operation:fileID:filePath:line:column:)`` and can be used simply like
> this:
>
> ```swift
> @Observable
Expand Down Expand Up @@ -78,7 +79,7 @@ For example, suppose we have a login feature such that if you try logging in and
causing a message to appear. But then later, if login succeeds that message goes away. We can
test that entire flow, from end-to-end, but starting the API client dependency in a state where
login fails, and then later change the dependency so that it succeeds using
``withDependencies(_:operation:)-3vrqy``:
``withDependencies(_:operation:)``:

```swift
@Test
Expand Down Expand Up @@ -242,7 +243,7 @@ still features missing from the Testing framework that XCTest has, there may be
steps you must take.

If you are are writing a _parameterized_ test using the `@Test` macro, you will need to surround the
entire body of your test in [`withDependencies`](<doc:withDependencies(_:operation:)-3vrqy>) that
entire body of your test in [`withDependencies`](<doc:withDependencies(_:operation:)>) that
resets the entire set of values to guarantee that a fresh set of dependencies is used per parameter:

```swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,11 @@ For previews, you can use the `.dependencies` preview trait to override the
does not actually sleep for any amount of time:

```swift
#Preview(
.dependencies { $0.continuousClock = .immediate }
) {
FeatureView(
model: withDependencies {
$0.continuousClock = ImmediateClock()
} operation: {
FeatureModel()
}
)
#Preview {
let _ = prepareDependencies {
$0.continuousClock = .immediate
}
FeatureView(model: FeatureModel())
}
```

Expand Down
6 changes: 0 additions & 6 deletions Sources/Dependencies/Documentation.docc/Dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,3 @@ This library addresses all of the points above, and much, _much_ more.
- ``DependencyValues``
- ``DependencyKey``
- ``DependencyContext``

### Concurrency support

- ``ActorIsolated``
- ``LockIsolated``
- ``UncheckedSendable``
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

### Using a dependency

- ``init(_:fileID:filePath:line:column:)``
- ``init(_:fileID:filePath:line:column:)-1f0mh``
- ``init(_:fileID:filePath:line:column:)-1ytea``

### Getting the value

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
### Creating and accessing values

- ``init()``
- ``subscript(key:file:function:line:)
- ``subscript(_:fileID:filePath:line:column:function:)``
- ``subscript(_:)``

### Overriding values

- ``withDependencies(_:operation:)-4uz6m``
- ``withDependencies(from:operation:file:line:)-8e74m``
- ``withDependencies(from:operation:fileID:filePath:line:column:)``
- ``prepareDependencies(_:)``

### Escaping contexts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,3 @@

- ``AssertionEffect``
- ``AssertionFailureEffect``

### Custom assertions

- ``AnyAssertionEffect``
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ instead.
### Overriding values

- ``DependencyValues/withTestValues(_:assert:)-6erij``
- ``DependencyValues/withTestValues(_:assert:)-1egh6``
- ``DependencyValues/withTestValues(_:assert:)-6qwh1``
- ``DependencyValues/withValues(_:operation:)-9prz8``
- ``DependencyValues/withValues(_:operation:)-4pas8``
- ``DependencyValues/withValues(_:operation:)-4omsq``
- ``DependencyValues/withValue(_:_:operation:)-4lu7m``
- ``DependencyValues/withValue(_:_:operation:)-2ewcx``
- ``DependencyValues/withValue(_:_:operation:)-65nv4``
Loading
Loading