-
Notifications
You must be signed in to change notification settings - Fork 136
/
Copy pathUUID.swift
119 lines (110 loc) · 3.9 KB
/
UUID.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import Foundation
extension DependencyValues {
/// A dependency that generates UUIDs.
///
/// Introduce controllable UUID generation to your features by using the ``Dependency`` property
/// wrapper with a key path to this property. The wrapped value is an instance of
/// ``UUIDGenerator``, which can be called with a closure to create UUIDs. (It can be called
/// directly because it defines ``UUIDGenerator/callAsFunction()``, which is called when you
/// invoke the instance as you would invoke a function.)
///
/// For example, you could introduce controllable UUID generation to an observable object model
/// that creates to-dos with unique identifiers:
///
/// ```swift
/// final class TodosModel: ObservableObject {
/// @Published var todos: [Todo] = []
/// @Dependency(\.uuid) var uuid
///
/// func addButtonTapped() {
/// self.todos.append(Todo(id: self.uuid()))
/// }
/// }
/// ```
///
/// By default, a "live" generator is supplied, which returns a random UUID when called by
/// invoking `UUID.init` under the hood. When used in tests, an "unimplemented" generator that
/// additionally reports test failures if invoked, unless explicitly overridden.
///
/// To test a feature that depends on UUID generation, you can override its generator using
/// ``withDependencies(_:operation:)-4uz6m`` to override the underlying ``UUIDGenerator``:
///
/// * ``UUIDGenerator/incrementing`` for reproducible UUIDs that count up from
/// `00000000-0000-0000-0000-000000000000`.
///
/// * ``UUIDGenerator/constant(_:)`` for a generator that always returns the given UUID.
///
/// For example, you could test the to-do-creating model by supplying an
/// ``UUIDGenerator/incrementing`` generator as a dependency:
///
/// ```swift
/// func testFeature() {
/// let model = withDependencies {
/// $0.uuid = .incrementing
/// } operation: {
/// TodosModel()
/// }
///
/// model.addButtonTapped()
/// XCTAssertEqual(
/// model.todos,
/// [Todo(id: UUID(uuidString: "00000000-0000-0000-0000-000000000000")!)]
/// )
/// }
/// ```
public var uuid: UUIDGenerator {
get { self[UUIDGeneratorKey.self] }
set { self[UUIDGeneratorKey.self] = newValue }
}
private enum UUIDGeneratorKey: DependencyKey {
static let liveValue = UUIDGenerator { UUID() }
}
}
/// A dependency that generates a UUID.
///
/// See ``DependencyValues/uuid`` for more information.
public struct UUIDGenerator: Sendable {
private let generate: @Sendable () -> UUID
/// A generator that returns a constant UUID.
///
/// - Parameter uuid: A UUID to return.
/// - Returns: A generator that always returns the given UUID.
public static func constant(_ uuid: UUID) -> Self {
Self { uuid }
}
/// A generator that generates UUIDs in incrementing order.
///
/// For example:
///
/// ```swift
/// let generate = UUIDGenerator.incrementing
/// generate() // UUID(00000000-0000-0000-0000-000000000000)
/// generate() // UUID(00000000-0000-0000-0000-000000000001)
/// generate() // UUID(00000000-0000-0000-0000-000000000002)
/// ```
public static var incrementing: Self {
let generator = IncrementingUUIDGenerator()
return Self { generator() }
}
/// Initializes a UUID generator that generates a UUID from a closure.
///
/// - Parameter generate: A closure that returns the current date when called.
public init(_ generate: @escaping @Sendable () -> UUID) {
self.generate = generate
}
public func callAsFunction() -> UUID {
self.generate()
}
}
private final class IncrementingUUIDGenerator: @unchecked Sendable {
private let lock = NSLock()
private var sequence = 0
func callAsFunction() -> UUID {
self.lock.lock()
defer {
self.sequence += 1
self.lock.unlock()
}
return UUID(uuidString: "00000000-0000-0000-0000-\(String(format: "%012x", self.sequence))")!
}
}