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.
		
		
		
		
		
			
		
			
				
	
	
		
			244 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			244 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Swift
		
	
| //
 | |
| //  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| 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() {
 | |
|         return
 | |
|         
 | |
|         /*
 | |
|         guard let currentVersion = self.currentVersion else {
 | |
|             owsFailDebug("currentVersion was unexpectedly nil")
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         guard let bundleIdentifier = self.bundleIdentifier else {
 | |
|             owsFailDebug("bundleIdentifier was unexpectedly nil")
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         guard let lookupURL = lookupURL(bundleIdentifier: bundleIdentifier) else {
 | |
|             owsFailDebug("appStoreURL was unexpectedly nil")
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         firstly {
 | |
|             self.versionService.fetchLatestVersion(lookupURL: lookupURL)
 | |
|         }.done { appStoreRecord in
 | |
|             guard appStoreRecord.version.compare(currentVersion, options: .numeric) == ComparisonResult.orderedDescending else {
 | |
|                 Logger.debug("remote version: \(appStoreRecord) is not newer than currentVersion: \(currentVersion)")
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             Logger.info("new version available: \(appStoreRecord)")
 | |
|             self.showUpdateNagIfEnoughTimeHasPassed(appStoreRecord: appStoreRecord)
 | |
|         }.catch { error in
 | |
|             Logger.error("failed with error: \(error)")
 | |
|         }.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 {
 | |
|             Logger.info("firstHeardOfNewVersionDate: \(firstHeardOfNewVersionDate) not nagging for new release yet.")
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         if let lastNagDate = self.lastNagDate {
 | |
|             let intervalBetweenNags = 14 * kDayInterval
 | |
|             guard Date() > Date.init(timeInterval: intervalBetweenNags, since: lastNagDate) else {
 | |
|                 Logger.info("lastNagDate: \(lastNagDate) not nagging again so soon.")
 | |
|                 return
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Only show nag if we are "at rest" in the home view or registration view without any
 | |
|         // alerts or dialogs showing.
 | |
|         guard UIApplication.shared.frontmostViewController != nil else {
 | |
|             owsFailDebug("frontmostViewController was unexpectedly nil")
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|         switch frontmostViewController {
 | |
|         case is OnboardingSplashViewController:
 | |
|             self.setLastNagDate(Date())
 | |
|             self.clearFirstHeardOfNewVersionDate()
 | |
|             presentUpgradeNag(appStoreRecord: appStoreRecord)
 | |
|         default:
 | |
|             Logger.debug("not presenting alert due to frontmostViewController: \(frontmostViewController)")
 | |
|             break
 | |
|         }
 | |
|          */
 | |
|     }
 | |
| 
 | |
|     func presentUpgradeNag(appStoreRecord: AppStoreRecord) {
 | |
|         let title = NSLocalizedString("APP_UPDATE_NAG_ALERT_TITLE", comment: "Title for the 'new app version available' alert.")
 | |
| 
 | |
|         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.")
 | |
| 
 | |
|         let alert = UIAlertController(title: title, message: bodyText, preferredStyle: .alert)
 | |
| 
 | |
|         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)
 | |
|     }
 | |
| 
 | |
|     func showAppStore(appStoreURL: URL) {
 | |
|         Logger.debug("")
 | |
|         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> {
 | |
|         Logger.debug("lookupURL:\(lookupURL)")
 | |
| 
 | |
|         let (promise, resolver) = Promise<AppStoreRecord>.pending()
 | |
| 
 | |
|         let task = URLSession.ephemeral.dataTask(with: lookupURL) { (data, _, error) in
 | |
|             guard let data = data else {
 | |
|                 Logger.warn("data was unexpectedly nil")
 | |
|                 resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             do {
 | |
|                 let decoder = JSONDecoder()
 | |
|                 let resultSet = try decoder.decode(AppStoreLookupResultSet.self, from: data)
 | |
|                 guard let appStoreRecord = resultSet.results.first else {
 | |
|                     Logger.warn("record was unexpectedly nil")
 | |
|                     resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
 | |
|                     return
 | |
|                 }
 | |
| 
 | |
|                 resolver.fulfill(appStoreRecord)
 | |
|             } catch {
 | |
|                 resolver.reject(error)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         task.resume()
 | |
| 
 | |
|         return promise
 | |
|     }
 | |
| }
 | |
| 
 | |
| extension URLSession {
 | |
|     static var ephemeral: URLSession {
 | |
|         return URLSession(configuration: .ephemeral)
 | |
|     }
 | |
| }
 |