diff --git a/Clocker/Dependencies/iVersion/iVersion.m b/Clocker/Dependencies/iVersion/iVersion.m index 9bc9600..591b5f6 100755 --- a/Clocker/Dependencies/iVersion/iVersion.m +++ b/Clocker/Dependencies/iVersion/iVersion.m @@ -82,16 +82,20 @@ static NSString *const iVersionMacAppStoreURLFormat = @"macappstore://itunes.app - (NSComparisonResult)compareVersion:(NSString *)version { - return [self compare:version options:NSNumericSearch]; + return [self compare:version options:NSNumericSearch]; } - (NSComparisonResult)compareVersionDescending:(NSString *)version { - return (NSComparisonResult)(0 - [self compareVersion:version]); + return (NSComparisonResult)(0 - [self compareVersion:version]); } @end +static NSString *mostRecentVersionInDict(NSDictionary *dictionary) +{ + return [dictionary.allKeys sortedArrayUsingSelector:@selector(compareVersion:)].lastObject; +} @interface iVersion () @@ -109,741 +113,691 @@ static NSString *const iVersionMacAppStoreURLFormat = @"macappstore://itunes.app + (void)load { - [self performSelectorOnMainThread:@selector(sharedInstance) withObject:nil waitUntilDone:NO]; + [self performSelectorOnMainThread:@selector(sharedInstance) withObject:nil waitUntilDone:NO]; } + (iVersion *)sharedInstance { - static iVersion *sharedInstance = nil; - if (sharedInstance == nil) - { - sharedInstance = [[iVersion alloc] init]; - } - return sharedInstance; + static iVersion *sharedInstance = nil; + if (sharedInstance == nil) + { + sharedInstance = [[iVersion alloc] init]; + } + return sharedInstance; } - (NSString *)localizedStringForKey:(NSString *)key withDefault:(NSString *)defaultString { - static NSBundle *bundle = nil; - if (bundle == nil) - { - NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"iVersion" ofType:@"bundle"]; - if (self.useAllAvailableLanguages) - { - bundle = [NSBundle bundleWithPath:bundlePath]; - NSString *language = [NSLocale preferredLanguages].count? [NSLocale preferredLanguages][0]: @"en"; - if (![bundle.localizations containsObject:language]) - { - language = [language componentsSeparatedByString:@"-"][0]; - } - if ([bundle.localizations containsObject:language]) - { - bundlePath = [bundle pathForResource:language ofType:@"lproj"]; - } - } - bundle = [NSBundle bundleWithPath:bundlePath] ?: [NSBundle mainBundle]; - } - defaultString = [bundle localizedStringForKey:key value:defaultString table:nil]; - return [[NSBundle mainBundle] localizedStringForKey:key value:defaultString table:nil]; + static NSBundle *bundle = nil; + if (bundle == nil) + { + NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"iVersion" ofType:@"bundle"]; + if (self.useAllAvailableLanguages) + { + bundle = [NSBundle bundleWithPath:bundlePath]; + NSString *language = [NSLocale preferredLanguages].count? [NSLocale preferredLanguages][0]: @"en"; + if (![bundle.localizations containsObject:language]) + { + language = [language componentsSeparatedByString:@"-"][0]; + } + if ([bundle.localizations containsObject:language]) + { + bundlePath = [bundle pathForResource:language ofType:@"lproj"]; + } + } + bundle = [NSBundle bundleWithPath:bundlePath] ?: [NSBundle mainBundle]; + } + defaultString = [bundle localizedStringForKey:key value:defaultString table:nil]; + return [[NSBundle mainBundle] localizedStringForKey:key value:defaultString table:nil]; } - (iVersion *)init { - if ((self = [super init])) + if ((self = [super init])) + { + //get country + self.appStoreCountry = [(NSLocale *)[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]; + if ([self.appStoreCountry isEqualToString:@"150"]) { - -#if TARGET_OS_IPHONE - - //register for iphone application events - if (&UIApplicationWillEnterForegroundNotification) - { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillEnterForeground) - name:UIApplicationWillEnterForegroundNotification - object:nil]; - } - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(didRotate) - name:UIDeviceOrientationDidChangeNotification - object:nil]; - -#endif - - //get country - self.appStoreCountry = [(NSLocale *)[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]; - if ([self.appStoreCountry isEqualToString:@"150"]) - { - self.appStoreCountry = @"eu"; - } - else if ([self.appStoreCountry stringByReplacingOccurrencesOfString:@"[A-Za-z]{2}" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, 2)].length) - { - self.appStoreCountry = @"us"; - } - - //application version (use short version preferentially) - self.applicationVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; - if ((self.applicationVersion).length == 0) - { - self.applicationVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; - } - - //bundle id - self.applicationBundleID = [NSBundle mainBundle].bundleIdentifier; - - //default settings - self.updatePriority = iVersionUpdatePriorityDefault; - self.useAllAvailableLanguages = YES; - self.onlyPromptIfMainWindowIsAvailable = YES; - self.checkAtLaunch = YES; - self.checkPeriod = 0.0f; - self.remindPeriod = 1.0f; - + self.appStoreCountry = @"eu"; + } + else if ([self.appStoreCountry stringByReplacingOccurrencesOfString:@"[A-Za-z]{2}" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, 2)].length) + { + self.appStoreCountry = @"us"; + } + + //application version (use short version preferentially) + self.applicationVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + if ((self.applicationVersion).length == 0) + { + self.applicationVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + } + + //bundle id + self.applicationBundleID = [NSBundle mainBundle].bundleIdentifier; + + //default settings + self.updatePriority = iVersionUpdatePriorityDefault; + self.useAllAvailableLanguages = YES; + self.onlyPromptIfMainWindowIsAvailable = YES; + self.checkAtLaunch = YES; + self.checkPeriod = 0.0f; + self.remindPeriod = 1.0f; + self.verboseLogging = YES; + #ifdef DEBUG - - //enable verbose logging in debug mode - self.verboseLogging = YES; - + + //enable verbose logging in debug mode + self.verboseLogging = YES; + #endif - - //app launched - [self performSelectorOnMainThread:@selector(applicationLaunched) withObject:nil waitUntilDone:NO]; - } - return self; + + //app launched + [self performSelectorOnMainThread:@selector(applicationLaunched) withObject:nil waitUntilDone:NO]; + } + return self; } - (id)delegate { - if (_delegate == nil) - { - -#if TARGET_OS_IPHONE -#define APP_CLASS UIApplication -#else -#define APP_CLASS NSApplication -#endif - - _delegate = (id)[APP_CLASS sharedApplication].delegate; - } - return _delegate; + if (_delegate == nil) + { + _delegate = (id)[NSApplication sharedApplication].delegate; + } + return _delegate; } - (NSString *)inThisVersionTitle { - return _inThisVersionTitle ?: [self localizedStringForKey:iVersionInThisVersionTitleKey withDefault:@"New in this version"]; + return _inThisVersionTitle ?: [self localizedStringForKey:iVersionInThisVersionTitleKey withDefault:@"New in this version"]; } - (NSString *)updateAvailableTitle { - return _updateAvailableTitle ?: [self localizedStringForKey:iVersionUpdateAvailableTitleKey withDefault:@"New version available"]; + return _updateAvailableTitle ?: [self localizedStringForKey:iVersionUpdateAvailableTitleKey withDefault:@"New version available"]; } - (NSString *)versionLabelFormat { - return _versionLabelFormat ?: [self localizedStringForKey:iVersionVersionLabelFormatKey withDefault:@"Version %@"]; + return _versionLabelFormat ?: [self localizedStringForKey:iVersionVersionLabelFormatKey withDefault:@"Version %@"]; } - (NSString *)okButtonLabel { - return _okButtonLabel ?: [self localizedStringForKey:iVersionOKButtonKey withDefault:@"OK"]; + return _okButtonLabel ?: [self localizedStringForKey:iVersionOKButtonKey withDefault:@"OK"]; } - (NSString *)ignoreButtonLabel { - return _ignoreButtonLabel ?: [self localizedStringForKey:iVersionIgnoreButtonKey withDefault:@"Ignore"]; + return _ignoreButtonLabel ?: [self localizedStringForKey:iVersionIgnoreButtonKey withDefault:@"Ignore"]; } - (NSString *)downloadButtonLabel { - return _downloadButtonLabel ?: [self localizedStringForKey:iVersionDownloadButtonKey withDefault:@"Download"]; + return _downloadButtonLabel ?: [self localizedStringForKey:iVersionDownloadButtonKey withDefault:@"Download"]; } - (NSString *)remindButtonLabel { - return _remindButtonLabel ?: [self localizedStringForKey:iVersionRemindButtonKey withDefault:@"Remind Me Later"]; + return _remindButtonLabel ?: [self localizedStringForKey:iVersionRemindButtonKey withDefault:@"Remind Me Later"]; } - (NSURL *)updateURL { - if (_updateURL) - { - return _updateURL; - } - - if (!self.appStoreID) - { - NSLog(@"iVersion error: No App Store ID was found for this application. If the application is not intended for App Store release then you must specify a custom updateURL."); - } - -#if TARGET_OS_IPHONE - - return [NSURL URLWithString:[NSString stringWithFormat:iVersioniOSAppStoreURLFormat, @(self.appStoreID)]]; - -#else - - return [NSURL URLWithString:[NSString stringWithFormat:iVersionMacAppStoreURLFormat, @(self.appStoreID)]]; - -#endif - + if (_updateURL) + { + return _updateURL; + } + + if (!self.appStoreID) + { + NSLog(@"iVersion error: No App Store ID was found for this application. If the application is not intended for App Store release then you must specify a custom updateURL."); + } + + return [NSURL URLWithString:[NSString stringWithFormat:iVersionMacAppStoreURLFormat, @(self.appStoreID)]]; } - (NSUInteger)appStoreID { - return [[[NSUserDefaults standardUserDefaults] objectForKey:iVersionAppStoreIDKey] unsignedIntegerValue]; + return [[[NSUserDefaults standardUserDefaults] objectForKey:iVersionAppStoreIDKey] unsignedIntegerValue]; } - (void)setAppStoreID:(NSUInteger)appStoreID { - [[NSUserDefaults standardUserDefaults] setInteger:(NSInteger)appStoreID forKey:iVersionAppStoreIDKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [[NSUserDefaults standardUserDefaults] setInteger:(NSInteger)appStoreID forKey:iVersionAppStoreIDKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; } - (NSDate *)lastChecked { - return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastCheckedKey]; + return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastCheckedKey]; } - (void)setLastChecked:(NSDate *)date { - [[NSUserDefaults standardUserDefaults] setObject:date forKey:iVersionLastCheckedKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [[NSUserDefaults standardUserDefaults] setObject:date forKey:iVersionLastCheckedKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; } - (NSDate *)lastReminded { - return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastRemindedKey]; + return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastRemindedKey]; } - (void)setLastReminded:(NSDate *)date { - [[NSUserDefaults standardUserDefaults] setObject:date forKey:iVersionLastRemindedKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [[NSUserDefaults standardUserDefaults] setObject:date forKey:iVersionLastRemindedKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; } - (NSString *)ignoredVersion { - return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionIgnoreVersionKey]; + return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionIgnoreVersionKey]; } - (void)setIgnoredVersion:(NSString *)version { - [[NSUserDefaults standardUserDefaults] setObject:version forKey:iVersionIgnoreVersionKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [[NSUserDefaults standardUserDefaults] setObject:version forKey:iVersionIgnoreVersionKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; } - (BOOL)viewedVersionDetails { - return [[[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastVersionKey] isEqualToString:self.applicationVersion]; + return [[[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastVersionKey] isEqualToString:self.applicationVersion]; } - (void)setViewedVersionDetails:(BOOL)viewed { - [[NSUserDefaults standardUserDefaults] setObject:(viewed? self.applicationVersion: nil) forKey:iVersionLastVersionKey]; + [[NSUserDefaults standardUserDefaults] setObject:(viewed? self.applicationVersion: nil) forKey:iVersionLastVersionKey]; } - - (NSString *)lastVersion { - return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastVersionKey]; + return [[NSUserDefaults standardUserDefaults] objectForKey:iVersionLastVersionKey]; } - (void)setLastVersion:(NSString *)version { - [[NSUserDefaults standardUserDefaults] setObject:version forKey:iVersionLastVersionKey]; - [[NSUserDefaults standardUserDefaults] synchronize]; + [[NSUserDefaults standardUserDefaults] setObject:version forKey:iVersionLastVersionKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; } - (NSDictionary *)localVersionsDict { - static NSDictionary *versionsDict = nil; - if (versionsDict == nil) + static NSDictionary *versionsDict = nil; + if (versionsDict == nil) + { + if (self.localVersionsPlistPath == nil) { - if (self.localVersionsPlistPath == nil) - { - versionsDict = [[NSDictionary alloc] init]; //empty dictionary - } - else - { - NSString *versionsFile = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:self.localVersionsPlistPath]; - versionsDict = [[NSDictionary alloc] initWithContentsOfFile:versionsFile]; - if (!versionsDict) - { - // Get the path to versions plist in localized directory - NSArray *pathComponents = [self.localVersionsPlistPath componentsSeparatedByString:@"."]; - versionsFile = (pathComponents.count == 2) ? [[NSBundle mainBundle] pathForResource:pathComponents[0] ofType:pathComponents[1]] : nil; - versionsDict = [[NSDictionary alloc] initWithContentsOfFile:versionsFile]; - } - } + versionsDict = [[NSDictionary alloc] init]; //empty dictionary } - return versionsDict; -} - -- (NSString *)mostRecentVersionInDict:(NSDictionary *)dict -{ - return [dict.allKeys sortedArrayUsingSelector:@selector(compareVersion:)].lastObject; + else + { + NSString *versionsFile = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:self.localVersionsPlistPath]; + versionsDict = [[NSDictionary alloc] initWithContentsOfFile:versionsFile]; + if (!versionsDict) + { + // Get the path to versions plist in localized directory + NSArray *pathComponents = [self.localVersionsPlistPath componentsSeparatedByString:@"."]; + versionsFile = (pathComponents.count == 2) ? [[NSBundle mainBundle] pathForResource:pathComponents[0] ofType:pathComponents[1]] : nil; + versionsDict = [[NSDictionary alloc] initWithContentsOfFile:versionsFile]; + } + } + } + return versionsDict; } - (NSString *)versionDetails:(NSString *)version inDict:(NSDictionary *)dict { - id versionData = dict[version]; - if ([versionData isKindOfClass:[NSString class]]) - { - return versionData; - } - else if ([versionData isKindOfClass:[NSArray class]]) - { - return [versionData componentsJoinedByString:@"\n"]; - } - return nil; + id versionData = dict[version]; + if ([versionData isKindOfClass:[NSString class]]) + { + return versionData; + } + else if ([versionData isKindOfClass:[NSArray class]]) + { + return [versionData componentsJoinedByString:@"\n"]; + } + return nil; } - (NSString *)versionDetailsSince:(NSString *)lastVersion inDict:(NSDictionary *)dict { - if (self.previewMode) - { - lastVersion = @"0"; - } - BOOL newVersionFound = NO; - NSMutableString *details = [NSMutableString string]; - NSArray *versions = [dict.allKeys sortedArrayUsingSelector:@selector(compareVersionDescending:)]; - for (NSString *version in versions) - { - if ([version compareVersion:lastVersion] == NSOrderedDescending) - { - newVersionFound = YES; - if (self.groupNotesByVersion) - { - [details appendString:[self.versionLabelFormat stringByReplacingOccurrencesOfString:@"%@" withString:version]]; - [details appendString:@"\n\n"]; - } - [details appendString:[self versionDetails:version inDict:dict] ?: @""]; - [details appendString:@"\n"]; - if (self.groupNotesByVersion) - { - [details appendString:@"\n"]; - } - } - } - return newVersionFound? [details stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]: nil; + if (self.previewMode) + { + lastVersion = @"0"; + } + BOOL newVersionFound = NO; + NSMutableString *details = [NSMutableString string]; + NSArray *versions = [dict.allKeys sortedArrayUsingSelector:@selector(compareVersionDescending:)]; + for (NSString *version in versions) + { + if ([version compareVersion:lastVersion] == NSOrderedDescending) + { + newVersionFound = YES; + if (self.groupNotesByVersion) + { + [details appendString:[self.versionLabelFormat stringByReplacingOccurrencesOfString:@"%@" withString:version]]; + [details appendString:@"\n\n"]; + } + [details appendString:[self versionDetails:version inDict:dict] ?: @""]; + [details appendString:@"\n"]; + if (self.groupNotesByVersion) + { + [details appendString:@"\n"]; + } + } + } + return newVersionFound? [details stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]: nil; } - (NSString *)versionDetails { - if (!_versionDetails) + if (!_versionDetails) + { + if (self.viewedVersionDetails) { - if (self.viewedVersionDetails) - { - self.versionDetails = [self versionDetails:self.applicationVersion inDict:[self localVersionsDict]]; - } - else - { - self.versionDetails = [self versionDetailsSince:self.lastVersion inDict:[self localVersionsDict]]; - } + self.versionDetails = [self versionDetails:self.applicationVersion inDict:[self localVersionsDict]]; + } + else + { + self.versionDetails = [self versionDetailsSince:self.lastVersion inDict:[self localVersionsDict]]; } - return _versionDetails; + } + return _versionDetails; } - (void)downloadedVersionsData { - -#if !TARGET_OS_IPHONE - - //only show when main window is available - if (self.onlyPromptIfMainWindowIsAvailable && ![NSApplication sharedApplication].mainWindow) - { - [self performSelector:@selector(downloadedVersionsData) withObject:nil afterDelay:0.5]; - return; - } - -#endif - - if (self.checkingForNewVersion) - { - //no longer checking - self.checkingForNewVersion = NO; - - //check if data downloaded - if (!self.remoteVersionsDict) - { - //log the error - if (self.downloadError) - { - NSLog(@"iVersion update check failed because: %@", (self.downloadError).localizedDescription); - } - else - { - NSLog(@"iVersion update check failed because an unknown error occured"); - } - - if ([self.delegate respondsToSelector:@selector(iVersionVersionCheckDidFailWithError:)]) - { - [self.delegate iVersionVersionCheckDidFailWithError:self.downloadError]; - } - - //deprecated code path - else if ([self.delegate respondsToSelector:@selector(iVersionVersionCheckFailed:)]) - { - NSLog(@"iVersionVersionCheckFailed: delegate method is deprecated, use iVersionVersionCheckDidFailWithError: instead"); - [self.delegate performSelector:@selector(iVersionVersionCheckFailed:) withObject:self.downloadError]; - } - return; - } - - //get version details - NSString *details = [self versionDetailsSince:self.applicationVersion inDict:self.remoteVersionsDict]; - NSString *mostRecentVersion = [self mostRecentVersionInDict:self.remoteVersionsDict]; - if (details) - { - //inform delegate of new version - if ([self.delegate respondsToSelector:@selector(iVersionDidDetectNewVersion:details:)]) - { - [self.delegate iVersionDidDetectNewVersion:mostRecentVersion details:details]; - } - - //deprecated code path - else if ([self.delegate respondsToSelector:@selector(iVersionDetectedNewVersion:details:)]) - { - NSLog(@"iVersionDetectedNewVersion:details: delegate method is deprecated, use iVersionDidDetectNewVersion:details: instead"); - [self.delegate performSelector:@selector(iVersionDetectedNewVersion:details:) withObject:mostRecentVersion withObject:details]; - } - - //check if ignored - BOOL showDetails = ![self.ignoredVersion isEqualToString:mostRecentVersion] || self.previewMode; - if (showDetails) - { - if ([self.delegate respondsToSelector:@selector(iVersionShouldDisplayNewVersion:details:)]) - { - showDetails = [self.delegate iVersionShouldDisplayNewVersion:mostRecentVersion details:details]; - if (!showDetails && self.verboseLogging) - { - NSLog(@"iVersion did not display the new version because the iVersionShouldDisplayNewVersion:details: delegate method returned NO"); - } - } - } - else if (self.verboseLogging) - { - NSLog(@"iVersion did not display the new version because it was marked as ignored"); - } - - //show details - if (showDetails && !self.visibleRemoteAlert) - { - NSString *title = self.updateAvailableTitle; - if (!self.groupNotesByVersion) - { - title = [title stringByAppendingFormat:@" (%@)", mostRecentVersion]; - } - - self.visibleRemoteAlert = [self showAlertWithTitle:title - details:details - defaultButton:self.downloadButtonLabel - ignoreButton:[self showIgnoreButton]? self.ignoreButtonLabel: nil - remindButton:[self showRemindButton]? self.remindButtonLabel: nil]; - } - } - else if ([self.delegate respondsToSelector:@selector(iVersionDidNotDetectNewVersion)]) - { - [self.delegate iVersionDidNotDetectNewVersion]; - } - } + //only show when main window is available + if (self.onlyPromptIfMainWindowIsAvailable && ![NSApplication sharedApplication].mainWindow) + { + [self performSelector:@selector(downloadedVersionsData) withObject:nil afterDelay:0.5]; + return; + } + + if (self.checkingForNewVersion) + { + //no longer checking + self.checkingForNewVersion = NO; + + //check if data downloaded + if (!self.remoteVersionsDict) + { + //log the error + if (self.downloadError) + { + NSLog(@"iVersion update check failed because: %@", (self.downloadError).localizedDescription); + } + else + { + NSLog(@"iVersion update check failed because an unknown error occured"); + } + + if ([self.delegate respondsToSelector:@selector(iVersionVersionCheckDidFailWithError:)]) + { + [self.delegate iVersionVersionCheckDidFailWithError:self.downloadError]; + } + + //deprecated code path + else if ([self.delegate respondsToSelector:@selector(iVersionVersionCheckFailed:)]) + { + NSLog(@"iVersionVersionCheckFailed: delegate method is deprecated, use iVersionVersionCheckDidFailWithError: instead"); + [self.delegate performSelector:@selector(iVersionVersionCheckFailed:) withObject:self.downloadError]; + } + return; + } + + //get version details + NSString *details = [self versionDetailsSince:self.applicationVersion inDict:self.remoteVersionsDict]; + NSString *mostRecentVersion = mostRecentVersionInDict(self.remoteVersionsDict); + if (details) + { + //inform delegate of new version + if ([self.delegate respondsToSelector:@selector(iVersionDidDetectNewVersion:details:)]) + { + [self.delegate iVersionDidDetectNewVersion:mostRecentVersion details:details]; + } + + //deprecated code path + else if ([self.delegate respondsToSelector:@selector(iVersionDetectedNewVersion:details:)]) + { + NSLog(@"iVersionDetectedNewVersion:details: delegate method is deprecated, use iVersionDidDetectNewVersion:details: instead"); + [self.delegate performSelector:@selector(iVersionDetectedNewVersion:details:) withObject:mostRecentVersion withObject:details]; + } + + //check if ignored + BOOL showDetails = ![self.ignoredVersion isEqualToString:mostRecentVersion] || self.previewMode; + if (showDetails) + { + if ([self.delegate respondsToSelector:@selector(iVersionShouldDisplayNewVersion:details:)]) + { + showDetails = [self.delegate iVersionShouldDisplayNewVersion:mostRecentVersion details:details]; + if (!showDetails && self.verboseLogging) + { + NSLog(@"iVersion did not display the new version because the iVersionShouldDisplayNewVersion:details: delegate method returned NO"); + } + } + } + else if (self.verboseLogging) + { + NSLog(@"iVersion did not display the new version because it was marked as ignored"); + } + + //show details + if (showDetails && !self.visibleRemoteAlert) + { + NSString *title = self.updateAvailableTitle; + if (!self.groupNotesByVersion) + { + title = [title stringByAppendingFormat:@" (%@)", mostRecentVersion]; + } + + self.visibleRemoteAlert = [self showAlertWithTitle:title + details:details + defaultButton:self.downloadButtonLabel + ignoreButton:[self showIgnoreButton]? self.ignoreButtonLabel: nil + remindButton:[self showRemindButton]? self.remindButtonLabel: nil]; + } + } + else if ([self.delegate respondsToSelector:@selector(iVersionDidNotDetectNewVersion)]) + { + [self.delegate iVersionDidNotDetectNewVersion]; + } + } } - (BOOL)shouldCheckForNewVersion { - //debug mode? - if (!self.previewMode) + //debug mode? + if (!self.previewMode) + { + //check if within the reminder period + if (self.lastReminded != nil) { - //check if within the reminder period - if (self.lastReminded != nil) - { - //reminder takes priority over check period - if ([[NSDate date] timeIntervalSinceDate:self.lastReminded] < self.remindPeriod * SECONDS_IN_A_DAY) - { - if (self.verboseLogging) - { - NSLog(@"iVersion did not check for a new version because the user last asked to be reminded less than %g days ago", self.remindPeriod); - } - return NO; - } - } - - //check if within the check period - else if (self.lastChecked != nil && [[NSDate date] timeIntervalSinceDate:self.lastChecked] < self.checkPeriod * SECONDS_IN_A_DAY) + //reminder takes priority over check period + if ([[NSDate date] timeIntervalSinceDate:self.lastReminded] < self.remindPeriod * SECONDS_IN_A_DAY) + { + if (self.verboseLogging) { - if (self.verboseLogging) - { - NSLog(@"iVersion did not check for a new version because the last check was less than %g days ago", self.checkPeriod); - } - return NO; + NSLog(@"iVersion did not check for a new version because the user last asked to be reminded less than %g days ago", self.remindPeriod); } + return NO; + } } - else if (self.verboseLogging) + + //check if within the check period + else if (self.lastChecked != nil && [[NSDate date] timeIntervalSinceDate:self.lastChecked] < self.checkPeriod * SECONDS_IN_A_DAY) { - NSLog(@"iVersion debug mode is enabled - make sure you disable this for release"); + if (self.verboseLogging) + { + NSLog(@"iVersion did not check for a new version because the last check was less than %g days ago", self.checkPeriod); + } + return NO; } - - //confirm with delegate - if ([self.delegate respondsToSelector:@selector(iVersionShouldCheckForNewVersion)]) + } + else if (self.verboseLogging) + { + NSLog(@"iVersion debug mode is enabled - make sure you disable this for release"); + } + + //confirm with delegate + if ([self.delegate respondsToSelector:@selector(iVersionShouldCheckForNewVersion)]) + { + BOOL shouldCheck = [self.delegate iVersionShouldCheckForNewVersion]; + if (!shouldCheck && self.verboseLogging) { - BOOL shouldCheck = [self.delegate iVersionShouldCheckForNewVersion]; - if (!shouldCheck && self.verboseLogging) - { - NSLog(@"iVersion did not check for a new version because the iVersionShouldCheckForNewVersion delegate method returned NO"); - } - return shouldCheck; + NSLog(@"iVersion did not check for a new version because the iVersionShouldCheckForNewVersion delegate method returned NO"); } - - //perform the check - return YES; + return shouldCheck; + } + + //perform the check + return YES; } - (void)setAppStoreIDOnMainThread:(NSString *)appStoreIDString { - self.appStoreID = appStoreIDString.longLongValue; + self.appStoreID = appStoreIDString.longLongValue; } - (void)checkForNewVersionInBackground { - @synchronized (self) - { - @autoreleasepool - { - __block BOOL newerVersionAvailable = NO; - __block BOOL osVersionSupported = NO; - __block NSString *latestVersion = nil; - __block NSDictionary *versions = nil; - - //first check iTunes - NSString *iTunesServiceURL = [NSString stringWithFormat:iVersionAppLookupURLFormat, self.appStoreCountry]; - if (self.appStoreID) + @synchronized (self) + { + @autoreleasepool + { + __block BOOL newerVersionAvailable = NO; + __block BOOL osVersionSupported = NO; + __block NSString *latestVersion = nil; + __block NSDictionary *versions = nil; + + //first check iTunes + NSString *iTunesServiceURL = [NSString stringWithFormat:iVersionAppLookupURLFormat, self.appStoreCountry]; + if (self.appStoreID) + { + iTunesServiceURL = [iTunesServiceURL stringByAppendingFormat:@"?id=%@", @(self.appStoreID)]; + } + else + { + iTunesServiceURL = [iTunesServiceURL stringByAppendingFormat:@"?bundleId=%@", self.applicationBundleID]; + } + + if (self.verboseLogging) + { + NSLog(@"iVersion is checking %@ for a new app version...", iTunesServiceURL); + } + + __block NSError *jsonError = nil; + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:iTunesServiceURL] + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData + timeoutInterval:REQUEST_TIMEOUT]; + + __weak typeof(self) weakSelf = self; + + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + + NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request + completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; + + __strong typeof(self) strongSelf = weakSelf; + + if (!strongSelf) { + return; + } + + if (error != nil || data == nil) { + return; + } + + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; + + if (!jsonError) { + //check bundle ID matches + NSArray *resultsArray = json[@"results"]; + + if (![resultsArray isKindOfClass:[NSArray class]]) { + return; + } + + NSDictionary *firstResult = [resultsArray firstObject]; + + if (!firstResult) { + return; + } + + NSString *bundleID = firstResult[@"bundleId"]; + + if (![bundleID isKindOfClass:[NSString class]]) { + return; + } + + if (bundleID) { + if ([bundleID isEqualToString:strongSelf.applicationBundleID]) { - iTunesServiceURL = [iTunesServiceURL stringByAppendingFormat:@"?id=%@", @(self.appStoreID)]; + //get supported OS version + NSString *minimumSupportedOSVersion = firstResult[@"minimumOsVersion"]; + + if (!minimumSupportedOSVersion || ![minimumSupportedOSVersion isKindOfClass:[NSString class]]) { + return; + } + + NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; + NSString *systemVersion = [NSString stringWithFormat:@"%zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion]; + + osVersionSupported = ([systemVersion compare:minimumSupportedOSVersion options:NSNumericSearch] != NSOrderedAscending); + if (!osVersionSupported) + { + error = [NSError errorWithDomain:iVersionErrorDomain + code:iVersionErrorOSVersionNotSupported + userInfo:@{NSLocalizedDescriptionKey: @"Current OS version is not supported."}]; + } + + //get version details + NSString *releaseNotes = firstResult[@"releaseNotes"]; + latestVersion = firstResult[@"version"]; + if (latestVersion && osVersionSupported) + { + versions = @{latestVersion: releaseNotes ?: @""}; + } + + //get app id + if (!strongSelf.appStoreID) + { + NSString *appStoreIDString = firstResult[@"trackId"]; + [strongSelf performSelectorOnMainThread:@selector(setAppStoreIDOnMainThread:) + withObject:appStoreIDString + waitUntilDone:YES]; + + if (strongSelf.verboseLogging) + { + NSLog(@"iVersion found the app on iTunes. The App Store ID is %@", appStoreIDString); + } + } + + //check for new version + newerVersionAvailable = ([latestVersion compareVersion:strongSelf.applicationVersion] == NSOrderedDescending); + if (strongSelf.verboseLogging) + { + if (newerVersionAvailable) + { + NSLog(@"iVersion found a new version (%@) of the app on iTunes. Current version is %@", latestVersion, strongSelf.applicationVersion); + } + else + { + NSLog(@"iVersion did not find a new version of the app on iTunes. Current version is %@, latest version is %@", strongSelf.applicationVersion, latestVersion); + } + } } else { - iTunesServiceURL = [iTunesServiceURL stringByAppendingFormat:@"?bundleId=%@", self.applicationBundleID]; + if (strongSelf.verboseLogging) + { + NSLog(@"iVersion found that the application bundle ID (%@) does not match the bundle ID of the app found on iTunes (%@) with the specified App Store ID (%@)", strongSelf.applicationBundleID, bundleID, @(strongSelf.appStoreID)); + } + + error = [NSError errorWithDomain:iVersionErrorDomain + code:iVersionErrorBundleIdDoesNotMatchAppStore + userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Application bundle ID does not match expected value of %@", bundleID]}]; } - - if (self.verboseLogging) + } else if (strongSelf.appStoreID || !strongSelf.remoteVersionsPlistURL) + { + if (strongSelf.verboseLogging) { - NSLog(@"iVersion is checking %@ for a new app version...", iTunesServiceURL); + NSLog(@"iVersion could not find this application on iTunes. If your app is not intended for App Store release then you must specify a remoteVersionsPlistURL. If this is the first release of your application then it's not a problem that it cannot be found on the store yet"); } - __block NSError *jsonError = nil; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:iTunesServiceURL] - cachePolicy:NSURLRequestReloadIgnoringLocalCacheData - timeoutInterval:REQUEST_TIMEOUT]; - - __weak typeof(self) weakSelf = self; - - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; - - NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request - completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; - - __strong typeof(self) strongSelf = weakSelf; - - if (!strongSelf) { - return; - } - - if (error != nil || data == nil) { - return; - } - - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; - - if (!jsonError) { - //check bundle ID matches - NSArray *resultsArray = json[@"results"]; - - if (![resultsArray isKindOfClass:[NSArray class]]) { - return; - } - - NSDictionary *firstResult = [resultsArray firstObject]; - - if (!firstResult) { - return; - } - - NSString *bundleID = firstResult[@"bundleId"]; - - if (![bundleID isKindOfClass:[NSString class]]) { - return; - } - - if (bundleID) { - if ([bundleID isEqualToString:strongSelf.applicationBundleID]) - { - //get supported OS version - NSString *minimumSupportedOSVersion = firstResult[@"minimumOsVersion"]; - - if (!minimumSupportedOSVersion || ![minimumSupportedOSVersion isKindOfClass:[NSString class]]) { - return; - } - - NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; - NSString *systemVersion = [NSString stringWithFormat:@"%zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion]; - - osVersionSupported = ([systemVersion compare:minimumSupportedOSVersion options:NSNumericSearch] != NSOrderedAscending); - if (!osVersionSupported) - { - error = [NSError errorWithDomain:iVersionErrorDomain - code:iVersionErrorOSVersionNotSupported - userInfo:@{NSLocalizedDescriptionKey: @"Current OS version is not supported."}]; - } - - //get version details - NSString *releaseNotes = firstResult[@"releaseNotes"]; - latestVersion = firstResult[@"version"]; - if (latestVersion && osVersionSupported) - { - versions = @{latestVersion: releaseNotes ?: @""}; - } - - //get app id - if (!strongSelf.appStoreID) - { - NSString *appStoreIDString = firstResult[@"trackId"]; - [strongSelf performSelectorOnMainThread:@selector(setAppStoreIDOnMainThread:) - withObject:appStoreIDString - waitUntilDone:YES]; - - if (strongSelf.verboseLogging) - { - NSLog(@"iVersion found the app on iTunes. The App Store ID is %@", appStoreIDString); - } - } - - //check for new version - newerVersionAvailable = ([latestVersion compareVersion:strongSelf.applicationVersion] == NSOrderedDescending); - if (strongSelf.verboseLogging) - { - if (newerVersionAvailable) - { - NSLog(@"iVersion found a new version (%@) of the app on iTunes. Current version is %@", latestVersion, strongSelf.applicationVersion); - } - else - { - NSLog(@"iVersion did not find a new version of the app on iTunes. Current version is %@, latest version is %@", strongSelf.applicationVersion, latestVersion); - } - } - } - else - { - if (strongSelf.verboseLogging) - { - NSLog(@"iVersion found that the application bundle ID (%@) does not match the bundle ID of the app found on iTunes (%@) with the specified App Store ID (%@)", strongSelf.applicationBundleID, bundleID, @(strongSelf.appStoreID)); - } - - error = [NSError errorWithDomain:iVersionErrorDomain - code:iVersionErrorBundleIdDoesNotMatchAppStore - userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Application bundle ID does not match expected value of %@", bundleID]}]; - } - } else if (strongSelf.appStoreID || !strongSelf.remoteVersionsPlistURL) - { - if (strongSelf.verboseLogging) - { - NSLog(@"iVersion could not find this application on iTunes. If your app is not intended for App Store release then you must specify a remoteVersionsPlistURL. If this is the first release of your application then it's not a problem that it cannot be found on the store yet"); - } - - error = [NSError errorWithDomain:iVersionErrorDomain - code:iVersionErrorApplicationNotFoundOnAppStore - userInfo:@{NSLocalizedDescriptionKey: @"The application could not be found on the App Store."}]; - } - else if (!strongSelf.appStoreID && strongSelf.verboseLogging) - { - NSLog(@"iVersion could not find your app on iTunes. If your app is not yet on the store or is not intended for App Store release then don't worry about this"); - } - - - } else { - //http error - NSString *message = [NSString stringWithFormat:@"The server returned a %@ error", @([httpResponse statusCode])]; - error = [NSError errorWithDomain:@"HTTPResponseErrorDomain" - code:[httpResponse statusCode] - userInfo:@{NSLocalizedDescriptionKey: message}]; - } - - [strongSelf performSelectorOnMainThread:@selector(setDownloadError:) withObject:error waitUntilDone:YES]; - [strongSelf performSelectorOnMainThread:@selector(setRemoteVersionsDict:) withObject:versions waitUntilDone:YES]; - [strongSelf performSelectorOnMainThread:@selector(setLastChecked:) withObject:[NSDate date] waitUntilDone:YES]; - [strongSelf performSelectorOnMainThread:@selector(downloadedVersionsData) withObject:nil waitUntilDone:YES]; - - }]; - - [dataTask resume]; - } - } + error = [NSError errorWithDomain:iVersionErrorDomain + code:iVersionErrorApplicationNotFoundOnAppStore + userInfo:@{NSLocalizedDescriptionKey: @"The application could not be found on the App Store."}]; + } + else if (!strongSelf.appStoreID && strongSelf.verboseLogging) + { + NSLog(@"iVersion could not find your app on iTunes. If your app is not yet on the store or is not intended for App Store release then don't worry about this"); + } + + + } else { + //http error + NSString *message = [NSString stringWithFormat:@"The server returned a %@ error", @([httpResponse statusCode])]; + error = [NSError errorWithDomain:@"HTTPResponseErrorDomain" + code:[httpResponse statusCode] + userInfo:@{NSLocalizedDescriptionKey: message}]; + } + + [strongSelf performSelectorOnMainThread:@selector(setDownloadError:) withObject:error waitUntilDone:YES]; + [strongSelf performSelectorOnMainThread:@selector(setRemoteVersionsDict:) withObject:versions waitUntilDone:YES]; + [strongSelf performSelectorOnMainThread:@selector(setLastChecked:) withObject:[NSDate date] waitUntilDone:YES]; + [strongSelf performSelectorOnMainThread:@selector(downloadedVersionsData) withObject:nil waitUntilDone:YES]; + + }]; + + [dataTask resume]; + } + } } - (void)checkForNewVersion { - if (!self.checkingForNewVersion) - { - self.checkingForNewVersion = YES; - [self performSelectorInBackground:@selector(checkForNewVersionInBackground) - withObject:nil]; - } + if (!self.checkingForNewVersion) + { + self.checkingForNewVersion = YES; + [self performSelectorInBackground:@selector(checkForNewVersionInBackground) + withObject:nil]; + } } - (void)checkIfNewVersion { - -#if !TARGET_OS_IPHONE - - //only show when main window is available - if (self.onlyPromptIfMainWindowIsAvailable && ![NSApplication sharedApplication].mainWindow) - { - [self performSelector:@selector(checkIfNewVersion) withObject:nil afterDelay:0.5]; - return; - } - -#endif - - if (self.lastVersion != nil || self.showOnFirstLaunch || self.previewMode) - { - if ([self.applicationVersion compareVersion:self.lastVersion] == NSOrderedDescending || self.previewMode) - { - //clear reminder - self.lastReminded = nil; - - //get version details - BOOL showDetails = !!self.versionDetails; - if (showDetails && [self.delegate respondsToSelector:@selector(iVersionShouldDisplayCurrentVersionDetails:)]) - { - showDetails = [self.delegate iVersionShouldDisplayCurrentVersionDetails:self.versionDetails]; - } - - //show details - if (showDetails && !self.visibleLocalAlert && !self.visibleRemoteAlert) - { - self.visibleLocalAlert = [self showAlertWithTitle:self.inThisVersionTitle - details:self.versionDetails - defaultButton:self.okButtonLabel - ignoreButton:nil - remindButton:nil]; - } - } - } - else - { - //record this as last viewed release - self.viewedVersionDetails = YES; - } + //only show when main window is available + if (self.onlyPromptIfMainWindowIsAvailable && ![NSApplication sharedApplication].mainWindow) + { + [self performSelector:@selector(checkIfNewVersion) withObject:nil afterDelay:0.5]; + return; + } + + if (self.lastVersion != nil || self.showOnFirstLaunch || self.previewMode) + { + if ([self.applicationVersion compareVersion:self.lastVersion] == NSOrderedDescending || self.previewMode) + { + //clear reminder + self.lastReminded = nil; + + //get version details + BOOL showDetails = !!self.versionDetails; + if (showDetails && [self.delegate respondsToSelector:@selector(iVersionShouldDisplayCurrentVersionDetails:)]) + { + showDetails = [self.delegate iVersionShouldDisplayCurrentVersionDetails:self.versionDetails]; + } + + //show details + if (showDetails && !self.visibleLocalAlert && !self.visibleRemoteAlert) + { + self.visibleLocalAlert = [self showAlertWithTitle:self.inThisVersionTitle + details:self.versionDetails + defaultButton:self.okButtonLabel + ignoreButton:nil + remindButton:nil]; + } + } + } + else + { + //record this as last viewed release + self.viewedVersionDetails = YES; + } } - (BOOL)showIgnoreButton { - return (self.ignoreButtonLabel).length && self.updatePriority < iVersionUpdatePriorityMedium; + return (self.ignoreButtonLabel).length && self.updatePriority < iVersionUpdatePriorityMedium; } - (BOOL)showRemindButton { - return (self.remindButtonLabel).length && self.updatePriority < iVersionUpdatePriorityHigh; + return (self.remindButtonLabel).length && self.updatePriority < iVersionUpdatePriorityHigh; } - (id)showAlertWithTitle:(NSString *)title @@ -852,445 +806,185 @@ static NSString *const iVersionMacAppStoreURLFormat = @"macappstore://itunes.app ignoreButton:(NSString *)ignoreButton remindButton:(NSString *)remindButton { - -#if TARGET_OS_IPHONE - - UIViewController *topController = [UIApplication sharedApplication].delegate.window.rootViewController; - while (topController.presentedViewController) - { - topController = topController.presentedViewController; - } - - if ([UIAlertController class] && topController && self.useUIAlertControllerIfAvailable) - { - UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:details preferredStyle:UIAlertControllerStyleAlert]; - - //download/ok action - [alert addAction:[UIAlertAction actionWithTitle:self.downloadButtonLabel style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) { - [self didDismissAlert:alert withButtonAtIndex:0]; - }]]; - - //ignore action - if ([self showIgnoreButton]) - { - [alert addAction:[UIAlertAction actionWithTitle:self.ignoreButtonLabel style:UIAlertActionStyleCancel handler:^(__unused UIAlertAction *action) { - [self didDismissAlert:alert withButtonAtIndex:1]; - }]]; - } - - //remind action - if ([self showRemindButton]) - { - [alert addAction:[UIAlertAction actionWithTitle:self.remindButtonLabel style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) { - [self didDismissAlert:alert withButtonAtIndex:[self showIgnoreButton]? 2: 1]; - }]]; - } - - //get current view controller and present alert - [topController presentViewController:alert animated:YES completion:NULL]; - - return alert; + NSAlert *alert = [[NSAlert alloc] init]; + alert.messageText = title; + alert.informativeText = self.inThisVersionTitle; + [alert addButtonWithTitle:defaultButton]; + + NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 380.0, 15.0)]; + NSSize contentSize = scrollview.contentSize; + scrollview.borderType = NSBezelBorder; + scrollview.hasVerticalScroller = YES; + scrollview.hasHorizontalScroller = NO; + scrollview.autoresizingMask = (NSAutoresizingMaskOptions)(NSViewWidthSizable|NSViewHeightSizable); + + NSTextView *textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0.0, 0.0, contentSize.width, contentSize.height)]; + textView.minSize = NSMakeSize(0.0, contentSize.height); + textView.maxSize = NSMakeSize(FLT_MAX, FLT_MAX); + textView.verticallyResizable = YES; + textView.horizontallyResizable = NO; + textView.editable = NO; + textView.autoresizingMask = NSViewWidthSizable; + textView.textContainer.containerSize = NSMakeSize(contentSize.width, FLT_MAX); + textView.textContainer.widthTracksTextView = YES; + textView.string = details; + scrollview.documentView = textView; + [textView sizeToFit]; + + CGFloat height = MIN(200.0, [[scrollview documentView] frame].size.height) + 3.0; + scrollview.frame = NSMakeRect(0.0, 0.0, scrollview.frame.size.width, height); + alert.accessoryView = scrollview; + + if (ignoreButton) + { + [alert addButtonWithTitle:ignoreButton]; + } + + if (remindButton) + { + [alert addButtonWithTitle:remindButton]; + + + NSModalResponse modalResponse = [alert runModal]; + if (modalResponse == NSAlertFirstButtonReturn) + { + //right most button + [self didDismissAlert:alert withButtonAtIndex:0]; + } + else if (modalResponse == NSAlertSecondButtonReturn) + { + [self didDismissAlert:alert withButtonAtIndex:1]; } else { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title - message:details - delegate:(id)self - cancelButtonTitle:nil - otherButtonTitles:defaultButton, nil]; - if (ignoreButton) - { - [alert addButtonWithTitle:ignoreButton]; - alert.cancelButtonIndex = 1; - } - - if (remindButton) - { - [alert addButtonWithTitle:remindButton]; - } - - [alert show]; - - return alert; + [self didDismissAlert:alert withButtonAtIndex:2]; } + } -#else - - NSAlert *alert = [[NSAlert alloc] init]; - alert.messageText = title; - alert.informativeText = self.inThisVersionTitle; - [alert addButtonWithTitle:defaultButton]; - - NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 380.0, 15.0)]; - NSSize contentSize = scrollview.contentSize; - scrollview.borderType = NSBezelBorder; - scrollview.hasVerticalScroller = YES; - scrollview.hasHorizontalScroller = NO; - scrollview.autoresizingMask = (NSAutoresizingMaskOptions)(NSViewWidthSizable|NSViewHeightSizable); - - NSTextView *textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0.0, 0.0, contentSize.width, contentSize.height)]; - textView.minSize = NSMakeSize(0.0, contentSize.height); - textView.maxSize = NSMakeSize(FLT_MAX, FLT_MAX); - textView.verticallyResizable = YES; - textView.horizontallyResizable = NO; - textView.editable = NO; - textView.autoresizingMask = NSViewWidthSizable; - textView.textContainer.containerSize = NSMakeSize(contentSize.width, FLT_MAX); - textView.textContainer.widthTracksTextView = YES; - textView.string = details; - scrollview.documentView = textView; - [textView sizeToFit]; - - CGFloat height = MIN(200.0, [[scrollview documentView] frame].size.height) + 3.0; - scrollview.frame = NSMakeRect(0.0, 0.0, scrollview.frame.size.width, height); - alert.accessoryView = scrollview; - - if (ignoreButton) - { - [alert addButtonWithTitle:ignoreButton]; - } - - if (remindButton) - { - [alert addButtonWithTitle:remindButton]; - } - -#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9 - - if (![alert respondsToSelector:@selector(runModal:)]) - { - - NSModalResponse modalResponse = [alert runModal]; - if (modalResponse == NSAlertFirstButtonReturn) - { - //right most button - [self didDismissAlert:alert withButtonAtIndex:0]; - } - else if (modalResponse == NSAlertSecondButtonReturn) - { - [self didDismissAlert:alert withButtonAtIndex:1]; - } - else - { - [self didDismissAlert:alert withButtonAtIndex:2]; - } - } - else - -#endif - - { - NSModalResponse modalResponse = [alert runModal]; - if (modalResponse == NSAlertFirstButtonReturn) - { - //right most button - [self didDismissAlert:alert withButtonAtIndex:0]; - } - else if (modalResponse == NSAlertSecondButtonReturn) - { - [self didDismissAlert:alert withButtonAtIndex:1]; - } - else - { - [self didDismissAlert:alert withButtonAtIndex:2]; - } - - } return alert; - -#endif - -} - + } + - (void)didDismissAlert:(id)alertView withButtonAtIndex:(NSInteger)buttonIndex -{ + { //get button indices NSInteger downloadButtonIndex = 0; NSInteger ignoreButtonIndex = [self showIgnoreButton]? 1: 0; NSInteger remindButtonIndex = [self showRemindButton]? ignoreButtonIndex + 1: 0; //latest version - NSString *latestVersion = [self mostRecentVersionInDict:self.remoteVersionsDict]; + NSString *latestVersion = mostRecentVersionInDict(self.remoteVersionsDict); if (alertView == self.visibleLocalAlert) { - //record that details have been viewed - self.viewedVersionDetails = YES; - - //release alert - self.visibleLocalAlert = nil; - return; + //record that details have been viewed + self.viewedVersionDetails = YES; + + //release alert + self.visibleLocalAlert = nil; + return; } if (buttonIndex == downloadButtonIndex) { - //clear reminder - self.lastReminded = nil; - - //log event - if ([self.delegate respondsToSelector:@selector(iVersionUserDidAttemptToDownloadUpdate:)]) - { - [self.delegate iVersionUserDidAttemptToDownloadUpdate:latestVersion]; - } - - if (![self.delegate respondsToSelector:@selector(iVersionShouldOpenAppStore)] || - [self.delegate iVersionShouldOpenAppStore]) - { - //go to download page - [self openAppPageInAppStore]; - } + //clear reminder + self.lastReminded = nil; + + //log event + if ([self.delegate respondsToSelector:@selector(iVersionUserDidAttemptToDownloadUpdate:)]) + { + [self.delegate iVersionUserDidAttemptToDownloadUpdate:latestVersion]; + } + + if (![self.delegate respondsToSelector:@selector(iVersionShouldOpenAppStore)] || + [self.delegate iVersionShouldOpenAppStore]) + { + //go to download page + [self openAppPageInAppStore]; + } } else if (buttonIndex == ignoreButtonIndex) { - //ignore this version - self.ignoredVersion = latestVersion; - self.lastReminded = nil; - - //log event - if ([self.delegate respondsToSelector:@selector(iVersionUserDidIgnoreUpdate:)]) - { - [self.delegate iVersionUserDidIgnoreUpdate:latestVersion]; - } + //ignore this version + self.ignoredVersion = latestVersion; + self.lastReminded = nil; + + //log event + if ([self.delegate respondsToSelector:@selector(iVersionUserDidIgnoreUpdate:)]) + { + [self.delegate iVersionUserDidIgnoreUpdate:latestVersion]; + } } else if (buttonIndex == remindButtonIndex) { - //remind later - self.lastReminded = [NSDate date]; - - //log event - if ([self.delegate respondsToSelector:@selector(iVersionUserDidRequestReminderForUpdate:)]) - { - [self.delegate iVersionUserDidRequestReminderForUpdate:latestVersion]; - } + //remind later + self.lastReminded = [NSDate date]; + + //log event + if ([self.delegate respondsToSelector:@selector(iVersionUserDidRequestReminderForUpdate:)]) + { + [self.delegate iVersionUserDidRequestReminderForUpdate:latestVersion]; + } } - + //release alert self.visibleRemoteAlert = nil; -} - -#if TARGET_OS_IPHONE - -- (BOOL)openAppPageInAppStore -{ - if (!_updateURL && !self.appStoreID) - { - if (self.verboseLogging) - { - NSLog(@"iVersion was unable to open the App Store because the app store ID is not set."); - } - return NO; - } - -#if defined(IVERSION_USE_STOREKIT) && IVERSION_USE_STOREKIT - - if (!_updateURL && [SKStoreProductViewController class]) - { - if (self.verboseLogging) - { - NSLog(@"iVersion will attempt to open the StoreKit in-app product page using the following app store ID: %@", @(self.appStoreID)); - } - - //create store view controller - SKStoreProductViewController *productController = [[SKStoreProductViewController alloc] init]; - productController.delegate = (id)self; - - //load product details - NSDictionary *productParameters = @{SKStoreProductParameterITunesItemIdentifier: [@(_appStoreID) description]}; - [productController loadProductWithParameters:productParameters completionBlock:NULL]; - - //get root view controller - UIWindow *window = [[UIApplication sharedApplication] delegate].window; - UIViewController *rootViewController = window.rootViewController; - if (!rootViewController) - { - if (self.verboseLogging) - { - NSLog(@"iVersion couldn't find a root view controller from which to display StoreKit product page"); - } - } - else - { - while (rootViewController.presentedViewController) - { - rootViewController = rootViewController.presentedViewController; - } - - //present product view controller - [rootViewController presentViewController:productController animated:YES completion:nil]; - if ([self.delegate respondsToSelector:@selector(iVersionDidPresentStoreKitModal)]) - { - [self.delegate iVersionDidPresentStoreKitModal]; - } - return YES; - } - } - -#endif - - if (self.verboseLogging) - { - NSLog(@"iVersion will open the App Store using the following URL: %@", self.updateURL); - } - - [[UIApplication sharedApplication] openURL:self.updateURL]; - return YES; -} - -- (void)productViewControllerDidFinish:(UIViewController *)controller -{ - [controller.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; - if ([self.delegate respondsToSelector:@selector(iVersionDidDismissStoreKitModal)]) - { - [self.delegate iVersionDidDismissStoreKitModal]; - } -} - -- (void)resizeAlertView:(UIAlertView *)alertView -{ - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && - UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) && - [[UIDevice currentDevice].systemVersion floatValue] < 7.0f) - { - CGFloat max = alertView.window.bounds.size.height - alertView.frame.size.height - 10.0f; - CGFloat offset = 0.0f; - for (UIView *view in alertView.subviews) - { - CGRect frame = view.frame; - if ([view isKindOfClass:[UILabel class]]) - { - UILabel *label = (UILabel *)view; - if ([label.text isEqualToString:alertView.message]) - { - label.lineBreakMode = NSLineBreakByWordWrapping; - label.numberOfLines = 0; - label.alpha = 1.0f; - [label sizeToFit]; - offset = label.frame.size.height - frame.size.height; - frame.size.height = label.frame.size.height; - if (offset > max) - { - frame.size.height -= (offset - max); - offset = max; - } - if (offset > max - 10.0f) - { - frame.size.height -= (offset - max - 10); - frame.origin.y += (offset - max - 10) / 2.0f; - } - } - } - else if ([view isKindOfClass:[UITextView class]]) - { - view.alpha = 0.0f; - } - else if ([view isKindOfClass:[UIControl class]]) - { - frame.origin.y += offset; - } - view.frame = frame; - } - CGRect frame = alertView.frame; - frame.origin.y -= roundf(offset/2.0f); - frame.size.height += offset; - alertView.frame = frame; - } -} - -- (void)didRotate -{ - [self performSelectorOnMainThread:@selector(resizeAlertView:) withObject:self.visibleLocalAlert waitUntilDone:NO]; - [self performSelectorOnMainThread:@selector(resizeAlertView:) withObject:self.visibleRemoteAlert waitUntilDone:NO]; -} - -- (void)willPresentAlertView:(UIAlertView *)alertView -{ - [self resizeAlertView:alertView]; -} - -- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex -{ - [self didDismissAlert:alertView withButtonAtIndex:buttonIndex]; -} - -#else - -- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(__unused void *)contextInfo -{ + } + + - (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(__unused void *)contextInfo + { [self didDismissAlert:alert withButtonAtIndex:returnCode - NSAlertFirstButtonReturn]; -} - -- (void)openAppPageWhenAppStoreLaunched -{ + } + + - (void)openAppPageWhenAppStoreLaunched + { //check if app store is running for (NSRunningApplication *app in [NSWorkspace sharedWorkspace].runningApplications) { - if ([app.bundleIdentifier isEqualToString:iVersionMacAppStoreBundleID]) - { - //open app page - [[NSWorkspace sharedWorkspace] performSelector:@selector(openURL:) withObject:self.updateURL afterDelay:MAC_APP_STORE_REFRESH_DELAY]; - return; - } + if ([app.bundleIdentifier isEqualToString:iVersionMacAppStoreBundleID]) + { + //open app page + [[NSWorkspace sharedWorkspace] performSelector:@selector(openURL:) withObject:self.updateURL afterDelay:MAC_APP_STORE_REFRESH_DELAY]; + return; + } } //try again [self performSelector:@selector(openAppPageWhenAppStoreLaunched) withObject:nil afterDelay:0.0]; -} - -- (BOOL)openAppPageInAppStore -{ + } + + - (BOOL)openAppPageInAppStore + { if (!_updateURL && !self.appStoreID) { - if (self.verboseLogging) - { - NSLog(@"iVersion was unable to open the App Store because the app store ID is not set."); - } - return NO; + if (self.verboseLogging) + { + NSLog(@"iVersion was unable to open the App Store because the app store ID is not set."); + } + return NO; } if (self.verboseLogging) { - NSLog(@"iVersion will open the App Store using the following URL: %@", self.updateURL); + NSLog(@"iVersion will open the App Store using the following URL: %@", self.updateURL); } [[NSWorkspace sharedWorkspace] openURL:self.updateURL]; if (!_updateURL) [self openAppPageWhenAppStoreLaunched]; return YES; -} - -#endif - -- (void)applicationLaunched -{ - + } + + - (void)applicationLaunched + { if (self.checkAtLaunch) { - [self checkIfNewVersion]; - if ([self shouldCheckForNewVersion]) [self checkForNewVersion]; + [self checkIfNewVersion]; + if ([self shouldCheckForNewVersion]) [self checkForNewVersion]; } else if (self.verboseLogging) { - NSLog(@"iVersion will not check for updates because the checkAtLaunch option is disabled"); - } -} - -#if TARGET_OS_IPHONE - -- (void)applicationWillEnterForeground -{ - if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) - { - if (self.checkAtLaunch) - { - if ([self shouldCheckForNewVersion]) [self checkForNewVersion]; - } - else if (self.verboseLogging) - { - NSLog(@"iVersion will not check for updates because the checkAtLaunch option is disabled"); - } + NSLog(@"iVersion will not check for updates because the checkAtLaunch option is disabled"); } -} - -#endif - -@end + } + + @end