From 205cf2687ce48f3d004792ff223422a86cc6bdbc Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Wed, 6 May 2026 20:41:49 -0500 Subject: feat(cache): persist read-only API responses Add a bounded stale-while-revalidate cache at the Sourcehut API boundary with stable keys, centralized TTLs, request coalescing, payload hashing, and LRU disk pruning. Cache high-value read-only repo, build, ticket, project, profile, paste, and Home/Work Queue data while keeping mutations network-only and invalidating related prefixes after successful writes. Add focused cache tests and implementation notes. --- Hutch/Views/Lookup/UserProfileViewModel.swift | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'Hutch/Views/Lookup/UserProfileViewModel.swift') diff --git a/Hutch/Views/Lookup/UserProfileViewModel.swift b/Hutch/Views/Lookup/UserProfileViewModel.swift index 0e503ce..c482d6f 100644 --- a/Hutch/Views/Lookup/UserProfileViewModel.swift +++ b/Hutch/Views/Lookup/UserProfileViewModel.swift @@ -49,13 +49,17 @@ final class UserProfileViewModel { defer { isLoadingRepositories = false } do { - let result = try await client.execute( + let cached = try await client.executeCached( service: .git, query: Self.repositoriesQuery, variables: ["owner": ownerUsername], - responseType: UserRepositoriesResponse.self + responseType: UserRepositoriesResponse.self, + cacheKey: APICacheKeys.userRepositories(owner: ownerUsername), + resourceType: .userProfile, + ttl: APICacheTTLs.userProfile, + policy: .cacheFirstThenRefresh ) - repositories = result.user.repositories.results.map { $0.repositorySummary(service: .git) } + repositories = cached.value.user.repositories.results.map { $0.repositorySummary(service: .git) } } catch { repositoriesError = error.userFacingMessage } @@ -78,13 +82,17 @@ final class UserProfileViewModel { defer { isLoadingTrackers = false } do { - let result = try await client.execute( + let cached = try await client.executeCached( service: .todo, query: Self.trackersQuery, variables: ["owner": ownerUsername], - responseType: UserTrackersResponse.self + responseType: UserTrackersResponse.self, + cacheKey: APICacheKeys.userTrackers(owner: ownerUsername), + resourceType: .userProfile, + ttl: APICacheTTLs.userProfile, + policy: .cacheFirstThenRefresh ) - trackers = result.user.trackers.results + trackers = cached.value.user.trackers.results } catch { trackersError = error.userFacingMessage } -- cgit v1.2.3