Skip to content

Commit

Permalink
Rework the caching logic for subscription and entitlements (#741)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
miasma13 authored Mar 22, 2024
1 parent 781b5f1 commit 91d9eb2
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 17 deletions.
24 changes: 19 additions & 5 deletions Sources/Subscription/AccountManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions Sources/Subscription/Services/SubscriptionService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public final class SubscriptionService: APIService {
}
}

private static let subscriptionCache = UserDefaultsCache<Subscription>(key: UserDefaultsCacheKey.subscription)
private static let subscriptionCache = UserDefaultsCache<Subscription>(key: UserDefaultsCacheKey.subscription,
settings: UserDefaultsCacheSettings(defaultExpirationInterval: .minutes(20)))

public enum CachePolicy {
case reloadIgnoringLocalCacheData
Expand All @@ -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))
Expand Down
15 changes: 5 additions & 10 deletions Sources/Subscription/UserDefaultsCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -43,7 +37,7 @@ public class UserDefaultsCache<ObjectType: Codable> {
}

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)
Expand All @@ -55,14 +49,15 @@ public class UserDefaultsCache<ObjectType: Codable> {
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)
Expand Down

0 comments on commit 91d9eb2

Please sign in to comment.