Skip to content

Commit

Permalink
A few more changes for Swift 6 mode. (#235)
Browse files Browse the repository at this point in the history
* A few more changes for Swift 6 mode.

* fix

* Fix sendability warnings in new Swift 6 toolchains (#241)

---------

Co-authored-by: Hal Lee <[email protected]>
  • Loading branch information
mbrandonw and hallee authored Jul 15, 2024
1 parent 8390ca8 commit cd2494d
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 191 deletions.
215 changes: 125 additions & 90 deletions Sources/Dependencies/Dependency.swift
Original file line number Diff line number Diff line change
@@ -1,102 +1,138 @@
/// A property wrapper for accessing dependencies.
///
/// All dependencies are stored in ``DependencyValues`` and one uses this property wrapper to gain
/// access to a particular dependency. Typically it used to provide dependencies to features such as
/// an observable object:
///
/// ```swift
/// final class FeatureModel: ObservableObject {
/// @Dependency(\.apiClient) var apiClient
/// @Dependency(\.continuousClock) var clock
/// @Dependency(\.uuid) var uuid
///
/// // ...
/// }
/// ```
///
/// Or, if you are using [the Composable Architecture][tca]:
///
/// ```swift
/// struct Feature: ReducerProtocol {
/// @Dependency(\.apiClient) var apiClient
/// @Dependency(\.continuousClock) var clock
/// @Dependency(\.uuid) var uuid
///
/// // ...
/// }
/// ```
///
/// But it can be used in other situations too, such as a helper function:
///
/// ```swift
/// func sharedEffect() async throws -> Action {
/// @Dependency(\.apiClient) var apiClient
/// @Dependency(\.continuousClock) var clock
///
/// // ...
/// }
/// ```
///
/// > Warning: There are caveats to using `@Dependency` in this style, especially for applications
/// not built in the Composable Architecture or that are not structured around a "single point of
/// entry" concept. See the articles <doc:Lifetimes> and <doc:SingleEntryPointSystems> for more
/// information.
///
/// > Important: Do **not** use `@Dependency` with "static" properties, _e.g._:
/// >
/// > ```swift
/// > struct User {
/// > @Dependency(\.uuid) static var uuid
/// > // ...
/// > }
/// > ```
/// >
/// > Static properties are lazily initialized in Swift, and so a static `@Dependency` will lazily
/// > capture its dependency values wherever it is first accessed, and will likely produce
/// > unexpected behavior.
///
/// For the complete list of dependency values provided by the library, see the properties of the
/// ``DependencyValues`` structure.
///
/// [tca]: https://github.com/pointfreeco/swift-composable-architecture
@propertyWrapper
public struct Dependency<Value>: @unchecked Sendable, _HasInitialValues {
let initialValues: DependencyValues
// NB: Key paths do not conform to sendable and are instead diagnosed at the time of forming the
// literal.
private let keyPath: KeyPath<DependencyValues, Value>
private let file: StaticString
private let fileID: StaticString
private let line: UInt

/// Creates a dependency property to read the specified key path.
#if swift(<6)
/// A property wrapper for accessing dependencies.
///
/// Don't call this initializer directly. Instead, declare a property with the `Dependency`
/// property wrapper, and provide the key path of the dependency value that the property should
/// reflect:
/// All dependencies are stored in ``DependencyValues`` and one uses this property wrapper to gain
/// access to a particular dependency. Typically it used to provide dependencies to features such as
/// an observable object:
///
/// ```swift
/// final class FeatureModel: ObservableObject {
/// @Dependency(\.date) var date
/// @Dependency(\.apiClient) var apiClient
/// @Dependency(\.continuousClock) var clock
/// @Dependency(\.uuid) var uuid
///
/// // ...
/// }
/// ```
///
/// - Parameter keyPath: A key path to a specific resulting value.
public init(
_ keyPath: KeyPath<DependencyValues, Value>,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) {
self.initialValues = DependencyValues._current
self.keyPath = keyPath
self.file = file
self.fileID = fileID
self.line = line
/// Or, if you are using [the Composable Architecture][tca]:
///
/// ```swift
/// struct Feature: ReducerProtocol {
/// @Dependency(\.apiClient) var apiClient
/// @Dependency(\.continuousClock) var clock
/// @Dependency(\.uuid) var uuid
///
/// // ...
/// }
/// ```
///
/// But it can be used in other situations too, such as a helper function:
///
/// ```swift
/// func sharedEffect() async throws -> Action {
/// @Dependency(\.apiClient) var apiClient
/// @Dependency(\.continuousClock) var clock
///
/// // ...
/// }
/// ```
///
/// > Warning: There are caveats to using `@Dependency` in this style, especially for applications
/// not built in the Composable Architecture or that are not structured around a "single point of
/// entry" concept. See the articles <doc:Lifetimes> and <doc:SingleEntryPointSystems> for more
/// information.
///
/// > Important: Do **not** use `@Dependency` with "static" properties, _e.g._:
/// >
/// > ```swift
/// > struct User {
/// > @Dependency(\.uuid) static var uuid
/// > // ...
/// > }
/// > ```
/// >
/// > Static properties are lazily initialized in Swift, and so a static `@Dependency` will lazily
/// > capture its dependency values wherever it is first accessed, and will likely produce
/// > unexpected behavior.
///
/// For the complete list of dependency values provided by the library, see the properties of the
/// ``DependencyValues`` structure.
///
/// [tca]: https://github.com/pointfreeco/swift-composable-architecture
@propertyWrapper
public struct Dependency<Value>: @unchecked Sendable, _HasInitialValues {
let initialValues: DependencyValues
// NB: Key paths do not conform to sendable and are instead diagnosed at the time of forming the
// literal.
private let keyPath: KeyPath<DependencyValues, Value>
private let file: StaticString
private let fileID: StaticString
private let line: UInt

public init(
_ keyPath: KeyPath<DependencyValues, Value>,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) {
self.initialValues = DependencyValues._current
self.keyPath = keyPath
self.file = file
self.fileID = fileID
self.line = line
}

/// The current value of the dependency property.
public var wrappedValue: Value {
_wrappedValue
}
}
#else
@propertyWrapper
public struct Dependency<Value>: Sendable, _HasInitialValues {
let initialValues: DependencyValues
private let keyPath: KeyPath<DependencyValues, Value> & Sendable
private let file: StaticString
private let fileID: StaticString
private let line: UInt

/// Creates a dependency property to read the specified key path.
///
/// Don't call this initializer directly. Instead, declare a property with the `Dependency`
/// property wrapper, and provide the key path of the dependency value that the property should
/// reflect:
///
/// ```swift
/// final class FeatureModel: ObservableObject {
/// @Dependency(\.date) var date
///
/// // ...
/// }
/// ```
///
/// - Parameter keyPath: A key path to a specific resulting value.
public init(
_ keyPath: KeyPath<DependencyValues, Value> & Sendable,
file: StaticString = #file,
fileID: StaticString = #fileID,
line: UInt = #line
) {
self.initialValues = DependencyValues._current
self.keyPath = keyPath
self.file = file
self.fileID = fileID
self.line = line
}

/// The current value of the dependency property.
public var wrappedValue: Value {
_wrappedValue
}
}
#endif

extension Dependency {
/// Creates a dependency property to read a dependency object.
///
/// Don't call this initializer directly. Instead, declare a property with the `Dependency`
Expand Down Expand Up @@ -138,8 +174,7 @@ public struct Dependency<Value>: @unchecked Sendable, _HasInitialValues {
)
}

/// The current value of the dependency property.
public var wrappedValue: Value {
fileprivate var _wrappedValue: Value {
#if DEBUG
var currentDependency = DependencyValues.currentDependency
currentDependency.file = self.file
Expand Down
Loading

0 comments on commit cd2494d

Please sign in to comment.