Allow configuration via Appirater.plist, support landscape mode. #69

Open
wants to merge 3 commits into
from
Jump to file or symbol
Failed to load files and symbols.
+191 −54
Split
View
@@ -34,7 +34,7 @@
* Copyright 2012 Arash Payan. All rights reserved.
*/
-#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
extern NSString *const kAppiraterFirstUseDate;
extern NSString *const kAppiraterUseCount;
@@ -47,7 +47,7 @@ extern NSString *const kAppiraterReminderRequestDate;
/*
Place your Apple generated software id here.
*/
-#define APPIRATER_APP_ID 301377083
+#define APPIRATER_APP_ID [bundle objectForInfoDictionaryKey:@"iTunesAppID"]
/*
@@ -64,14 +64,12 @@ extern NSString *const kAppiraterReminderRequestDate;
This is the message your users will see once they've passed the day+launches
threshold.
*/
-#define APPIRATER_LOCALIZED_MESSAGE 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)
-#define APPIRATER_MESSAGE [NSString stringWithFormat:APPIRATER_LOCALIZED_MESSAGE, APPIRATER_APP_NAME]
+#define APPIRATER_MESSAGE 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)
/*
This is the title of the message alert that users will see.
*/
-#define APPIRATER_LOCALIZED_MESSAGE_TITLE NSLocalizedString(@"Rate %@", nil)
-#define APPIRATER_MESSAGE_TITLE [NSString stringWithFormat:APPIRATER_LOCALIZED_MESSAGE_TITLE, APPIRATER_APP_NAME]
+#define APPIRATER_MESSAGE_TITLE NSLocalizedString(@"Rate %@", nil)
/*
The text of the button that rejects reviewing the app.
@@ -81,8 +79,7 @@ extern NSString *const kAppiraterReminderRequestDate;
/*
Text of button that will send user to app review page.
*/
-#define APPIRATER_LOCALIZED_RATE_BUTTON NSLocalizedString(@"Rate %@", nil)
-#define APPIRATER_RATE_BUTTON [NSString stringWithFormat:APPIRATER_LOCALIZED_RATE_BUTTON, APPIRATER_APP_NAME]
+#define APPIRATER_RATE_BUTTON NSLocalizedString(@"Rate %@", nil)
/*
Text for button to remind the user to review later.
@@ -127,11 +124,19 @@ extern NSString *const kAppiraterReminderRequestDate;
*/
#define APPIRATER_TIME_BEFORE_REMINDING 1 // double
+/*
+ Due to screen dimension limitations, only one of the 'Remind me later' or
+ the 'No, thanks' button may be shown. This setting specifies how many times
+ the 'Remind me later' button should be shown before switching to the 'No thanks'
+ button. This setting does not have any effect while in portrait mode.
+ */
+#define APPIRATER_LANDSCAPE_HIDE_CANCEL_COUNT 5 // integer
+
/*
'YES' will show the Appirater alert everytime. Useful for testing how your message
looks and making sure the link to your app's review page works.
*/
-#define APPIRATER_DEBUG YES
+#define APPIRATER_DEBUG NO
@interface Appirater : NSObject <UIAlertViewDelegate> {
View
@@ -45,21 +45,102 @@
NSString *const kAppiraterRatedCurrentVersion = @"kAppiraterRatedCurrentVersion";
NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate";
NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate";
+NSString *const kAppiraterDialogCount = @"kAppiraterDialogCount";
-NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
+NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@";
+
+NSString *const kConfigAppID = @"AppID";
+NSString *const kConfigAppName = @"AppName";
+NSString *const kConfigMessage = @"Message";
+NSString *const kConfigMessageTitle = @"MessageTitle";
+NSString *const kConfigCancelButton = @"CancelButton";
+NSString *const kConfigRateButton = @"RateButton";
+NSString *const kConfigRateLater = @"RateLater";
+NSString *const kConfigDaysUntilPrompt = @"DaysUntilPrompt";
+NSString *const kConfigUsesUntilPrompt = @"UsesUntilPrompt";
+NSString *const kConfigSigEventsUntilPrompt = @"SigEventsUntilPrompt";
+NSString *const kConfigTimeBeforeReminding = @"TimeBeforeReminding";
+NSString *const kConfigLandscapeHideCancelCount = @"LandscapeHideCancelCount";
+NSString *const kConfigDebug = @"Debug";
+
+@interface Appirater () {
+@private
+ NSDictionary *configuration;
+ BOOL debug;
+}
+
+@property (nonatomic, readonly, getter = isDebug) BOOL debug;
-@interface Appirater ()
- (BOOL)connectedToNetwork;
+ (Appirater*)sharedInstance;
- (void)showRatingAlert;
- (BOOL)ratingConditionsHaveBeenMet;
- (void)incrementUseCount;
- (void)hideRatingAlert;
+- (NSString *)configurationStringForKey:(NSString *)key;
+- (NSInteger)configurationIntegerForKey:(NSString *)key;
+
@end
-@implementation Appirater
+@implementation Appirater
@synthesize ratingAlert;
+@synthesize debug;
+
+- (id)init {
+ if (self = [super init]) {
+ NSBundle *bundle = [NSBundle mainBundle];
+
+ NSMutableDictionary *defaults = [NSMutableDictionary dictionaryWithCapacity:10];
+ [defaults setObject:APPIRATER_APP_ID forKey:kConfigAppID];
+ [defaults setObject:APPIRATER_APP_NAME forKey:kConfigAppName];
+ [defaults setObject:APPIRATER_MESSAGE forKey:kConfigMessage];
+ [defaults setObject:APPIRATER_MESSAGE_TITLE forKey:kConfigMessageTitle];
+ [defaults setObject:APPIRATER_CANCEL_BUTTON forKey:kConfigCancelButton];
+ [defaults setObject:APPIRATER_RATE_BUTTON forKey:kConfigRateButton];
+ [defaults setObject:APPIRATER_RATE_LATER forKey:kConfigRateLater];
+ [defaults setObject:[NSNumber numberWithInteger:APPIRATER_DAYS_UNTIL_PROMPT] forKey:kConfigDaysUntilPrompt];
+ [defaults setObject:[NSNumber numberWithInteger:APPIRATER_USES_UNTIL_PROMPT] forKey:kConfigUsesUntilPrompt];
+ [defaults setObject:[NSNumber numberWithInteger:APPIRATER_SIG_EVENTS_UNTIL_PROMPT] forKey:kConfigSigEventsUntilPrompt];
+ [defaults setObject:[NSNumber numberWithInteger:APPIRATER_TIME_BEFORE_REMINDING] forKey:kConfigTimeBeforeReminding];
+ [defaults setObject:[NSNumber numberWithInteger:APPIRATER_LANDSCAPE_HIDE_CANCEL_COUNT] forKey:kConfigLandscapeHideCancelCount];
+ [defaults setObject:[NSNumber numberWithBool:APPIRATER_DEBUG] forKey:kConfigDebug];
+
+ NSString *path = [bundle pathForResource:@"Appirater" ofType:@"plist"];
+ NSDictionary *dict = path ? [NSDictionary dictionaryWithContentsOfFile:path] : nil;
+ if (dict)
+ [defaults addEntriesFromDictionary:dict];
+
+ NSString *appName = [defaults objectForKey:kConfigAppName];
+ for (NSString *key in [defaults allKeys]) {
+ id value = [defaults objectForKey:key];
+ if ([value isKindOfClass:[NSString class]]) {
+ value = [value stringByReplacingOccurrencesOfString:@"%@" withString:appName];
+ [defaults setObject:value forKey:key];
+ }
+ }
+
+ configuration = [[NSDictionary alloc] initWithDictionary:defaults];
+ debug = [[configuration objectForKey:kConfigDebug] boolValue];
+
+ if (debug)
+ NSLog(@"Appirater Config: %@", configuration);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [configuration release];
+ [super dealloc];
+}
+
+- (NSString *)configurationStringForKey:(NSString *)key {
+ return [configuration objectForKey:key];
+}
+
+- (NSInteger)configurationIntegerForKey:(NSString *)key {
+ return [[configuration objectForKey:key] integerValue];
+}
- (BOOL)connectedToNetwork {
// Create zero addy
@@ -108,35 +189,56 @@ + (Appirater*)sharedInstance {
}
- (void)showRatingAlert {
- UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE
- message:APPIRATER_MESSAGE
+ // Increment dialog count
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSInteger count = [defaults integerForKey:kAppiraterDialogCount] + 1;
+ [defaults setInteger:count forKey:kAppiraterDialogCount];
+ [defaults synchronize];
+
+ // Hide one of cancel/reminder button if in landscape mode
+ NSString *cancelButtonTitle = [self configurationStringForKey:kConfigCancelButton];
+ NSString *reminderButtonTitle = [self configurationStringForKey:kConfigRateLater];
+ UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
+ BOOL isLandscape = UIInterfaceOrientationIsLandscape(orientation);
+ if (isLandscape) {
+ if (count <= [self configurationIntegerForKey:kConfigLandscapeHideCancelCount])
+ cancelButtonTitle = nil;
+ else
+ reminderButtonTitle = nil;
+ }
+
+ if (debug)
+ NSLog(@"APPIRATER Show dialog. Count=%d Orientation=%d Landscape=%d", count, orientation, isLandscape);
+
+ UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:[self configurationStringForKey:kConfigMessageTitle]
+ message:[self configurationStringForKey:kConfigMessage]
delegate:self
- cancelButtonTitle:APPIRATER_CANCEL_BUTTON
- otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil] autorelease];
+ cancelButtonTitle:cancelButtonTitle
+ otherButtonTitles:[self configurationStringForKey:kConfigRateButton], reminderButtonTitle, nil] autorelease];
self.ratingAlert = alertView;
[alertView show];
}
- (BOOL)ratingConditionsHaveBeenMet {
- if (APPIRATER_DEBUG)
+ if (debug)
return YES;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]];
NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
- NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_DAYS_UNTIL_PROMPT;
+ NSTimeInterval timeUntilRate = 60 * 60 * 24 * [self configurationIntegerForKey:kConfigDaysUntilPrompt];
if (timeSinceFirstLaunch < timeUntilRate)
return NO;
// check if the app has been used enough
int useCount = [userDefaults integerForKey:kAppiraterUseCount];
- if (useCount <= APPIRATER_USES_UNTIL_PROMPT)
+ if (useCount <= [self configurationIntegerForKey:kConfigUsesUntilPrompt])
return NO;
// check if the user has done enough significant events
int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
- if (sigEventCount <= APPIRATER_SIG_EVENTS_UNTIL_PROMPT)
+ if (sigEventCount <= [self configurationIntegerForKey:kConfigSigEventsUntilPrompt])
return NO;
// has the user previously declined to rate this version of the app?
@@ -150,7 +252,7 @@ - (BOOL)ratingConditionsHaveBeenMet {
// if the user wanted to be reminded later, has enough time passed?
NSDate *reminderRequestDate = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterReminderRequestDate]];
NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate];
- NSTimeInterval timeUntilReminder = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING;
+ NSTimeInterval timeUntilReminder = 60 * 60 * 24 * [self configurationIntegerForKey:kConfigTimeBeforeReminding];
if (timeSinceReminderRequest < timeUntilReminder)
return NO;
@@ -160,7 +262,7 @@ - (BOOL)ratingConditionsHaveBeenMet {
- (void)incrementUseCount {
// get the app's version
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey];
-
+
// get the version number that we've been tracking
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *trackingVersion = [userDefaults stringForKey:kAppiraterCurrentVersion];
@@ -170,7 +272,7 @@ - (void)incrementUseCount {
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}
- if (APPIRATER_DEBUG)
+ if (debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion);
if ([trackingVersion isEqualToString:version])
@@ -187,7 +289,7 @@ - (void)incrementUseCount {
int useCount = [userDefaults integerForKey:kAppiraterUseCount];
useCount++;
[userDefaults setInteger:useCount forKey:kAppiraterUseCount];
- if (APPIRATER_DEBUG)
+ if (debug)
NSLog(@"APPIRATER Use count: %d", useCount);
}
else
@@ -200,8 +302,9 @@ - (void)incrementUseCount {
[userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion];
[userDefaults setBool:NO forKey:kAppiraterDeclinedToRate];
[userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate];
+ [userDefaults setInteger:0 forKey:kAppiraterDialogCount];
}
-
+
[userDefaults synchronize];
}
@@ -218,7 +321,7 @@ - (void)incrementSignificantEventCount {
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}
- if (APPIRATER_DEBUG)
+ if (debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion);
if ([trackingVersion isEqualToString:version])
@@ -235,7 +338,7 @@ - (void)incrementSignificantEventCount {
int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
sigEventCount++;
[userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount];
- if (APPIRATER_DEBUG)
+ if (debug)
NSLog(@"APPIRATER Significant event count: %d", sigEventCount);
}
else
@@ -248,6 +351,7 @@ - (void)incrementSignificantEventCount {
[userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion];
[userDefaults setBool:NO forKey:kAppiraterDeclinedToRate];
[userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate];
+ [userDefaults setInteger:0 forKey:kAppiraterDialogCount];
}
[userDefaults synchronize];
@@ -294,14 +398,14 @@ + (void)appLaunched:(BOOL)canPromptForRating {
- (void)hideRatingAlert {
if (self.ratingAlert.visible) {
- if (APPIRATER_DEBUG)
+ if (debug)
NSLog(@"APPIRATER Hiding Alert");
[self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO];
}
}
+ (void)appWillResignActive {
- if (APPIRATER_DEBUG)
+ if ([[self sharedInstance] isDebug])
NSLog(@"APPIRATER appWillResignActive");
[[Appirater sharedInstance] hideRatingAlert];
}
@@ -325,7 +429,7 @@ + (void)rateApp {
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]];
+ NSString *reviewURL = [NSString stringWithFormat:templateReviewURL, [[self sharedInstance] configurationStringForKey:kConfigAppID]];
[userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
[userDefaults synchronize];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
@@ -334,28 +438,20 @@ + (void)rateApp {
- (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;
+
+ if (buttonIndex == alertView.cancelButtonIndex) {
+ // they don't want to rate it
+ [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate];
+ [userDefaults synchronize];
+ }
+ else if (buttonIndex == alertView.firstOtherButtonIndex) {
+ // they want to rate it
+ [Appirater rateApp];
+ }
+ else if (buttonIndex == alertView.firstOtherButtonIndex + 1) {
+ // remind them later
+ [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate];
+ [userDefaults synchronize];
}
}
Oops, something went wrong.