From 8cdc9899acb36f446e0c3a7fe5dbd2547b46bc2a Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Sat, 1 Oct 2011 16:14:02 -0400 Subject: [PATCH 1/5] Localize user-visible strings --- Appirater.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Appirater.h b/Appirater.h index 67f0a6bc..d0d2ff3d 100644 --- a/Appirater.h +++ b/Appirater.h @@ -51,33 +51,33 @@ extern NSString *const kAppiraterDeclinedToRate; /* Your app's name. */ -#define APPIRATER_APP_NAME [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey] +#define APPIRATER_APP_NAME [[[NSBundle mainBundle] infoDictionary] objectForKey: (NSString *) kCFBundleNameKey] /* This is the message your users will see once they've passed the day+launches threshold. */ -#define APPIRATER_MESSAGE [NSString stringWithFormat:@"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!", APPIRATER_APP_NAME] +#define APPIRATER_MESSAGE [NSString stringWithFormat: NSLocalizedString(@"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!", nil), APPIRATER_APP_NAME] /* This is the title of the message alert that users will see. */ -#define APPIRATER_MESSAGE_TITLE [NSString stringWithFormat:@"Rate %@", APPIRATER_APP_NAME] +#define APPIRATER_MESSAGE_TITLE [NSString stringWithFormat: NSLocalizedString(@"Rate %@", nil), APPIRATER_APP_NAME] /* The text of the button that rejects reviewing the app. */ -#define APPIRATER_CANCEL_BUTTON @"No, Thanks" +#define APPIRATER_CANCEL_BUTTON NSLocalizedString(@"No Thanks", nil) /* Text of button that will send user to app review page. */ -#define APPIRATER_RATE_BUTTON [NSString stringWithFormat:@"Rate %@", APPIRATER_APP_NAME] +#define APPIRATER_RATE_BUTTON [NSString stringWithFormat: NSLocalizedString(@"Rate %@", nil), APPIRATER_APP_NAME] /* Text for button to remind the user to review later. */ -#define APPIRATER_RATE_LATER @"Remind me later" +#define APPIRATER_RATE_LATER NSLocalizedString(@"Remind Me Later", nil) /* Users will need to have the same version of your app installed for this many From 22422c1b4b6ef509296911c9bacf652b41281cdb Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Sat, 1 Oct 2011 16:14:36 -0400 Subject: [PATCH 2/5] Rename interval user default keys --- Appirater.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Appirater.m b/Appirater.m index 0d2bb62d..b565ac92 100644 --- a/Appirater.m +++ b/Appirater.m @@ -38,13 +38,13 @@ #import #include -NSString *const kAppiraterFirstUseDate = @"kAppiraterFirstUseDate"; -NSString *const kAppiraterUseCount = @"kAppiraterUseCount"; -NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount"; -NSString *const kAppiraterCurrentVersion = @"kAppiraterCurrentVersion"; -NSString *const kAppiraterRatedCurrentVersion = @"kAppiraterRatedCurrentVersion"; -NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate"; -NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate"; +NSString *const kAppiraterFirstUseDate = @"Appirater.FirstUseDate"; +NSString *const kAppiraterUseCount = @"Appirater.UseCount"; +NSString *const kAppiraterSignificantEventCount = @"Appirater.SignificantEventCount"; +NSString *const kAppiraterCurrentVersion = @"Appirater.CurrentVersion"; +NSString *const kAppiraterRatedCurrentVersion = @"Appirater.RatedCurrentVersion"; +NSString *const kAppiraterDeclinedToRate = @"Appirater.DeclinedToRate"; +NSString *const kAppiraterReminderRequestDate = @"Appirater.ReminderRequestDate"; NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; From 0976202f960b2003e5f87c820531348ffcead40c Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Sat, 1 Oct 2011 16:15:30 -0400 Subject: [PATCH 3/5] Create compile-time error if APPIRATER_APP_ID is default value --- Appirater.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Appirater.m b/Appirater.m index b565ac92..1cc7dd24 100644 --- a/Appirater.m +++ b/Appirater.m @@ -48,6 +48,9 @@ NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; +#if APPIRATER_APP_ID == 301377083 + #error "APPIRATER_APP_ID was not set in \"Appirater.h\"" +#endif @interface Appirater (hidden) - (BOOL)connectedToNetwork; From fb76deddb9c67ac1770a44676dd00888a4564ffc Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Sat, 1 Oct 2011 16:17:02 -0400 Subject: [PATCH 4/5] Use class extension instead of internal category --- Appirater.m | 247 +++++++++++++++++++++++++--------------------------- 1 file changed, 121 insertions(+), 126 deletions(-) diff --git a/Appirater.m b/Appirater.m index 1cc7dd24..294df6e7 100644 --- a/Appirater.m +++ b/Appirater.m @@ -52,15 +52,132 @@ #error "APPIRATER_APP_ID was not set in \"Appirater.h\"" #endif -@interface Appirater (hidden) -- (BOOL)connectedToNetwork; +@interface Appirater () + (Appirater*)sharedInstance; -- (void)showRatingAlert; +- (BOOL)connectedToNetwork; - (BOOL)ratingConditionsHaveBeenMet; ++ (void)appWillResignActive; +- (void)hideRatingAlert; - (void)incrementUseCount; +- (void)showRatingAlert; @end -@implementation Appirater (hidden) +@implementation Appirater + +@synthesize ratingAlert; + +- (void)incrementAndRate:(NSNumber*)_canPromptForRating { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [self incrementUseCount]; + + if ([_canPromptForRating boolValue] == YES && + [self ratingConditionsHaveBeenMet] && + [self connectedToNetwork]) + { + [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; + } + + [pool release]; +} + +- (void)incrementSignificantEventAndRate:(NSNumber*)_canPromptForRating { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [self incrementSignificantEventCount]; + + if ([_canPromptForRating boolValue] == YES && + [self ratingConditionsHaveBeenMet] && + [self connectedToNetwork]) + { + [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; + } + + [pool release]; +} + ++ (void)appLaunched { + [Appirater appLaunched:YES]; +} + ++ (void)appLaunched:(BOOL)canPromptForRating { + NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; + [NSThread detachNewThreadSelector:@selector(incrementAndRate:) + toTarget:[Appirater sharedInstance] + withObject:_canPromptForRating]; + [_canPromptForRating release]; +} + +- (void)hideRatingAlert { + if (self.ratingAlert.visible) { + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER Hiding Alert"); + [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; + } +} + ++ (void)appWillResignActive { + if (APPIRATER_DEBUG) + NSLog(@"APPIRATER appWillResignActive"); + [[Appirater sharedInstance] hideRatingAlert]; +} + ++ (void)appEnteredForeground:(BOOL)canPromptForRating { + NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; + [NSThread detachNewThreadSelector:@selector(incrementAndRate:) + toTarget:[Appirater sharedInstance] + withObject:_canPromptForRating]; + [_canPromptForRating release]; +} + ++ (void)userDidSignificantEvent:(BOOL)canPromptForRating { + NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; + [NSThread detachNewThreadSelector:@selector(incrementSignificantEventAndRate:) + toTarget:[Appirater sharedInstance] + withObject:_canPromptForRating]; + [_canPromptForRating release]; +} + ++ (void)rateApp { +#if TARGET_IPHONE_SIMULATOR + NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page."); +#else + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", APPIRATER_APP_ID]]; + [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; + [userDefaults synchronize]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; +#endif +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + switch (buttonIndex) { + case 0: + { + // they don't want to rate it + [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate]; + [userDefaults synchronize]; + break; + } + case 1: + { + // they want to rate it + [Appirater rateApp]; + break; + } + case 2: + // remind them later + [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate]; + [userDefaults synchronize]; + break; + default: + break; + } +} + +#pragma mark - Hidden - (BOOL)connectedToNetwork { // Create zero addy @@ -255,125 +372,3 @@ - (void)incrementSignificantEventCount { } @end - - -@interface Appirater () -- (void)hideRatingAlert; -@end - -@implementation Appirater - -@synthesize ratingAlert; - -- (void)incrementAndRate:(NSNumber*)_canPromptForRating { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - [self incrementUseCount]; - - if ([_canPromptForRating boolValue] == YES && - [self ratingConditionsHaveBeenMet] && - [self connectedToNetwork]) - { - [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; - } - - [pool release]; -} - -- (void)incrementSignificantEventAndRate:(NSNumber*)_canPromptForRating { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - [self incrementSignificantEventCount]; - - if ([_canPromptForRating boolValue] == YES && - [self ratingConditionsHaveBeenMet] && - [self connectedToNetwork]) - { - [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; - } - - [pool release]; -} - -+ (void)appLaunched { - [Appirater appLaunched:YES]; -} - -+ (void)appLaunched:(BOOL)canPromptForRating { - NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; - [NSThread detachNewThreadSelector:@selector(incrementAndRate:) - toTarget:[Appirater sharedInstance] - withObject:_canPromptForRating]; - [_canPromptForRating release]; -} - -- (void)hideRatingAlert { - if (self.ratingAlert.visible) { - if (APPIRATER_DEBUG) - NSLog(@"APPIRATER Hiding Alert"); - [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; - } -} - -+ (void)appWillResignActive { - if (APPIRATER_DEBUG) - NSLog(@"APPIRATER appWillResignActive"); - [[Appirater sharedInstance] hideRatingAlert]; -} - -+ (void)appEnteredForeground:(BOOL)canPromptForRating { - NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; - [NSThread detachNewThreadSelector:@selector(incrementAndRate:) - toTarget:[Appirater sharedInstance] - withObject:_canPromptForRating]; - [_canPromptForRating release]; -} - -+ (void)userDidSignificantEvent:(BOOL)canPromptForRating { - NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; - [NSThread detachNewThreadSelector:@selector(incrementSignificantEventAndRate:) - toTarget:[Appirater sharedInstance] - withObject:_canPromptForRating]; - [_canPromptForRating release]; -} - -+ (void)rateApp { -#if TARGET_IPHONE_SIMULATOR - NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page."); -#else - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", APPIRATER_APP_ID]]; - [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; - [userDefaults synchronize]; - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; -#endif -} - -- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - - switch (buttonIndex) { - case 0: - { - // they don't want to rate it - [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate]; - [userDefaults synchronize]; - break; - } - case 1: - { - // they want to rate it - [Appirater rateApp]; - break; - } - case 2: - // remind them later - [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate]; - [userDefaults synchronize]; - break; - default: - break; - } -} - -@end From aed4690c4e8328c7d61180ca1794cc9580e62720 Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Sat, 1 Oct 2011 16:17:25 -0400 Subject: [PATCH 5/5] Fix memory leak on non-released NSURLConnection --- Appirater.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Appirater.m b/Appirater.m index 294df6e7..1d53e376 100644 --- a/Appirater.m +++ b/Appirater.m @@ -205,7 +205,7 @@ - (BOOL)connectedToNetwork { NSURL *testURL = [NSURL URLWithString:@"http://www.apple.com/"]; NSURLRequest *testRequest = [NSURLRequest requestWithURL:testURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0]; - NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self]; + NSURLConnection *testConnection = [[[NSURLConnection alloc] initWithRequest:testRequest delegate:self] autorelease]; return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO; }