diff --git a/Frameworks/Account/AccountMetadata.swift b/Frameworks/Account/AccountMetadata.swift index 7c5f378f9..f4c50ab44 100644 --- a/Frameworks/Account/AccountMetadata.swift +++ b/Frameworks/Account/AccountMetadata.swift @@ -23,6 +23,7 @@ final class AccountMetadata: Codable { case lastArticleFetchStartTime = "lastArticleFetch" case lastArticleFetchEndTime case endpointURL + case lastCredentialRenewTime = "lastCredentialRenewTime" } var name: String? { @@ -80,6 +81,16 @@ final class AccountMetadata: Codable { } } } + + /// The last moment an account successfully renewed its credentials, or `nil` if no such moment exists. + /// An account delegate can use this value to decide when to next ask the service provider to renew credentials. + var lastCredentialRenewTime: Date? { + didSet { + if lastCredentialRenewTime != oldValue { + valueDidChange(.lastCredentialRenewTime) + } + } + } weak var delegate: AccountMetadataDelegate? diff --git a/Frameworks/Account/Feedly/Operations/FeedlyRefreshAccessTokenOperation.swift b/Frameworks/Account/Feedly/Operations/FeedlyRefreshAccessTokenOperation.swift index d1c68d970..d6412f202 100644 --- a/Frameworks/Account/Feedly/Operations/FeedlyRefreshAccessTokenOperation.swift +++ b/Frameworks/Account/Feedly/Operations/FeedlyRefreshAccessTokenOperation.swift @@ -17,14 +17,31 @@ final class FeedlyRefreshAccessTokenOperation: FeedlyOperation { let account: Account let log: OSLog - init(account: Account, service: OAuthAccessTokenRefreshing, oauthClient: OAuthAuthorizationClient, log: OSLog) { + /// The moment the refresh is being requested. The token will refresh only if the account's `lastCredentialRenewTime` is not on the same day as this moment. When nil, the operation will always refresh the token. + let refreshDate: Date? + + init(account: Account, service: OAuthAccessTokenRefreshing, oauthClient: OAuthAuthorizationClient, refreshDate: Date?, log: OSLog) { self.oauthClient = oauthClient self.service = service self.account = account + self.refreshDate = refreshDate self.log = log } override func run() { + // Only refresh the token if these dates are not on the same day. + let shouldRefresh: Bool = { + guard let date = refreshDate, let lastRenewDate = account.metadata.lastCredentialRenewTime else { + return true + } + return !Calendar.current.isDate(lastRenewDate, equalTo: date, toGranularity: .day) + }() + + guard shouldRefresh else { + didFinish() + return + } + let refreshToken: Credentials do { @@ -64,6 +81,8 @@ final class FeedlyRefreshAccessTokenOperation: FeedlyOperation { // Now store the access token because we want the account delegate to use it. try account.storeCredentials(grant.accessToken) + account.metadata.lastCredentialRenewTime = Date() + didFinish() } catch { didFinish(with: error)