Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

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

Open
wants to merge 3 commits into from

1 participant

@jlopez

Hi, I added these changes to avoid having to change the code to tailor to a specific usage. Instead, all the configuration options may be specified in an Appirater.plist file (sample included). The plist may be omitted, in which case sensible defaults apply (just as before). Lastly, the AppID may be specified in the Info.plist.

Also added a small change to support landscape orientation (before, the dialog was trimmed if shown in landscape).

Lastly, made debug default to NO. Debug may still be turned on by using an Appirater.plist file.

jlopez added some commits
@jlopez jlopez Add plist configuration
Allow Appirater to be configured via optional Appirater.plist file.
If omitted, it defaults to the old defaults defined in Appirater.h,
except for the iTunes App ID, which defaults to the Info.plist
`iTunesAppID` string entry (if omitted it will be 0)
0632493
@jlopez jlopez Add landscape support
Show only two buttons in rate request dialog: 'Rate' and one of
'Reminde me' and 'No'. Use LandscapeHideCancelCount in Appirater.plist
to define how many times to show the 'Remind me' button before
showing the 'No' button.
f275683
@jlopez jlopez Make DEBUG default to NO
May be turned on via Appirater.plist
a5058ca
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 24, 2012
  1. @jlopez

    Add plist configuration

    jlopez authored
    Allow Appirater to be configured via optional Appirater.plist file.
    If omitted, it defaults to the old defaults defined in Appirater.h,
    except for the iTunes App ID, which defaults to the Info.plist
    `iTunesAppID` string entry (if omitted it will be 0)
Commits on Aug 28, 2012
  1. @jlopez

    Add landscape support

    jlopez authored
    Show only two buttons in rate request dialog: 'Rate' and one of
    'Reminde me' and 'No'. Use LandscapeHideCancelCount in Appirater.plist
    to define how many times to show the 'Remind me' button before
    showing the 'No' button.
  2. @jlopez

    Make DEBUG default to NO

    jlopez authored
    May be turned on via Appirater.plist
This page is out of date. Refresh to see the latest.
Showing with 191 additions and 54 deletions.
  1. +14 −9 Appirater.h
  2. +139 −43 Appirater.m
  3. +35 −0 Appirater.plist.sample
  4. +3 −2 README.md
View
23 Appirater.h
@@ -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.
@@ -128,10 +125,18 @@ 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
182 Appirater.m
@@ -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];
}
}
View
35 Appirater.plist.sample
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <!-- All these options have suitable defaults -->
+ <!-- See Appirater.h for more information -->
+ <!-- %@ is always replaced with AppName -->
+ <key>AppID</key>
+ <string>1234</string>
+ <key>AppName</key>
+ <string>Appirater</string>
+ <key>Message</key>
+ <string>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!</string>
+ <key>MessageTitle</key>
+ <string>Rate %@</string>
+ <key>CancelButton</key>
+ <string>No, Thanks</string>
+ <key>RateButton</key>
+ <string>Rate %@</string>
+ <key>RateLater</key>
+ <string>Remind me later</string>
+ <key>DaysUntilPrompt</key>
+ <integer>30</integer>
+ <key>UsesUntilPrompt</key>
+ <integer>20</integer>
+ <key>SigEventsUntilPrompt</key>
+ <integer>-1</integer>
+ <key>TimeBeforeReminding</key>
+ <integer>1</integer>
+ <key>LandscapeHideCancelCount</key>
+ <integer>5</integer>
+ <key>Debug</key>
+ <false/>
+</dict>
+</plist>
View
5 README.md
@@ -13,7 +13,8 @@ Getting Started
3. Call `[Appirater appLaunched:YES]` at the end of your app delegate's `application:didFinishLaunchingWithOptions:` method.
4. Call `[Appirater appEnteredForeground:YES]` in your app delegate's `applicationWillEnterForeground:` method.
5. (OPTIONAL) Call `[Appirater userDidSignificantEvent:YES]` when the user does something 'significant' in the app.
-6. Finally, set the `APPIRATER_APP_ID` in `Appirater.h` to your Apple provided software id.
+6. Set an Info.plist string entry `iTunesAppID` to your Apple provided software id.
+7. Optionally, include an Appirater.plist file containing customization parameters (see Appirater.plist.sample)
License
-------
@@ -30,4 +31,4 @@ MonoTouch Port
[homepage]: http://arashpayan.com/blog/index.php/2009/09/07/presenting-appirater/
[arash]: http://arashpayan.com
[ivan]: https://www.facebook.com/nikitinivan
-[monotouchport]: https://github.com/chebum/Appirater-for-MonoTouch
+[monotouchport]: https://github.com/chebum/Appirater-for-MonoTouch
Something went wrong with that request. Please try again.