From 91d9eb23c33ae8c1a6e19314c0349e42eab70326 Mon Sep 17 00:00:00 2001 From: Michal Smaga Date: Fri, 22 Mar 2024 20:21:05 +0100 Subject: [PATCH] Rework the caching logic for subscription and entitlements (#741) * Remove default cache expiry time * set 20min cache expiry for subscriptions * set 20min cache expiry for entitlements * When caching subscription if expiry or renewal is earlier then use it * Rework refreshSubscriptionAndEntitlements * Add convenience init * Swiftlint fix --- Sources/Subscription/AccountManager.swift | 24 +++++++++++++++---- .../Services/SubscriptionService.swift | 7 ++++-- Sources/Subscription/UserDefaultsCache.swift | 15 ++++-------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Sources/Subscription/AccountManager.swift b/Sources/Subscription/AccountManager.swift index 20cf1aa68..ed7acdd0a 100644 --- a/Sources/Subscription/AccountManager.swift +++ b/Sources/Subscription/AccountManager.swift @@ -53,10 +53,18 @@ public class AccountManager: AccountManaging { return accessToken != nil } + public convenience init(accessTokenStorage: SubscriptionTokenStorage) { + self.init(accessTokenStorage: accessTokenStorage, + entitlementsCache: UserDefaultsCache<[Entitlement]>(key: UserDefaultsCacheKey.subscriptionEntitlements, + settings: UserDefaultsCacheSettings(defaultExpirationInterval: .minutes(20)))) + } + public convenience init(subscriptionAppGroup: String) { let accessTokenStorage = SubscriptionTokenKeychainStorage(keychainType: .dataProtection(.named(subscriptionAppGroup))) self.init(accessTokenStorage: accessTokenStorage, - entitlementsCache: UserDefaultsCache<[Entitlement]>(subscriptionAppGroup: subscriptionAppGroup, key: UserDefaultsCacheKey.subscriptionEntitlements)) + entitlementsCache: UserDefaultsCache<[Entitlement]>(subscriptionAppGroup: subscriptionAppGroup, + key: UserDefaultsCacheKey.subscriptionEntitlements, + settings: UserDefaultsCacheSettings(defaultExpirationInterval: .minutes(20)))) } public init(storage: AccountStorage = AccountKeychainStorage(), @@ -304,16 +312,22 @@ public class AccountManager: AccountManaging { } } - public func checkSubscriptionState() async { - os_log(.info, log: .subscription, "[AccountManager] checkSubscriptionState") + public func refreshSubscriptionAndEntitlements() async { + os_log(.info, log: .subscription, "[AccountManager] refreshSubscriptionAndEntitlements") - guard let token = accessToken else { return } + guard let token = accessToken else { + SubscriptionService.signOut() + entitlementsCache.reset() + return + } - if case .success(let subscription) = await SubscriptionService.getSubscription(accessToken: token) { + if case .success(let subscription) = await SubscriptionService.getSubscription(accessToken: token, cachePolicy: .reloadIgnoringLocalCacheData) { if !subscription.isActive { signOut() } } + + _ = await fetchEntitlements(cachePolicy: .reloadIgnoringLocalCacheData) } @discardableResult diff --git a/Sources/Subscription/Services/SubscriptionService.swift b/Sources/Subscription/Services/SubscriptionService.swift index d6b94cb25..e29e2cb86 100644 --- a/Sources/Subscription/Services/SubscriptionService.swift +++ b/Sources/Subscription/Services/SubscriptionService.swift @@ -35,7 +35,8 @@ public final class SubscriptionService: APIService { } } - private static let subscriptionCache = UserDefaultsCache(key: UserDefaultsCacheKey.subscription) + private static let subscriptionCache = UserDefaultsCache(key: UserDefaultsCacheKey.subscription, + settings: UserDefaultsCacheSettings(defaultExpirationInterval: .minutes(20))) public enum CachePolicy { case reloadIgnoringLocalCacheData @@ -55,7 +56,9 @@ public final class SubscriptionService: APIService { switch result { case .success(let subscriptionResponse): - subscriptionCache.set(subscriptionResponse) + let defaultExpiryDate = Date().addingTimeInterval(subscriptionCache.settings.defaultExpirationInterval) + let expiryDate = min(defaultExpiryDate, subscriptionResponse.expiresOrRenewsAt) + subscriptionCache.set(subscriptionResponse, expires: expiryDate) return .success(subscriptionResponse) case .failure(let error): return .failure(.apiError(error)) diff --git a/Sources/Subscription/UserDefaultsCache.swift b/Sources/Subscription/UserDefaultsCache.swift index ffeee6ffc..e6896cb98 100644 --- a/Sources/Subscription/UserDefaultsCache.swift +++ b/Sources/Subscription/UserDefaultsCache.swift @@ -20,13 +20,7 @@ import Foundation import Common public struct UserDefaultsCacheSettings { - - // Default expiration interval set to 24 hours public let defaultExpirationInterval: TimeInterval - - public init(defaultExpirationInterval: TimeInterval = 1 * 60 * 60) { - self.defaultExpirationInterval = defaultExpirationInterval - } } public enum UserDefaultsCacheKey: String { @@ -43,7 +37,7 @@ public class UserDefaultsCache { } private var subscriptionAppGroup: String? - private var settings: UserDefaultsCacheSettings + public private(set) var settings: UserDefaultsCacheSettings private lazy var userDefaults: UserDefaults? = { if let appGroup = subscriptionAppGroup { return UserDefaults(suiteName: appGroup) @@ -55,14 +49,15 @@ public class UserDefaultsCache { private let key: UserDefaultsCacheKey public init(subscriptionAppGroup: String? = nil, key: UserDefaultsCacheKey, - settings: UserDefaultsCacheSettings = UserDefaultsCacheSettings()) { + settings: UserDefaultsCacheSettings) { self.subscriptionAppGroup = subscriptionAppGroup self.key = key self.settings = settings } - public func set(_ object: ObjectType, expires: Date = Date().addingTimeInterval(UserDefaultsCacheSettings().defaultExpirationInterval)) { - let cacheObject = CacheObject(expires: expires, object: object) + public func set(_ object: ObjectType, expires: Date? = nil) { + let expiryDate = expires ?? Date().addingTimeInterval(self.settings.defaultExpirationInterval) + let cacheObject = CacheObject(expires: expiryDate, object: object) let encoder = JSONEncoder() do { let data = try encoder.encode(cacheObject)