Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic configuration + ARC #58

Merged
merged 5 commits into from Aug 31, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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