mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
242 lines
8.1 KiB
Swift
242 lines
8.1 KiB
Swift
7 years ago
|
//
|
||
6 years ago
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||
7 years ago
|
//
|
||
|
|
||
|
import Foundation
|
||
|
import PromiseKit
|
||
|
|
||
|
@objc
|
||
|
class AppUpdateNag: NSObject {
|
||
|
|
||
|
// MARK: Public
|
||
|
|
||
|
@objc(sharedInstance)
|
||
|
public static let shared: AppUpdateNag = {
|
||
|
let versionService = AppStoreVersionService()
|
||
|
let nagManager = AppUpdateNag(versionService: versionService)
|
||
|
return nagManager
|
||
|
}()
|
||
|
|
||
|
@objc
|
||
|
public func showAppUpgradeNagIfNecessary() {
|
||
5 years ago
|
return
|
||
|
|
||
7 years ago
|
guard let currentVersion = self.currentVersion else {
|
||
7 years ago
|
owsFailDebug("currentVersion was unexpectedly nil")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
|
guard let bundleIdentifier = self.bundleIdentifier else {
|
||
7 years ago
|
owsFailDebug("bundleIdentifier was unexpectedly nil")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
|
guard let lookupURL = lookupURL(bundleIdentifier: bundleIdentifier) else {
|
||
7 years ago
|
owsFailDebug("appStoreURL was unexpectedly nil")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
|
firstly {
|
||
|
self.versionService.fetchLatestVersion(lookupURL: lookupURL)
|
||
7 years ago
|
}.done { appStoreRecord in
|
||
7 years ago
|
guard appStoreRecord.version.compare(currentVersion, options: .numeric) == ComparisonResult.orderedDescending else {
|
||
7 years ago
|
Logger.debug("remote version: \(appStoreRecord) is not newer than currentVersion: \(currentVersion)")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
7 years ago
|
Logger.info("new version available: \(appStoreRecord)")
|
||
7 years ago
|
self.showUpdateNagIfEnoughTimeHasPassed(appStoreRecord: appStoreRecord)
|
||
|
}.catch { error in
|
||
7 years ago
|
Logger.error("failed with error: \(error)")
|
||
7 years ago
|
}.retainUntilComplete()
|
||
|
}
|
||
|
|
||
|
// MARK: - Internal
|
||
|
|
||
|
let kUpgradeNagCollection = "TSStorageManagerAppUpgradeNagCollection"
|
||
|
let kLastNagDateKey = "TSStorageManagerAppUpgradeNagDate"
|
||
|
let kFirstHeardOfNewVersionDateKey = "TSStorageManagerAppUpgradeFirstHeardOfNewVersionDate"
|
||
|
|
||
|
var dbConnection: YapDatabaseConnection {
|
||
|
return OWSPrimaryStorage.shared().dbReadWriteConnection
|
||
|
}
|
||
|
|
||
|
// MARK: Bundle accessors
|
||
|
|
||
|
var bundle: Bundle {
|
||
|
return Bundle.main
|
||
|
}
|
||
|
|
||
|
var currentVersion: String? {
|
||
|
return bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
||
|
}
|
||
|
|
||
|
var bundleIdentifier: String? {
|
||
|
return bundle.bundleIdentifier
|
||
|
}
|
||
|
|
||
|
func lookupURL(bundleIdentifier: String) -> URL? {
|
||
|
return URL(string: "https://itunes.apple.com/lookup?bundleId=\(bundleIdentifier)")
|
||
|
}
|
||
|
|
||
|
let versionService: AppStoreVersionService
|
||
|
|
||
|
required init(versionService: AppStoreVersionService) {
|
||
|
self.versionService = versionService
|
||
|
super.init()
|
||
|
|
||
|
SwiftSingletons.register(self)
|
||
|
}
|
||
|
|
||
|
func showUpdateNagIfEnoughTimeHasPassed(appStoreRecord: AppStoreRecord) {
|
||
|
guard let firstHeardOfNewVersionDate = self.firstHeardOfNewVersionDate else {
|
||
|
self.setFirstHeardOfNewVersionDate(Date())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let intervalBeforeNag = 7 * kDayInterval
|
||
|
guard Date() > Date.init(timeInterval: intervalBeforeNag, since: firstHeardOfNewVersionDate) else {
|
||
7 years ago
|
Logger.info("firstHeardOfNewVersionDate: \(firstHeardOfNewVersionDate) not nagging for new release yet.")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
|
if let lastNagDate = self.lastNagDate {
|
||
|
let intervalBetweenNags = 14 * kDayInterval
|
||
|
guard Date() > Date.init(timeInterval: intervalBetweenNags, since: lastNagDate) else {
|
||
7 years ago
|
Logger.info("lastNagDate: \(lastNagDate) not nagging again so soon.")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Only show nag if we are "at rest" in the home view or registration view without any
|
||
|
// alerts or dialogs showing.
|
||
|
guard let frontmostViewController = UIApplication.shared.frontmostViewController else {
|
||
7 years ago
|
owsFailDebug("frontmostViewController was unexpectedly nil")
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
5 years ago
|
/*
|
||
7 years ago
|
switch frontmostViewController {
|
||
5 years ago
|
case is OnboardingSplashViewController:
|
||
7 years ago
|
self.setLastNagDate(Date())
|
||
|
self.clearFirstHeardOfNewVersionDate()
|
||
|
presentUpgradeNag(appStoreRecord: appStoreRecord)
|
||
|
default:
|
||
7 years ago
|
Logger.debug("not presenting alert due to frontmostViewController: \(frontmostViewController)")
|
||
7 years ago
|
break
|
||
|
}
|
||
5 years ago
|
*/
|
||
7 years ago
|
}
|
||
|
|
||
|
func presentUpgradeNag(appStoreRecord: AppStoreRecord) {
|
||
|
let title = NSLocalizedString("APP_UPDATE_NAG_ALERT_TITLE", comment: "Title for the 'new app version available' alert.")
|
||
|
|
||
7 years ago
|
let bodyFormat = NSLocalizedString("APP_UPDATE_NAG_ALERT_MESSAGE_FORMAT", comment: "Message format for the 'new app version available' alert. Embeds: {{The latest app version number}}")
|
||
|
let bodyText = String(format: bodyFormat, appStoreRecord.version)
|
||
|
let updateButtonText = NSLocalizedString("APP_UPDATE_NAG_ALERT_UPDATE_BUTTON", comment: "Label for the 'update' button in the 'new app version available' alert.")
|
||
|
let dismissButtonText = NSLocalizedString("APP_UPDATE_NAG_ALERT_DISMISS_BUTTON", comment: "Label for the 'dismiss' button in the 'new app version available' alert.")
|
||
7 years ago
|
|
||
7 years ago
|
let alert = UIAlertController(title: title, message: bodyText, preferredStyle: .alert)
|
||
7 years ago
|
|
||
7 years ago
|
let updateAction = UIAlertAction(title: updateButtonText, style: .default) { [weak self] _ in
|
||
|
guard let strongSelf = self else {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
strongSelf.showAppStore(appStoreURL: appStoreRecord.appStoreURL)
|
||
|
}
|
||
|
|
||
|
alert.addAction(updateAction)
|
||
|
alert.addAction(UIAlertAction(title: dismissButtonText, style: .cancel, handler: nil))
|
||
|
|
||
|
OWSAlerts.showAlert(alert)
|
||
7 years ago
|
}
|
||
|
|
||
|
func showAppStore(appStoreURL: URL) {
|
||
7 years ago
|
Logger.debug("")
|
||
7 years ago
|
UIApplication.shared.openURL(appStoreURL)
|
||
|
}
|
||
|
|
||
|
// MARK: Storage
|
||
|
|
||
|
var firstHeardOfNewVersionDate: Date? {
|
||
|
return self.dbConnection.date(forKey: kFirstHeardOfNewVersionDateKey, inCollection: kUpgradeNagCollection)
|
||
|
}
|
||
|
|
||
|
func setFirstHeardOfNewVersionDate(_ date: Date) {
|
||
|
self.dbConnection.setDate(date, forKey: kFirstHeardOfNewVersionDateKey, inCollection: kUpgradeNagCollection)
|
||
|
}
|
||
|
|
||
|
func clearFirstHeardOfNewVersionDate() {
|
||
|
self.dbConnection.removeObject(forKey: kFirstHeardOfNewVersionDateKey, inCollection: kUpgradeNagCollection)
|
||
|
}
|
||
|
|
||
|
var lastNagDate: Date? {
|
||
|
return self.dbConnection.date(forKey: kLastNagDateKey, inCollection: kUpgradeNagCollection)
|
||
|
}
|
||
|
|
||
|
func setLastNagDate(_ date: Date) {
|
||
|
self.dbConnection.setDate(date, forKey: kLastNagDateKey, inCollection: kUpgradeNagCollection)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MARK: Parsing Structs
|
||
|
|
||
|
struct AppStoreLookupResultSet: Codable {
|
||
|
let resultCount: UInt
|
||
|
let results: [AppStoreRecord]
|
||
|
}
|
||
|
|
||
|
struct AppStoreRecord: Codable {
|
||
|
let appStoreURL: URL
|
||
|
let version: String
|
||
|
|
||
|
private enum CodingKeys: String, CodingKey {
|
||
|
case appStoreURL = "trackViewUrl"
|
||
|
case version
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class AppStoreVersionService: NSObject {
|
||
|
|
||
|
// MARK:
|
||
|
|
||
|
func fetchLatestVersion(lookupURL: URL) -> Promise<AppStoreRecord> {
|
||
7 years ago
|
Logger.debug("lookupURL:\(lookupURL)")
|
||
7 years ago
|
|
||
7 years ago
|
let (promise, resolver) = Promise<AppStoreRecord>.pending()
|
||
7 years ago
|
|
||
|
let task = URLSession.ephemeral.dataTask(with: lookupURL) { (data, _, error) in
|
||
|
guard let data = data else {
|
||
7 years ago
|
Logger.warn("data was unexpectedly nil")
|
||
7 years ago
|
resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
let decoder = JSONDecoder()
|
||
|
let resultSet = try decoder.decode(AppStoreLookupResultSet.self, from: data)
|
||
|
guard let appStoreRecord = resultSet.results.first else {
|
||
7 years ago
|
Logger.warn("record was unexpectedly nil")
|
||
7 years ago
|
resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||
7 years ago
|
return
|
||
|
}
|
||
|
|
||
7 years ago
|
resolver.fulfill(appStoreRecord)
|
||
7 years ago
|
} catch {
|
||
7 years ago
|
resolver.reject(error)
|
||
7 years ago
|
}
|
||
|
}
|
||
|
|
||
|
task.resume()
|
||
|
|
||
|
return promise
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension URLSession {
|
||
|
static var ephemeral: URLSession {
|
||
|
return URLSession(configuration: .ephemeral)
|
||
|
}
|
||
|
}
|