Skip to content

Commit e3a60f8

Browse files
authored
Swift 5.6 support (#5)
* Swift 5.6 support * wip * wip * wip
1 parent e6c9b80 commit e3a60f8

File tree

6 files changed

+202
-113
lines changed

6 files changed

+202
-113
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
runs-on: macos-12
1919
strategy:
2020
matrix:
21-
xcode: ['14.2']
21+
xcode: ['13.4.1', '14.2']
2222
config: ['debug', 'release']
2323
steps:
2424
- uses: actions/checkout@v3

Sources/Dependencies/DependencyValues/WithRandomNumberGenerator.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,28 @@ extension DependencyValues {
6565
///
6666
/// See ``DependencyValues/withRandomNumberGenerator`` for more information.
6767
public final class WithRandomNumberGenerator: @unchecked Sendable {
68-
private var generator: RandomNumberGenerator
68+
private var generator: _AnyRandomNumberGenerator
6969
private let lock = NSLock()
7070

7171
public init<T: RandomNumberGenerator & Sendable>(_ generator: T) {
72-
self.generator = generator
72+
self.generator = _AnyRandomNumberGenerator(generator)
7373
}
7474

75-
public func callAsFunction<R>(_ work: (inout RandomNumberGenerator) -> R) -> R {
75+
public func callAsFunction<R>(_ work: (inout _AnyRandomNumberGenerator) -> R) -> R {
7676
self.lock.lock()
7777
defer { self.lock.unlock() }
7878
return work(&self.generator)
7979
}
80+
81+
public class _AnyRandomNumberGenerator: RandomNumberGenerator {
82+
private(set) public var base: RandomNumberGenerator
83+
84+
public init(_ base: RandomNumberGenerator) {
85+
self.base = base
86+
}
87+
88+
public func next() -> UInt64 {
89+
self.base.next()
90+
}
91+
}
8092
}

Sources/Dependencies/WithDependencies.swift

Lines changed: 175 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -39,45 +39,67 @@ public func withDependencies<R>(
3939
}
4040
}
4141

42-
/// Updates the current dependencies for the duration of an asynchronous operation.
43-
///
44-
/// Any mutations made to ``DependencyValues`` inside `updateValuesForOperation` will be visible to
45-
/// everything executed in the operation. For example, if you wanted to force the
46-
/// ``DependencyValues/date`` dependency to be a particular date, you can do:
47-
///
48-
/// ```swift
49-
/// await withDependencies {
50-
/// $0.date.now = Date(timeIntervalSince1970: 1234567890)
51-
/// } operation: {
52-
/// // References to date in here are pinned to 1234567890.
53-
/// }
54-
/// ```
55-
///
56-
/// - Parameters:
57-
/// - updateValuesForOperation: A closure for updating the current dependency values for the
58-
/// duration of the operation.
59-
/// - operation: An operation to perform wherein dependencies have been overridden.
60-
/// - Returns: The result returned from `operation`.
61-
@_unsafeInheritExecutor
62-
@discardableResult
63-
public func withDependencies<R>(
64-
_ updateValuesForOperation: (inout DependencyValues) async throws -> Void,
65-
operation: () async throws -> R
66-
) async rethrows -> R {
67-
try await DependencyValues.$isSetting.withValue(true) {
68-
var dependencies = DependencyValues._current
69-
try await updateValuesForOperation(&dependencies)
70-
return try await DependencyValues.$_current.withValue(dependencies) {
71-
try await DependencyValues.$isSetting.withValue(false) {
72-
let result = try await operation()
73-
if R.self is AnyClass {
74-
dependencyObjects.store(result as AnyObject)
42+
#if swift(>=5.7)
43+
/// Updates the current dependencies for the duration of an asynchronous operation.
44+
///
45+
/// Any mutations made to ``DependencyValues`` inside `updateValuesForOperation` will be visible
46+
/// to everything executed in the operation. For example, if you wanted to force the
47+
/// ``DependencyValues/date`` dependency to be a particular date, you can do:
48+
///
49+
/// ```swift
50+
/// await withDependencies {
51+
/// $0.date.now = Date(timeIntervalSince1970: 1234567890)
52+
/// } operation: {
53+
/// // References to date in here are pinned to 1234567890.
54+
/// }
55+
/// ```
56+
///
57+
/// - Parameters:
58+
/// - updateValuesForOperation: A closure for updating the current dependency values for the
59+
/// duration of the operation.
60+
/// - operation: An operation to perform wherein dependencies have been overridden.
61+
/// - Returns: The result returned from `operation`.
62+
@_unsafeInheritExecutor
63+
@discardableResult
64+
public func withDependencies<R>(
65+
_ updateValuesForOperation: (inout DependencyValues) async throws -> Void,
66+
operation: () async throws -> R
67+
) async rethrows -> R {
68+
try await DependencyValues.$isSetting.withValue(true) {
69+
var dependencies = DependencyValues._current
70+
try await updateValuesForOperation(&dependencies)
71+
return try await DependencyValues.$_current.withValue(dependencies) {
72+
try await DependencyValues.$isSetting.withValue(false) {
73+
let result = try await operation()
74+
if R.self is AnyClass {
75+
dependencyObjects.store(result as AnyObject)
76+
}
77+
return result
7578
}
76-
return result
7779
}
7880
}
7981
}
80-
}
82+
#else
83+
@discardableResult
84+
public func withDependencies<R>(
85+
_ updateValuesForOperation: (inout DependencyValues) async throws -> Void,
86+
operation: () async throws -> R
87+
) async rethrows -> R {
88+
try await DependencyValues.$isSetting.withValue(true) {
89+
var dependencies = DependencyValues._current
90+
try await updateValuesForOperation(&dependencies)
91+
return try await DependencyValues.$_current.withValue(dependencies) {
92+
try await DependencyValues.$isSetting.withValue(false) {
93+
let result = try await operation()
94+
if R.self is AnyClass {
95+
dependencyObjects.store(result as AnyObject)
96+
}
97+
return result
98+
}
99+
}
100+
}
101+
}
102+
#endif
81103

82104
/// Updates the current dependencies for the duration of a synchronous operation by taking the
83105
/// dependencies tied to a given object.
@@ -146,74 +168,129 @@ public func withDependencies<Model: AnyObject, R>(
146168
)
147169
}
148170

149-
/// Updates the current dependencies for the duration of an asynchronous operation by taking the
150-
/// dependencies tied to a given object.
151-
///
152-
/// - Parameters:
153-
/// - model: An object with dependencies. The given model should have at least one `@Dependency`
154-
/// property, or should have been initialized and returned from a `withDependencies` operation.
155-
/// - updateValuesForOperation: A closure for updating the current dependency values for the
156-
/// duration of the operation.
157-
/// - operation: The operation to run with the updated dependencies.
158-
/// - Returns: The result returned from `operation`.
159-
@_unsafeInheritExecutor
160-
@discardableResult
161-
public func withDependencies<Model: AnyObject, R>(
162-
from model: Model,
163-
_ updateValuesForOperation: (inout DependencyValues) async throws -> Void,
164-
operation: () async throws -> R,
165-
file: StaticString? = nil,
166-
line: UInt? = nil
167-
) async rethrows -> R {
168-
guard let values = dependencyObjects.values(from: model)
169-
else {
170-
runtimeWarn(
171-
"""
172-
You are trying to propagate dependencies to a child model from a model with no dependencies. \
173-
To fix this, the given '\(Model.self)' must be returned from another 'withDependencies' \
174-
closure, or the class must hold at least one '@Dependency' property.
175-
""",
171+
#if swift(>=5.7)
172+
/// Updates the current dependencies for the duration of an asynchronous operation by taking the
173+
/// dependencies tied to a given object.
174+
///
175+
/// - Parameters:
176+
/// - model: An object with dependencies. The given model should have at least one `@Dependency`
177+
/// property, or should have been initialized and returned from a `withDependencies`
178+
/// operation.
179+
/// - updateValuesForOperation: A closure for updating the current dependency values for the
180+
/// duration of the operation.
181+
/// - operation: The operation to run with the updated dependencies.
182+
/// - Returns: The result returned from `operation`.
183+
@_unsafeInheritExecutor
184+
@discardableResult
185+
public func withDependencies<Model: AnyObject, R>(
186+
from model: Model,
187+
_ updateValuesForOperation: (inout DependencyValues) async throws -> Void,
188+
operation: () async throws -> R,
189+
file: StaticString? = nil,
190+
line: UInt? = nil
191+
) async rethrows -> R {
192+
guard let values = dependencyObjects.values(from: model)
193+
else {
194+
runtimeWarn(
195+
"""
196+
You are trying to propagate dependencies to a child model from a model with no \
197+
dependencies. To fix this, the given '\(Model.self)' must be returned from another \
198+
'withDependencies' closure, or the class must hold at least one '@Dependency' property.
199+
""",
200+
file: file,
201+
line: line
202+
)
203+
return try await operation()
204+
}
205+
return try await withDependencies {
206+
$0 = values.merging(DependencyValues._current)
207+
try await updateValuesForOperation(&$0)
208+
} operation: {
209+
let result = try await operation()
210+
if R.self is AnyClass {
211+
dependencyObjects.store(result as AnyObject)
212+
}
213+
return result
214+
}
215+
}
216+
#else
217+
@discardableResult
218+
public func withDependencies<Model: AnyObject, R>(
219+
from model: Model,
220+
_ updateValuesForOperation: (inout DependencyValues) async throws -> Void,
221+
operation: () async throws -> R,
222+
file: StaticString? = nil,
223+
line: UInt? = nil
224+
) async rethrows -> R {
225+
guard let values = dependencyObjects.values(from: model)
226+
else {
227+
runtimeWarn(
228+
"""
229+
You are trying to propagate dependencies to a child model from a model with no \
230+
dependencies. To fix this, the given '\(Model.self)' must be returned from another \
231+
'withDependencies' closure, or the class must hold at least one '@Dependency' property.
232+
""",
233+
file: file,
234+
line: line
235+
)
236+
return try await operation()
237+
}
238+
return try await withDependencies {
239+
$0 = values.merging(DependencyValues._current)
240+
try await updateValuesForOperation(&$0)
241+
} operation: {
242+
let result = try await operation()
243+
if R.self is AnyClass {
244+
dependencyObjects.store(result as AnyObject)
245+
}
246+
return result
247+
}
248+
}
249+
#endif
250+
251+
#if swift(>=5.7)
252+
/// Updates the current dependencies for the duration of an asynchronous operation by taking the
253+
/// dependencies tied to a given object.
254+
///
255+
/// - Parameters:
256+
/// - model: An object with dependencies. The given model should have at least one `@Dependency`
257+
/// property, or should have been initialized and returned from a `withDependencies`
258+
/// operation.
259+
/// - operation: The operation to run with the updated dependencies.
260+
/// - Returns: The result returned from `operation`.
261+
@_unsafeInheritExecutor
262+
@discardableResult
263+
public func withDependencies<Model: AnyObject, R>(
264+
from model: Model,
265+
operation: () async throws -> R,
266+
file: StaticString? = nil,
267+
line: UInt? = nil
268+
) async rethrows -> R {
269+
try await withDependencies(
270+
from: model,
271+
{ _ in },
272+
operation: operation,
176273
file: file,
177274
line: line
178275
)
179-
return try await operation()
180276
}
181-
return try await withDependencies {
182-
$0 = values.merging(DependencyValues._current)
183-
try await updateValuesForOperation(&$0)
184-
} operation: {
185-
let result = try await operation()
186-
if R.self is AnyClass {
187-
dependencyObjects.store(result as AnyObject)
188-
}
189-
return result
277+
#else
278+
@discardableResult
279+
public func withDependencies<Model: AnyObject, R>(
280+
from model: Model,
281+
operation: () async throws -> R,
282+
file: StaticString? = nil,
283+
line: UInt? = nil
284+
) async rethrows -> R {
285+
try await withDependencies(
286+
from: model,
287+
{ _ in },
288+
operation: operation,
289+
file: file,
290+
line: line
291+
)
190292
}
191-
}
192-
193-
/// Updates the current dependencies for the duration of an asynchronous operation by taking the
194-
/// dependencies tied to a given object.
195-
///
196-
/// - Parameters:
197-
/// - model: An object with dependencies. The given model should have at least one `@Dependency`
198-
/// property, or should have been initialized and returned from a `withDependencies` operation.
199-
/// - operation: The operation to run with the updated dependencies.
200-
/// - Returns: The result returned from `operation`.
201-
@_unsafeInheritExecutor
202-
@discardableResult
203-
public func withDependencies<Model: AnyObject, R>(
204-
from model: Model,
205-
operation: () async throws -> R,
206-
file: StaticString? = nil,
207-
line: UInt? = nil
208-
) async rethrows -> R {
209-
try await withDependencies(
210-
from: model,
211-
{ _ in },
212-
operation: operation,
213-
file: file,
214-
line: line
215-
)
216-
}
293+
#endif
217294

218295
/// Propagates the current dependencies to an escaping context.
219296
///

Tests/DependenciesTests/DependencyTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ final class DependencyTests: XCTestCase {
102102
await withDependencies {
103103
await Task.yield()
104104
$0.string = "howdy"
105-
} operation: {
105+
} operation: { () -> Model in
106106
await Task.yield()
107107
return Model()
108108
}

Tests/DependenciesTests/DependencyValuesTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ final class DependencyValuesTests: XCTestCase {
103103

104104
withDependencies {
105105
$0.context = .preview
106-
} operation: {
106+
} operation: { () -> Void in
107107
XCTAssertEqual(reuseClient.count(), 0)
108108
reuseClient.setCount(1729)
109109
XCTAssertEqual(reuseClient.count(), 1729)
@@ -451,7 +451,7 @@ final class DependencyValuesTests: XCTestCase {
451451
func testTaskPropagation() async throws {
452452
let task = withDependencies {
453453
$0.date.now = Date(timeIntervalSinceReferenceDate: 1_234_567_890)
454-
} operation: {
454+
} operation: { () -> Task<Void, Never> in
455455
@Dependency(\.date.now) var now: Date
456456
XCTAssertEqual(now.timeIntervalSinceReferenceDate, 1_234_567_890)
457457
return Task {
@@ -483,7 +483,7 @@ final class DependencyValuesTests: XCTestCase {
483483
func testAsyncStreamUnfoldingWithoutEscapedDependencies() async {
484484
let stream = withDependencies {
485485
$0.fullDependency.value = 42
486-
} operation: {
486+
} operation: { () -> AsyncStream<Int> in
487487
var isDone = false
488488
return AsyncStream(unfolding: {
489489
defer { isDone = true }
@@ -499,7 +499,7 @@ final class DependencyValuesTests: XCTestCase {
499499
func testAsyncStreamUnfoldingWithEscapedDependencies() async {
500500
let stream = withDependencies {
501501
$0.fullDependency.value = 42
502-
} operation: {
502+
} operation: { () -> AsyncStream<Int> in
503503
var isDone = false
504504
return withEscapedDependencies { continuation in
505505
AsyncStream(unfolding: {

0 commit comments

Comments
 (0)