AppExpiry should take database reference as needed

This change should have no user impact.
This commit is contained in:
Evan Hahn 2023-04-19 10:14:03 -05:00 committed by GitHub
parent f06e6c4fe8
commit e5340f6d56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 48 deletions

View File

@ -112,7 +112,10 @@ public class AppSetup {
let modelReadCaches = ModelReadCaches(factory: ModelReadCacheFactory())
let earlyMessageManager = EarlyMessageManager()
let messagePipelineSupervisor = MessagePipelineSupervisor.createStandardSupervisor()
let appExpiry = AppExpiry()
let appExpiry = AppExpiry(
keyValueStoreFactory: SDSKeyValueStoreFactory(),
schedulers: DispatchQueueSchedulers()
)
let paymentsHelper = PaymentsHelperImpl()
let paymentsCurrencies = PaymentsCurrenciesImpl()
let spamChallengeResolver = SpamChallengeResolver()

View File

@ -43,7 +43,7 @@ extension HTTPUtils {
}
if httpError.responseStatusCode == AppExpiry.appExpiredStatusCode {
appExpiry.setHasAppExpiredAtCurrentVersion()
appExpiry.setHasAppExpiredAtCurrentVersion(db: DependenciesBridge.shared.db)
}
}

View File

@ -472,7 +472,7 @@ public class OWSURLSession: NSObject, OWSURLSessionProtocol {
return
}
AppExpiry.shared.setHasAppExpiredAtCurrentVersion()
AppExpiry.shared.setHasAppExpiredAtCurrentVersion(db: DependenciesBridge.shared.db)
}
// MARK: Request building

View File

@ -469,7 +469,7 @@ public class OWSWebSocket: NSObject {
// The websocket is only used to connect to the main signal
// service, so we need to check for remote deprecation.
if responseStatus == AppExpiry.appExpiredStatusCode {
appExpiry.setHasAppExpiredAtCurrentVersion()
appExpiry.setHasAppExpiredAtCurrentVersion(db: DependenciesBridge.shared.db)
}
let headers = OWSHttpHeaders()
@ -1186,7 +1186,7 @@ extension OWSWebSocket {
},
failure: { (failure: OWSHTTPErrorWrapper) in
if failure.error.responseStatusCode == AppExpiry.appExpiredStatusCode {
Self.appExpiry.setHasAppExpiredAtCurrentVersion()
Self.appExpiry.setHasAppExpiredAtCurrentVersion(db: DependenciesBridge.shared.db)
}
failureParam(failure)

View File

@ -243,6 +243,11 @@ public class SSKEnvironment: NSObject {
warmCachesForObject("paymentsCurrencies", paymentsCurrencies.warmCaches)
warmCachesForObject("storyManager", StoryManager.setup)
warmCachesForObject("deviceManager", DependenciesBridge.shared.deviceManager.warmCaches)
warmCachesForObject("appExpiry") {
DependenciesBridge.shared.db.read { tx in
self.appExpiry.warmCaches(with: tx)
}
}
NotificationCenter.default.post(name: SSKEnvironment.warmCachesNotification, object: nil)
}

View File

@ -109,7 +109,10 @@ public class MockSSKEnvironment: SSKEnvironment {
let modelReadCaches = ModelReadCaches(factory: TestableModelReadCacheFactory())
let earlyMessageManager = EarlyMessageManager()
let messagePipelineSupervisor = MessagePipelineSupervisor.createStandardSupervisor()
let appExpiry = AppExpiry()
let appExpiry = AppExpiry(
keyValueStoreFactory: SDSKeyValueStoreFactory(),
schedulers: DispatchQueueSchedulers()
)
let paymentsHelper = MockPaymentsHelper()
let paymentsCurrencies = MockPaymentsCurrencies()
let paymentsEvents = PaymentsEventsNoop()

View File

@ -10,8 +10,8 @@ public class AppExpiry: NSObject {
@objc
public static let appExpiredStatusCode: UInt = 499
@objc
public let keyValueStore = SDSKeyValueStore(collection: "AppExpiry")
private let keyValueStore: KeyValueStore
private let schedulers: Schedulers
private struct ExpirationState: Codable, Equatable {
let version4: String
@ -37,18 +37,20 @@ public class AppExpiry: NSObject {
}
}
private let expirationState = AtomicValue<ExpirationState>(.init(mode: .default))
private static var expirationStateKey: String { "expirationState" }
@objc
public required override init() {
static let keyValueCollection = "AppExpiry"
static let keyValueKey = "expirationState"
public required init(
keyValueStoreFactory: KeyValueStoreFactory,
schedulers: Schedulers
) {
self.keyValueStore = keyValueStoreFactory.keyValueStore(collection: Self.keyValueCollection)
self.schedulers = schedulers
super.init()
SwiftSingletons.register(self)
AppReadiness.runNowOrWhenAppWillBecomeReady {
// We don't need to re-warm this cache after a device migration.
self.warmCaches()
}
}
private func logExpirationState() {
@ -61,41 +63,40 @@ public class AppExpiry: NSObject {
}
}
private func warmCaches() {
public func warmCaches(with tx: DBReadTransaction) {
owsAssertDebug(GRDBSchemaMigrator.areMigrationsComplete)
if let persistedExpirationState: ExpirationState = databaseStorage.read(block: { transaction in
guard let persistedExpirationState: ExpirationState = try? self.keyValueStore.getCodableValue(
forKey: Self.expirationStateKey,
transaction: transaction
) else {
return nil
}
let persistedExpirationState: ExpirationState? = try? self.keyValueStore.getCodableValue(
forKey: Self.keyValueKey,
transaction: tx
)
// We only want to restore the persisted state if it's for our current version.
guard persistedExpirationState.version4 == AppVersion.shared.currentAppVersion4 else {
return nil
}
return persistedExpirationState
}) {
expirationState.set(persistedExpirationState)
// We only want to restore the persisted state if it's for our current version.
guard
let persistedExpirationState,
persistedExpirationState.version4 == AppVersion.shared.currentAppVersion4
else {
return
}
expirationState.set(persistedExpirationState)
logExpirationState()
}
private func updateExpirationState(_ state: ExpirationState) {
private func updateExpirationState(_ state: ExpirationState, db: DB) {
expirationState.set(state)
logExpirationState()
databaseStorage.asyncWrite { transaction in
db.asyncWrite { transaction in
do {
// Don't write or fire notification if the value hasn't changed.
if let oldState: ExpirationState = try self.keyValueStore.getCodableValue(forKey: Self.expirationStateKey,
transaction: transaction),
oldState == state {
let oldState: ExpirationState? = try self.keyValueStore.getCodableValue(
forKey: Self.keyValueKey,
transaction: transaction
)
if let oldState, oldState == state {
return
}
} catch {
@ -104,13 +105,14 @@ public class AppExpiry: NSObject {
do {
try self.keyValueStore.setCodable(
state,
key: Self.expirationStateKey,
key: Self.keyValueKey,
transaction: transaction
)
} catch {
owsFailDebug("Error persisting expiration state \(error)")
}
transaction.addAsyncCompletionOffMain {
transaction.addAsyncCompletion(on: self.schedulers.global()) {
NotificationCenter.default.postNotificationNameAsync(
Self.AppExpiryDidChange,
object: nil
@ -119,28 +121,28 @@ public class AppExpiry: NSObject {
}
}
@objc
public func setHasAppExpiredAtCurrentVersion() {
public func setHasAppExpiredAtCurrentVersion(db: DB) {
Logger.warn("")
updateExpirationState(ExpirationState(mode: .immediately))
updateExpirationState(ExpirationState(mode: .immediately), db: db)
}
@objc
public func setExpirationDateForCurrentVersion(_ newExpirationDate: Date?) {
public func setExpirationDateForCurrentVersion(_ newExpirationDate: Date?, db: DB) {
guard !isExpired else {
return owsFailDebug("Ignoring expiration date change for expired build.")
}
Logger.warn("\(String(describing: newExpirationDate))")
let newState: ExpirationState
if let newExpirationDate = newExpirationDate {
// Ignore any expiration date that is later than when the app expires by default.
guard newExpirationDate < AppVersion.shared.defaultExpirationDate else { return }
updateExpirationState(ExpirationState(mode: .atDate, expirationDate: newExpirationDate))
newState = .init(mode: .atDate, expirationDate: newExpirationDate)
} else {
updateExpirationState(ExpirationState(mode: .default))
newState = .init(mode: .default)
}
updateExpirationState(newState, db: db)
}
@objc

View File

@ -854,12 +854,13 @@ private extension ServiceRemoteConfigManager {
if let minimumVersions = minimumVersions {
Logger.info("Minimum client versions: \(minimumVersions)")
let db = DependenciesBridge.shared.db
if let remoteExpirationDate = remoteExpirationDate(minimumVersions: minimumVersions) {
Logger.info("Setting client expiration date: \(remoteExpirationDate)")
AppExpiry.shared.setExpirationDateForCurrentVersion(remoteExpirationDate)
AppExpiry.shared.setExpirationDateForCurrentVersion(remoteExpirationDate, db: db)
} else {
Logger.info("Clearing client expiration date")
AppExpiry.shared.setExpirationDateForCurrentVersion(nil)
AppExpiry.shared.setExpirationDateForCurrentVersion(nil, db: db)
}
}
}