// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. // // stringlint:disable import UIKit // MARK: - Cache public extension Cache { static let appVersion: CacheConfig = Dependencies.create( identifier: "appVersion", createInstance: { dependencies in AppVersion(using: dependencies) }, mutableInstance: { $0 }, immutableInstance: { $0 } ) } // MARK: - AppVersion public class AppVersion: AppVersionCacheType { private let dependencies: Dependencies public let isValid: Bool public let appVersion: String public let buildNumber: String public let commitHash: String public let libSessionVersion: String public var firstAppVersion: String? public var lastAppVersion: String? public var lastCompletedLaunchAppVersion: String? public var lastCompletedLaunchMainAppVersion: String? public var lastCompletedLaunchSAEAppVersion: String? public var isFirstLaunch: Bool { self.firstAppVersion != nil } public var didJustUpdate: Bool { (lastAppVersion?.count ?? 0) > 0 && lastAppVersion != appVersion } public var versionInfo: String { return [ "iOS \(UIDevice.current.systemVersion)", [ "App: \(appVersion)", [buildNumber.nullIfEmpty, commitHash.nullIfEmpty] .compactMap { $0 } .joined(separator: " - ") .nullIfEmpty .map { "(\($0))" } ].compactMap { $0 }.joined(separator: " "), "libSession: \(LibSession.version)" ].joined(separator: ", ") } // MARK: - Initialization fileprivate init(using dependencies: Dependencies) { self.dependencies = dependencies guard let appVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { self.isValid = false self.appVersion = "" self.buildNumber = "" self.commitHash = "" self.libSessionVersion = "" self.firstAppVersion = nil self.lastAppVersion = nil self.lastCompletedLaunchAppVersion = nil self.lastCompletedLaunchMainAppVersion = nil self.lastCompletedLaunchSAEAppVersion = nil return } let oldFirstAppVersion: String? = dependencies[defaults: .appGroup, key: .firstAppVersion] self.isValid = true self.appVersion = appVersion self.buildNumber = (Bundle.main.infoDictionary?["CFBundleVersion"] as? String).defaulting(to: "") self.commitHash = (Bundle.main.infoDictionary?["GitCommitHash"] as? String).defaulting(to: "") self.libSessionVersion = LibSession.version self.firstAppVersion = dependencies[defaults: .appGroup, key: .firstAppVersion] .defaulting(to: appVersion) self.lastAppVersion = dependencies[defaults: .appGroup, key: .lastAppVersion] self.lastCompletedLaunchAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchAppVersion] self.lastCompletedLaunchMainAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchMainAppVersion] self.lastCompletedLaunchSAEAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchSAEAppVersion] // Ensure the value for the "first launched version". if oldFirstAppVersion == nil { dependencies[defaults: .appGroup, key: .firstAppVersion] = appVersion } // Update the value for the "most recently launched version". dependencies[defaults: .appGroup, key: .lastAppVersion] = appVersion } // MARK: - Functions private func anyLaunchDidComplete() { lastCompletedLaunchAppVersion = appVersion // Update the value for the "most recently launch-completed version". dependencies[defaults: .appGroup, key: .lastCompletedLaunchAppVersion] = appVersion } public func mainAppLaunchDidComplete() { lastCompletedLaunchMainAppVersion = appVersion dependencies[defaults: .appGroup, key: .lastCompletedLaunchMainAppVersion] = appVersion anyLaunchDidComplete() } public func saeLaunchDidComplete() { lastCompletedLaunchSAEAppVersion = appVersion dependencies[defaults: .appGroup, key: .lastCompletedLaunchSAEAppVersion] = appVersion anyLaunchDidComplete() } } // MARK: - AppVersionCacheType /// This is a read-only version of the Cache designed to avoid unintentionally mutating the instance in a non-thread-safe way public protocol AppVersionImmutableCacheType: ImmutableCacheType { /// Flag indicating whether the version information is valid var isValid: Bool { get } /// The current app version var appVersion: String { get } /// The current build number var buildNumber: String { get } /// The commit hash for the current build var commitHash: String { get } /// The current `libSession` version var libSessionVersion: String { get } /// The version of the app when it was first launched (`nil` if the app has never been launched before) var firstAppVersion: String? { get } /// The version of the app the last time it was launched (`nil` if the app has never been launched before) var lastAppVersion: String? { get } /// The last app version where is successfully launched the main app var lastCompletedLaunchAppVersion: String? { get } /// The last app version where is successfully launched the main app var lastCompletedLaunchMainAppVersion: String? { get } /// The last app version where is successfully launched the main app var lastCompletedLaunchSAEAppVersion: String? { get } /// Flag indicating whether this is the first app launch var isFirstLaunch: Bool { get } /// Flag indicating whether the app was just updated var didJustUpdate: Bool { get } /// The full version information for the current version var versionInfo: String { get } } public protocol AppVersionCacheType: AppVersionImmutableCacheType, MutableCacheType { /// Function to call when the main app successfully completed a launch func mainAppLaunchDidComplete() /// Function to call when the share extension successfully completed a launch func saeLaunchDidComplete() } // MARK: - UserDefaults Keys private extension UserDefaults.StringKey { /// The version of the app when it was first launched static let firstAppVersion: UserDefaults.StringKey = "kNSUserDefaults_FirstAppVersion" /// The version of the app when it was last launched static let lastAppVersion: UserDefaults.StringKey = "kNSUserDefaults_LastVersion" static let lastCompletedLaunchAppVersion: UserDefaults.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion" static let lastCompletedLaunchMainAppVersion: UserDefaults.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp" static let lastCompletedLaunchSAEAppVersion: UserDefaults.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion_SAE" }