summaryrefslogtreecommitdiff
path: root/Hutch/Networking/SystemStatusRepository.swift
blob: 48c5b16ac662faa80502c8693e29a5459c4b67bc (plain) (blame)
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
import Foundation

actor SystemStatusRepository {
    private let service: SystemStatusService
    private let ttl: TimeInterval

    private var snapshotCache: CacheEntry<SystemStatusSnapshot>?
    private var incidentsCache: CacheEntry<[StatusIncident]>?

    init(service: SystemStatusService = SystemStatusService(), ttl: TimeInterval = 10 * 60) {
        self.service = service
        self.ttl = ttl
    }

    func snapshot(forceRefresh: Bool = false) async throws -> SystemStatusSnapshot {
        if let cached = snapshotCache, !forceRefresh, !cached.isExpired(ttl: ttl) {
            return cached.value
        }

        do {
            let snapshot = try await service.fetchSnapshot()
            snapshotCache = CacheEntry(value: snapshot, timestamp: Date())
            return snapshot
        } catch {
            if let cached = snapshotCache {
                return cached.value
            }
            throw error
        }
    }

    func recentIncidents(forceRefresh: Bool = false) async throws -> [StatusIncident] {
        if let cached = incidentsCache, !forceRefresh, !cached.isExpired(ttl: ttl) {
            return cached.value
        }

        do {
            let incidents = try await service.fetchIncidentFeed()
            incidentsCache = CacheEntry(value: incidents, timestamp: Date())
            return incidents
        } catch {
            if let cached = incidentsCache {
                return cached.value
            }
            throw error
        }
    }
}

private struct CacheEntry<Value: Sendable>: Sendable {
    let value: Value
    let timestamp: Date

    nonisolated func isExpired(ttl: TimeInterval) -> Bool {
        Date().timeIntervalSince(timestamp) > ttl
    }
}