From e3dacf7b1fac0044ec36a18cae32169ceb401249 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 19 Mar 2025 13:02:54 -0700 Subject: [PATCH 1/3] Undeprecate preview traits --- Sources/Dependencies/DependencyValues.swift | 6 +-- .../Dependencies/Internal/Deprecations.swift | 54 ------------------- .../Dependencies/Traits/PreviewTrait.swift | 33 ++++++++++++ 3 files changed, 34 insertions(+), 59 deletions(-) diff --git a/Sources/Dependencies/DependencyValues.swift b/Sources/Dependencies/DependencyValues.swift index ba51b626..17be92f4 100644 --- a/Sources/Dependencies/DependencyValues.swift +++ b/Sources/Dependencies/DependencyValues.swift @@ -579,11 +579,7 @@ public final class CachedValues: @unchecked Sendable { } if !CachedValues.isAccessingCachedDependencies { value = CachedValues.$isAccessingCachedDependencies.withValue(true) { - #if canImport(SwiftUI) && compiler(>=6) - return previewValues[key] - #else - return Key.previewValue - #endif + return Key.previewValue } } else { value = Key.previewValue diff --git a/Sources/Dependencies/Internal/Deprecations.swift b/Sources/Dependencies/Internal/Deprecations.swift index 8c7f8343..04cfaac8 100644 --- a/Sources/Dependencies/Internal/Deprecations.swift +++ b/Sources/Dependencies/Internal/Deprecations.swift @@ -1,57 +1,3 @@ -// MARK: - Deprecated after 1.6.3 - -#if canImport(SwiftUI) && compiler(>=6) - import SwiftUI - - @available(iOS 18, macOS 15, tvOS 18, watchOS 11, visionOS 2, *) - extension PreviewTrait where T == Preview.ViewTraits { - @available( - *, deprecated, - message: """ - Use 'withDependencies' or 'prepareDependencies' from the body of the preview, instead. - """ - ) - @_documentation(visibility: private) - public static func dependency( - _ keyPath: WritableKeyPath & Sendable, - _ value: Value - ) -> PreviewTrait { - .dependencies { $0[keyPath: keyPath] = value } - } - - @available( - *, deprecated, - message: """ - Use 'withDependencies' or 'prepareDependencies' from the body of the preview, instead. - """ - ) - @_documentation(visibility: private) - public static func dependency( - _ value: Value - ) -> PreviewTrait where Value == Value.Value { - .dependencies { $0[Value.self] = value } - } - - @available( - *, deprecated, - message: """ - Use 'withDependencies' or 'prepareDependencies' from the body of the preview, instead. - """ - ) - @_documentation(visibility: private) - public static func dependencies( - _ updateValuesForPreview: (inout DependencyValues) -> Void - ) -> PreviewTrait { - var copy = previewValues - defer { previewValues = copy } - updateValuesForPreview(©) - return PreviewTrait() - } - } - - nonisolated(unsafe) var previewValues = DependencyValues(context: .preview) -#endif - // MARK: - Deprecated after 0.4.2 extension AsyncStream { diff --git a/Sources/Dependencies/Traits/PreviewTrait.swift b/Sources/Dependencies/Traits/PreviewTrait.swift index 8b137891..91d376e9 100644 --- a/Sources/Dependencies/Traits/PreviewTrait.swift +++ b/Sources/Dependencies/Traits/PreviewTrait.swift @@ -1 +1,34 @@ +#if canImport(SwiftUI) && compiler(>=6) + import SwiftUI + @available(iOS 18, macOS 15, tvOS 18, watchOS 11, visionOS 2, *) + extension PreviewTrait where T == Preview.ViewTraits { + public static func dependency( + _ keyPath: WritableKeyPath & Sendable, + _ value: @escaping @Sendable () -> Value + ) -> PreviewTrait { + .dependencies { $0[keyPath: keyPath] = value() } + } + + public static func dependency( + _ value: Value + ) -> PreviewTrait where Value == Value.Value { + .dependencies { $0[Value.self] = value } + } + + public static func dependencies( + _ updateValuesForPreview: @escaping @Sendable (inout DependencyValues) -> Void + ) -> PreviewTrait { + return .modifier(DependenciesPreviewModifier(updateValuesForPreview: updateValuesForPreview)) + } + } + + private struct DependenciesPreviewModifier: PreviewModifier { + let updateValuesForPreview: @Sendable (inout DependencyValues) -> Void + + func body(content: Content, context: ()) -> some View { + prepareDependencies(updateValuesForPreview) + return content + } + } +#endif From d78f887c50bae36c2da2428ce2cc15ecebe31023 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 31 Mar 2025 16:10:10 -0700 Subject: [PATCH 2/3] wip --- Sources/Dependencies/Traits/PreviewTrait.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Dependencies/Traits/PreviewTrait.swift b/Sources/Dependencies/Traits/PreviewTrait.swift index 91d376e9..36f5635e 100644 --- a/Sources/Dependencies/Traits/PreviewTrait.swift +++ b/Sources/Dependencies/Traits/PreviewTrait.swift @@ -5,7 +5,7 @@ extension PreviewTrait where T == Preview.ViewTraits { public static func dependency( _ keyPath: WritableKeyPath & Sendable, - _ value: @escaping @Sendable () -> Value + _ value: @autoclosure @escaping @Sendable () -> Value ) -> PreviewTrait { .dependencies { $0[keyPath: keyPath] = value() } } From 8c1214b9a359fbc3a54dc761e73295e160eb33b7 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 31 Mar 2025 16:26:16 -0700 Subject: [PATCH 3/3] docs --- README.md | 10 +++------ .../Documentation.docc/Articles/Lifetimes.md | 7 +++--- .../Articles/LivePreviewTest.md | 5 +++++ .../Documentation.docc/Articles/QuickStart.md | 10 +++------ .../Articles/WhatAreDependencies.md | 5 +---- .../PreviewTraitsTests.swift | 22 ------------------- 6 files changed, 15 insertions(+), 44 deletions(-) delete mode 100644 Tests/DependenciesTests/PreviewTraitsTests.swift diff --git a/README.md b/README.md index 75676892..1c77d62f 100644 --- a/README.md +++ b/README.md @@ -164,15 +164,11 @@ 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 -`prepareDependencies`: +how the view changes, you can override the clock dependency to be an "immediate" clock using the +`.dependencies` preview trait: ```swift -#Preview { - let _ = prepareDependencies { - $0.continuousClock = .immediate - } - +#Preview(trait: .dependencies { $0.continuousClock = ImmediateClock() }) { // All access of '@Dependency(\.continuousClock)' in this preview will // use an immediate clock. FeatureView(model: FeatureModel()) diff --git a/Sources/Dependencies/Documentation.docc/Articles/Lifetimes.md b/Sources/Dependencies/Documentation.docc/Articles/Lifetimes.md index f46ef843..7f16e469 100644 --- a/Sources/Dependencies/Documentation.docc/Articles/Lifetimes.md +++ b/Sources/Dependencies/Documentation.docc/Articles/Lifetimes.md @@ -218,10 +218,9 @@ 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 { - let _ = prepareDependencies { - $0.apiClient.fetchUser = { _ in throw SomeError() } - } +#Preview(traits: .dependencies { + $0.apiClient.fetchUser = { _ in throw SomeError() } +}) { FeatureView(model: FeatureModel()) } ``` diff --git a/Sources/Dependencies/Documentation.docc/Articles/LivePreviewTest.md b/Sources/Dependencies/Documentation.docc/Articles/LivePreviewTest.md index 07759451..43073753 100644 --- a/Sources/Dependencies/Documentation.docc/Articles/LivePreviewTest.md +++ b/Sources/Dependencies/Documentation.docc/Articles/LivePreviewTest.md @@ -306,3 +306,8 @@ func testFeature() { [unimplemented-docs]: https://pointfreeco.github.io/xctest-dynamic-overlay/main/documentation/xctestdynamicoverlay/unimplemented(_:fileid:line:)-5098a [issue-reporting-gh]: http://github.com/pointfreeco/xctest-dynamic-overlay +## Topics + +### Previews + +- ``DeveloperToolsSupport/PreviewTrait`` diff --git a/Sources/Dependencies/Documentation.docc/Articles/QuickStart.md b/Sources/Dependencies/Documentation.docc/Articles/QuickStart.md index c5b048fc..86ed4f5b 100644 --- a/Sources/Dependencies/Documentation.docc/Articles/QuickStart.md +++ b/Sources/Dependencies/Documentation.docc/Articles/QuickStart.md @@ -118,15 +118,11 @@ 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 -``prepareDependencies(_:)``: +how the view changes, you can override the clock dependency to be an "immediate" clock using the +``DeveloperToolsSupport/PreviewTrait/dependencies(_:)`` preview trait: ```swift -#Preview { - let _ = prepareDependencies { - $0.continuousClock = .immediate - } - +#Preview(trait: .dependencies { $0.continuousClock = ImmediateClock() }) { // All access of '@Dependency(\.continuousClock)' in this preview will // use an immediate clock. FeatureView(model: FeatureModel()) diff --git a/Sources/Dependencies/Documentation.docc/Articles/WhatAreDependencies.md b/Sources/Dependencies/Documentation.docc/Articles/WhatAreDependencies.md index c9454fd9..41038171 100644 --- a/Sources/Dependencies/Documentation.docc/Articles/WhatAreDependencies.md +++ b/Sources/Dependencies/Documentation.docc/Articles/WhatAreDependencies.md @@ -104,10 +104,7 @@ For previews, you can use the `.dependencies` preview trait to override the does not actually sleep for any amount of time: ```swift -#Preview { - let _ = prepareDependencies { - $0.continuousClock = .immediate - } +#Preview(traits: .dependencies { $0.continuousClock = ImmediateClock() }) { FeatureView(model: FeatureModel()) } ``` diff --git a/Tests/DependenciesTests/PreviewTraitsTests.swift b/Tests/DependenciesTests/PreviewTraitsTests.swift deleted file mode 100644 index 5e3d93c9..00000000 --- a/Tests/DependenciesTests/PreviewTraitsTests.swift +++ /dev/null @@ -1,22 +0,0 @@ -#if canImport(Testing) && (os(iOS) || os(macOS) || os(tvOS) || os(watchOS)) - import Dependencies - import Testing - import SwiftUI - - @Suite - @MainActor - struct PreviewTraitsTests { - @Test - @available(iOS 18, macOS 15, tvOS 18, watchOS 11, visionOS 2, *) - @available(*, deprecated, message: "") - func dependency() { - _ = PreviewTrait.dependency(\.date.now, Date(timeIntervalSince1970: 1_234_567_890)) - withDependencies { - $0.context = .preview - } operation: { - @Dependency(\.date.now) var now - #expect(now == Date(timeIntervalSince1970: 1_234_567_890)) - } - } - } -#endif