-
Notifications
You must be signed in to change notification settings - Fork 87
Expand file tree
/
Copy pathFactKey.swift
More file actions
61 lines (56 loc) · 2.01 KB
/
Copy pathFactKey.swift
File metadata and controls
61 lines (56 loc) · 2.01 KB
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
import Foundation
import Sharing
import Synchronization
extension SharedReaderKey where Self == FactKey {
static func fact(_ number: Int?) -> Self {
Self(number: number)
}
}
// NB: This 'SharedReaderKey` conformance is designed to allow one to hold onto a 'fact: String?'
// in their features, while secretly it is powered by a network request to fetch the fact.
// Conformances to 'SharedReaderKey' must be Sendable, and so this is why the 'loadTask'
// variable is held in a mutex.
final class FactKey: SharedReaderKey {
let id = UUID()
let number: Int?
let loadTask = Mutex<Task<Void, Never>?>(nil)
init(number: Int?) {
self.number = number
}
func load(context _: LoadContext<String?>, continuation: LoadContinuation<String?>) {
guard let number else {
continuation.resume(returning: nil)
return
}
loadTask.withLock { task in
task?.cancel()
task = Task {
do {
// NB: This dependence on 'URLSession.shared' should be hidden behind a dependency
// so that features that use this shared state are still testable.
let (data, _) = try await URLSession.shared.data(
from: URL(string: "http://numbersapi.com/\(number)")!
)
// NB: The Numbers API can be quite fast. Let's simulate a slower connection.
try await Task.sleep(for: .seconds(1))
// NB: Simulate errors from the API for negative numbers.
guard number >= 0 else {
struct BoringNumber: LocalizedError {
let number: Int
var errorDescription: String? { "\(number) is a boring number." }
}
throw BoringNumber(number: number)
}
continuation.resume(returning: String(decoding: data, as: UTF8.self))
} catch {
continuation.resume(throwing: error)
}
}
}
}
func subscribe(
context _: LoadContext<String?>, subscriber _: SharedSubscriber<String?>
) -> SharedSubscription {
SharedSubscription {}
}
}