Skip to content

Commit

Permalink
Merge pull request #58 from robotmedia/master
Browse files Browse the repository at this point in the history
Dynamic configuration + ARC
  • Loading branch information
arashpayan committed Aug 31, 2012
2 parents fe6c9a2 + f876e29 commit da20fe6
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 75 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@

.DS_Store
126 changes: 68 additions & 58 deletions Appirater.h
Expand Up @@ -44,12 +44,6 @@ extern NSString *const kAppiraterRatedCurrentVersion;
extern NSString *const kAppiraterDeclinedToRate;
extern NSString *const kAppiraterReminderRequestDate;

/*
Place your Apple generated software id here.
*/
#define APPIRATER_APP_ID 301377083


/*
Your localized app's name.
*/
Expand Down Expand Up @@ -89,64 +83,13 @@ extern NSString *const kAppiraterReminderRequestDate;
*/
#define APPIRATER_RATE_LATER NSLocalizedString(@"Remind me later", nil)

/*
Users will need to have the same version of your app installed for this many
days before they will be prompted to rate it.
*/
#define APPIRATER_DAYS_UNTIL_PROMPT 30 // double

/*
An example of a 'use' would be if the user launched the app. Bringing the app
into the foreground (on devices that support it) would also be considered
a 'use'. You tell Appirater about these events using the two methods:
[Appirater appLaunched:]
[Appirater appEnteredForeground:]
Users need to 'use' the same version of the app this many times before
before they will be prompted to rate it.
*/
#define APPIRATER_USES_UNTIL_PROMPT 20 // integer

/*
A significant event can be anything you want to be in your app. In a
telephone app, a significant event might be placing or receiving a call.
In a game, it might be beating a level or a boss. This is just another
layer of filtering that can be used to make sure that only the most
loyal of your users are being prompted to rate you on the app store.
If you leave this at a value of -1, then this won't be a criteria
used for rating. To tell Appirater that the user has performed
a significant event, call the method:
[Appirater userDidSignificantEvent:];
*/
#define APPIRATER_SIG_EVENTS_UNTIL_PROMPT -1 // integer

/*
Once the rating alert is presented to the user, they might select
'Remind me later'. This value specifies how long (in days) Appirater
will wait before reminding them.
*/
#define APPIRATER_TIME_BEFORE_REMINDING 1 // double

/*
'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

@interface Appirater : NSObject <UIAlertViewDelegate> {

UIAlertView *ratingAlert;
}

@property(nonatomic, retain) UIAlertView *ratingAlert;

/*
DEPRECATED: While still functional, it's better to use
appLaunched:(BOOL)canPromptForRating instead.
Calls [Appirater appLaunched:YES]. See appLaunched: for details of functionality.
*/
+ (void)appLaunched;
@property(nonatomic, strong) UIAlertView *ratingAlert;

/*
Tells Appirater that the app has launched, and on devices that do NOT
Expand Down Expand Up @@ -207,3 +150,70 @@ extern NSString *const kAppiraterReminderRequestDate;
+ (void)rateApp;

@end

@interface Appirater(Configuration)

/*
Set your Apple generated software id here.
*/
+ (void) setAppId:(NSString*)appId;

/*
Users will need to have the same version of your app installed for this many
days before they will be prompted to rate it.
*/
+ (void) setDaysUntilPrompt:(double)value;

/*
An example of a 'use' would be if the user launched the app. Bringing the app
into the foreground (on devices that support it) would also be considered
a 'use'. You tell Appirater about these events using the two methods:
[Appirater appLaunched:]
[Appirater appEnteredForeground:]
Users need to 'use' the same version of the app this many times before
before they will be prompted to rate it.
*/
+ (void) setUsesUntilPrompt:(NSInteger)value;

/*
A significant event can be anything you want to be in your app. In a
telephone app, a significant event might be placing or receiving a call.
In a game, it might be beating a level or a boss. This is just another
layer of filtering that can be used to make sure that only the most
loyal of your users are being prompted to rate you on the app store.
If you leave this at a value of -1, then this won't be a criteria
used for rating. To tell Appirater that the user has performed
a significant event, call the method:
[Appirater userDidSignificantEvent:];
*/
+ (void) setSignificantEventsUntilPrompt:(NSInteger)value;


/*
Once the rating alert is presented to the user, they might select
'Remind me later'. This value specifies how long (in days) Appirater
will wait before reminding them.
*/
+ (void) setTimeBeforeReminding:(double)value;

/*
'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.
*/
+ (void) setDebug:(BOOL)debug;

@end


@interface Appirater(Deprecated)

/*
DEPRECATED: While still functional, it's better to use
appLaunched:(BOOL)canPromptForRating instead.
Calls [Appirater appLaunched:YES]. See appLaunched: for details of functionality.
*/
+ (void)appLaunched __attribute__((deprecated));

@end
64 changes: 48 additions & 16 deletions Appirater.m
Expand Up @@ -49,6 +49,13 @@
NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
NSString *templateReviewURLiOS6 = @"itms-apps://itunes.apple.com/LANGUAGE/app/idAPP_ID";

static NSString *_appId;
static double _daysUntilPrompt = 30;
static NSInteger _usesUntilPrompt = 20;
static NSInteger _significantEventsUntilPrompt = -1;
static double _timeBeforeReminding = 1;
static BOOL _debug = NO;

@interface Appirater ()
- (BOOL)connectedToNetwork;
+ (Appirater*)sharedInstance;
Expand All @@ -62,6 +69,31 @@ @implementation Appirater

@synthesize ratingAlert;

+ (void) setAppId:(NSString *)appId {
_appId = appId;
}

+ (void) setDaysUntilPrompt:(double)value {
_daysUntilPrompt = value;
}

+ (void) setUsesUntilPrompt:(NSInteger)value {
_usesUntilPrompt = value;
}

+ (void) setSignificantEventsUntilPrompt:(NSInteger)value {
_significantEventsUntilPrompt = value;
}

+ (void) setTimeBeforeReminding:(double)value {
_timeBeforeReminding = value;
}

+ (void) setDebug:(BOOL)debug {
_debug = debug;
}


- (BOOL)connectedToNetwork {
// Create zero addy
struct sockaddr_in zeroAddress;
Expand All @@ -88,7 +120,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] autorelease];
NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self];

return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO;
}
Expand All @@ -109,35 +141,35 @@ + (Appirater*)sharedInstance {
}

- (void)showRatingAlert {
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE
message:APPIRATER_MESSAGE
delegate:self
cancelButtonTitle:APPIRATER_CANCEL_BUTTON
otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil] autorelease];
otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil];
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 * _daysUntilPrompt;
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 <= _usesUntilPrompt)
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 <= _significantEventsUntilPrompt)
return NO;

// has the user previously declined to rate this version of the app?
Expand All @@ -151,7 +183,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 * _timeBeforeReminding;
if (timeSinceReminderRequest < timeUntilReminder)
return NO;

Expand All @@ -171,7 +203,7 @@ - (void)incrementUseCount {
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}

if (APPIRATER_DEBUG)
if (_debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion);

if ([trackingVersion isEqualToString:version])
Expand All @@ -188,7 +220,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
Expand Down Expand Up @@ -219,7 +251,7 @@ - (void)incrementSignificantEventCount {
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}

if (APPIRATER_DEBUG)
if (_debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion);

if ([trackingVersion isEqualToString:version])
Expand All @@ -236,7 +268,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
Expand Down Expand Up @@ -295,14 +327,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 (_debug)
NSLog(@"APPIRATER appWillResignActive");
[[Appirater sharedInstance] hideRatingAlert];
}
Expand Down Expand Up @@ -330,12 +362,12 @@ + (void)rateApp {
// added work arround for wrong URL Scheme used in new App store on iOS 6
NSString *reviewURL;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
reviewURL = [templateReviewURLiOS6 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", APPIRATER_APP_ID]];
reviewURL = [templateReviewURLiOS6 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];

reviewURL = [reviewURL stringByReplacingOccurrencesOfString:@"LANGUAGE" withString:[NSString stringWithFormat:@"%@", [[NSLocale preferredLanguages] objectAtIndex:0]]];

} else {
reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", APPIRATER_APP_ID]];
reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
}

[userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -13,7 +13,7 @@ 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. Finally, call `[Appirater setAppId:@"yourAppId"]` with your Apple provided software id.

License
-------
Expand Down

0 comments on commit da20fe6

Please sign in to comment.