@ -26,16 +26,17 @@ class VersionUpdateHandler: NSObject {
private var applicationVersion : String !
private var applicationVersion : String !
private var applicationBundleID : String = Bundle . main . bundleIdentifier ? ? " N/A "
private var applicationBundleID : String = Bundle . main . bundleIdentifier ? ? " N/A "
private var updatePriority = VersionUpdateHandlerPriority . defaultPri
private var updatePriority = VersionUpdateHandlerPriority . defaultPri
private var useAllAvailableLanguages : Bool = true
public var useAllAvailableLanguages : Bool = true
private var onlyPromptIfMainWindowIsAvailable : Bool = true
private var onlyPromptIfMainWindowIsAvailable : Bool = true
private var checkAtLaunch : Bool = true
private var checkAtLaunch : Bool = true
private var checkPeriod : Float = 0.0
private var checkPeriod : Double = 0.0
private var remindPeriod : Float = 1.0
private var remindPeriod : Double = 1.0
private var verboseLogging : Bool = true
private var verboseLogging : Bool = true
private var updateURL : URL !
private var updateURL : URL !
private var checkingForNewVersion : Bool = false
private var checkingForNewVersion : Bool = false
private var remoteVersionsDict : [ String : Any ] = [ : ]
private var remoteVersionsDict : [ String : Any ] = [ : ]
private var downloadError : Error ?
private var downloadError : Error ?
private var dataTask : URLSessionDataTask ? = . none
private var showOnFirstLaunch : Bool = false
private var showOnFirstLaunch : Bool = false
public var previewMode : Bool = false
public var previewMode : Bool = false
@ -59,8 +60,20 @@ class VersionUpdateHandler: NSObject {
applicationVersion = appVersion ? ? " N/A "
applicationVersion = appVersion ? ? " N/A "
// B u n d l e I d e n t i f i e r
// B u n d l e I d e n t i f i e r
self . applicationBundleID = Bundle . main . bundleIdentifier ? ? " com.abhishek.Clocker "
// d e f a u l t s e t t i n g s
self . updatePriority = . defaultPri ;
self . useAllAvailableLanguages = true ;
self . onlyPromptIfMainWindowIsAvailable = true ;
self . checkAtLaunch = true ;
self . checkPeriod = 0.0 ;
self . remindPeriod = 1.0 ;
self . verboseLogging = true ;
super . init ( )
super . init ( )
applicationLaunched ( )
}
}
private func inThisVersionTitle ( ) -> String {
private func inThisVersionTitle ( ) -> String {
@ -108,11 +121,11 @@ class VersionUpdateHandler: NSObject {
return UserDefaults . standard . integer ( forKey : VersionUpdateHandler . kMacAppStoreIDKey )
return UserDefaults . standard . integer ( forKey : VersionUpdateHandler . kMacAppStoreIDKey )
}
}
func setAppStoreID ( _ appStoreID : Int ) {
@objc func setAppStoreID ( _ appStoreID : Int ) {
UserDefaults . standard . set ( appStoreID , forKey : VersionUpdateHandler . kMacAppStoreIDKey )
UserDefaults . standard . set ( appStoreID , forKey : VersionUpdateHandler . kMacAppStoreIDKey )
}
}
private func setLastChecked ( _ date : Date ) {
@objc private func setLastChecked ( _ date : Date ) {
UserDefaults . standard . set ( date , forKey : VersionUpdateHandler . kVersionLastCheckedKey )
UserDefaults . standard . set ( date , forKey : VersionUpdateHandler . kVersionLastCheckedKey )
}
}
@ -191,12 +204,137 @@ class VersionUpdateHandler: NSObject {
}
}
private func shouldCheckForNewVersion ( ) -> Bool {
private func shouldCheckForNewVersion ( ) -> Bool {
if ( ! self . previewMode ) {
if let lastRemindedDate = lastReminded ( ) {
// R e m i n d e r t a k e s p r i o r i t y o v e r c h e c k p e r i o d
if Date ( ) . timeIntervalSince ( lastRemindedDate ) < Double ( remindPeriod * Self . kSecondsInDay ) {
if verboseLogging {
Logger . info ( " iVersion did not check for a new version because the user last asked to be reminded less than \( self . remindPeriod ) days ago " )
}
return false
}
} else if let lastCheckedDate = lastChecked ( ) , Date ( ) . timeIntervalSince ( lastCheckedDate ) < Double ( self . checkPeriod * Self . kSecondsInDay ) {
if ( self . verboseLogging ) {
Logger . info ( " iVersion did not check for a new version because the last check was less than \( self . checkPeriod ) days ago " )
}
return false
}
} else if ( self . verboseLogging ) {
Logger . info ( " iVersion debug mode is enabled - make sure you disable this for release " )
}
// p e r f o r m t h e c h e c k
return true
return true
}
}
private func checkForNewVersionInBackground ( ) {
var newerVersionAvailable = false
var osVersionSupported = false
var latestVersion : String ? = nil
var versions : [ String : String ] ? = nil
var itunesServiceURL = " http://itunes.apple.com/ \( self . appStoreCountry ? ? " us " ) /lookup "
if let appStoreID = appStoreID ( ) , appStoreID != 0 {
Logger . info ( " --- App Store ID is \( appStoreID ) " )
itunesServiceURL = itunesServiceURL . appendingFormat ( " ?id=%@ " , appStoreID )
} else {
itunesServiceURL = itunesServiceURL . appendingFormat ( " ?bundleId=%@ " , self . applicationBundleID )
}
if ( verboseLogging ) {
Logger . info ( " iVersion is checking \( itunesServiceURL ) for a new app version... " )
}
dataTask = NetworkManager . task ( with : itunesServiceURL ) { [ weak self ] response , error in
guard let self , let data = response else { return }
if ( error != nil || response = = nil ) {
Logger . info ( " Response is nil or error is non-nil " )
}
let json = try ? JSONSerialization . jsonObject ( with : data , options : . mutableContainers )
if let unwrapped = json as ? [ String : Any ] ,
let results = unwrapped [ " results " ] as ? Array < Any > ,
let firstResult = results . first as ? [ String : Any ] ,
let bundleID = firstResult [ " bundleId " ] as ? String {
if ( bundleID = = self . applicationBundleID ) {
guard let minimumSupportedOSVersion = firstResult [ " minimumOsVersion " ] as ? String else { return }
let version = ProcessInfo . processInfo . operatingSystemVersion
let systemVersion = " \( version . majorVersion ) . \( version . minorVersion ) . \( version . patchVersion ) "
osVersionSupported = systemVersion . compareVersion ( minimumSupportedOSVersion ) != ComparisonResult . orderedAscending
if ( ! osVersionSupported ) {
Logger . info ( " Current OS version is not supported " )
}
// g e t v e r s i o n d e t a i l s
let releaseNotes = firstResult [ " releaseNotes " ]
latestVersion = firstResult [ " version " ] as ? String
if let version = latestVersion , osVersionSupported {
versions = [ version : ( releaseNotes as ? String ) ? ? " " ]
}
// G e t a p p I D
if ( appStoreID ( ) = = nil ) {
let appStoreIDString = firstResult [ " trackId " ]
performSelector ( onMainThread : #selector ( setAppStoreID ( _ : ) ) ,
with : appStoreIDString ,
waitUntilDone : true )
if ( verboseLogging ) {
Logger . info ( " iVersion found the app on iTunes. The App Store ID is \( appStoreIDString ? ? " " ) " )
}
}
newerVersionAvailable = latestVersion ? . compareVersion ( self . applicationVersion ) = = . orderedDescending
if ( verboseLogging ) {
if ( newerVersionAvailable ) {
Logger . info ( " iVersion found a new version \( latestVersion ? ? " N/A " ) of the app on iTunes. Current version is \( self . applicationVersion ? ? " nil " ) " )
} else {
Logger . info ( " iVersion did not find a new version of the app on iTunes. Current version is \( self . applicationVersion ? ? " nil " ) and the latest version is \( latestVersion ? ? " nil " ) " )
}
}
} else {
if ( verboseLogging ) {
Logger . info ( " iVersion found that the application bundle ID \( self . applicationBundleID ) does not match the bundle ID of the app found on iTunes \( bundleID ) with the specified App Store ID \( self . appStoreID ( ) ? ? 0 ) " )
}
}
} else {
Logger . info ( " Server returned an error while fetching version info " )
}
// TODO: S e t d o w n l o a d e r r o r
performSelector ( onMainThread : #selector ( setRemoteVersionsDict ( _ : ) ) , with : versions , waitUntilDone : true )
performSelector ( onMainThread : #selector ( setLastChecked ( _ : ) ) , with : Date ( ) , waitUntilDone : true )
performSelector ( onMainThread : #selector ( Self . downloadVersionsData ) , with : nil , waitUntilDone : true )
}
dataTask ? . resume ( )
}
@objc private func setRemoteVersionsDict ( _ dict : [ String : Any ] ? ) {
if let unwrappedDict = dict {
remoteVersionsDict = unwrappedDict
}
}
private func checkForNewVersion ( ) {
if ( ! self . checkingForNewVersion ) {
self . checkingForNewVersion = true
DispatchQueue . global ( qos : . userInitiated ) . async {
self . checkForNewVersionInBackground ( )
}
}
}
private func applicationLaunched ( ) {
private func applicationLaunched ( ) {
if checkAtLaunch {
if checkAtLaunch {
checkIfNewVersion ( )
checkIfNewVersion ( )
if ( shouldCheckForNewVersion ( ) ) {
checkForNewVersion ( )
}
} else if verboseLogging {
} else if verboseLogging {
Logger . info ( " iVersion will not check for updatess because checkAtLaunch option is disabled " )
Logger . info ( " iVersion will not check for updatess because checkAtLaunch option is disabled " )
}
}
@ -291,7 +429,7 @@ class VersionUpdateHandler: NSObject {
// G e t B u t t o n I n d i c e
// G e t B u t t o n I n d i c e
}
}
private func downloadVersionsData ( ) {
@objc private func downloadVersionsData ( ) {
if onlyPromptIfMainWindowIsAvailable {
if onlyPromptIfMainWindowIsAvailable {
guard NSApplication . shared . mainWindow != nil else {
guard NSApplication . shared . mainWindow != nil else {
return
return