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

Additional custom action and negative event tracking #216

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Appirater.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
extern NSString *const kAppiraterFirstUseDate;
extern NSString *const kAppiraterUseCount;
extern NSString *const kAppiraterSignificantEventCount;
extern NSString *const kAppiraterNegativeEventCount;
extern NSString *const kAppiraterCurrentVersion;
extern NSString *const kAppiraterRatedCurrentVersion;
extern NSString *const kAppiraterDeclinedToRate;
Expand Down Expand Up @@ -143,6 +144,16 @@ extern NSString *const kAppiraterReminderRequestDate;
*/
+ (void)userDidSignificantEvent:(BOOL)canPromptForRating;


/*!
Tells Appirater that the user experienced a negative event (like a crash)

If the user has experienced too many negative events, he is more likely to leave negative feedback.
Keep up with crash reports and update the app to keep users happy.

*/
+ (void)userExperiencedNegativeEvent;

/*!
Tells Appirater to try and show the prompt (a rating alert). The prompt will be showed
if there is connection available, the user hasn't declined to rate
Expand Down Expand Up @@ -182,6 +193,7 @@ extern NSString *const kAppiraterReminderRequestDate;
*/
+ (void)closeModal;


/*!
Asks Appirater if the user has declined to rate;
*/
Expand Down Expand Up @@ -235,6 +247,14 @@ extern NSString *const kAppiraterReminderRequestDate;
+ (void) setSignificantEventsUntilPrompt:(NSInteger)value;


/*!
A negative event is anything that can make user leave negative feedback
(crash, connectivity issue, etc.). To tell Appirater that the user has experienced
a negative event, call the method:
[Appirater userExperiencedNegativeEvent:];
*/
+ (void)setMaxNegativeEvents:(NSInteger)maxNegativeEvents;

/*!
Once the rating alert is presented to the user, they might select
'Remind me later'. This value specifies how long (in days) Appirater
Expand Down Expand Up @@ -267,6 +287,13 @@ extern NSString *const kAppiraterReminderRequestDate;
*/
+ (void) setCustomAlertRateLaterButtonTitle:(NSString *)rateLaterTitle;

/*!
Sets Custom Action button title. When set additional button will appear, and delegate method
appiraterDidSelectCustomAction: will be called. Can be used to add "Report a problem" button,
that will bring up a email composition or other form to communicate with authors directly, bypassing reviews.
*/
+ (void)setCustomActionButtonTitle:(NSString *)actionTitle;

/*!
'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.
Expand Down
112 changes: 99 additions & 13 deletions Appirater.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
NSString *const kAppiraterFirstUseDate = @"kAppiraterFirstUseDate";
NSString *const kAppiraterUseCount = @"kAppiraterUseCount";
NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount";
NSString *const kAppiraterNegativeEventCount = @"kAppiraterNegativeEventCount";
NSString *const kAppiraterCurrentVersion = @"kAppiraterCurrentVersion";
NSString *const kAppiraterRatedCurrentVersion = @"kAppiraterRatedCurrentVersion";
NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate";
Expand All @@ -58,6 +59,7 @@
static double _daysUntilPrompt = 30;
static NSInteger _usesUntilPrompt = 20;
static NSInteger _significantEventsUntilPrompt = -1;
static NSInteger _negativeEventsMax = 0;
static double _timeBeforeReminding = 1;
static BOOL _debug = NO;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
Expand All @@ -75,6 +77,7 @@ @interface Appirater ()
@property (nonatomic, copy) NSString *alertMessage;
@property (nonatomic, copy) NSString *alertCancelTitle;
@property (nonatomic, copy) NSString *alertRateTitle;
@property (nonatomic, copy) NSString *customActionTitle;
@property (nonatomic, copy) NSString *alertRateLaterTitle;
- (BOOL)connectedToNetwork;
+ (Appirater*)sharedInstance;
Expand Down Expand Up @@ -132,16 +135,25 @@ + (void) setCustomAlertRateButtonTitle:(NSString *)rateTitle
[self sharedInstance].alertRateTitle = rateTitle;
}

+ (void) setCustomActionButtonTitle:(NSString *)actionTitle
{
[self sharedInstance].customActionTitle = actionTitle;
}

+ (void) setCustomAlertRateLaterButtonTitle:(NSString *)rateLaterTitle
{
[self sharedInstance].alertRateLaterTitle = rateLaterTitle;
}

+ (void)setMaxNegativeEvents:(NSInteger)maxNegativeEvents {
_negativeEventsMax = maxNegativeEvents;
}

+ (void) setDebug:(BOOL)debug {
_debug = debug;
}
+ (void)setDelegate:(id<AppiraterDelegate>)delegate{
_delegate = delegate;
[self sharedInstance].delegate = delegate;
}
+ (void)setUsesAnimation:(BOOL)animation {
_usesAnimation = animation;
Expand Down Expand Up @@ -275,16 +287,17 @@ - (void)showRatingAlert:(BOOL)displayRateLaterButton {
message:self.alertMessage
delegate:self
cancelButtonTitle:self.alertCancelTitle
otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil];
otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, self.customActionTitle, nil];
} else {
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
message:self.alertMessage
delegate:self
cancelButtonTitle:self.alertCancelTitle
otherButtonTitles:self.alertRateTitle, nil];
otherButtonTitles:self.alertRateTitle, self.customActionTitle, nil];
}

self.ratingAlert = alertView;
self.ratingAlert.tag = displayRateLaterButton;
[alertView show];

id <AppiraterDelegate> delegate = _delegate;
Expand Down Expand Up @@ -334,8 +347,12 @@ - (BOOL)ratingConditionsHaveBeenMet {
return YES;

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]];

int failCount = [userDefaults integerForKey:kAppiraterNegativeEventCount];
if (failCount > _negativeEventsMax)
return NO;

NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]];
NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
NSTimeInterval timeUntilRate = 60 * 60 * 24 * _daysUntilPrompt;
if (timeSinceFirstLaunch < timeUntilRate)
Expand Down Expand Up @@ -401,6 +418,7 @@ - (void)incrementUseCount {
[userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterFirstUseDate];
[userDefaults setInteger:1 forKey:kAppiraterUseCount];
[userDefaults setInteger:0 forKey:kAppiraterSignificantEventCount];
[userDefaults setInteger:0 forKey:kAppiraterNegativeEventCount];
[userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion];
[userDefaults setBool:NO forKey:kAppiraterDeclinedToRate];
[userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate];
Expand Down Expand Up @@ -449,6 +467,7 @@ - (void)incrementSignificantEventCount {
[userDefaults setDouble:0 forKey:kAppiraterFirstUseDate];
[userDefaults setInteger:0 forKey:kAppiraterUseCount];
[userDefaults setInteger:1 forKey:kAppiraterSignificantEventCount];
[userDefaults setInteger:0 forKey:kAppiraterNegativeEventCount];
[userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion];
[userDefaults setBool:NO forKey:kAppiraterDeclinedToRate];
[userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate];
Expand All @@ -457,6 +476,55 @@ - (void)incrementSignificantEventCount {
[userDefaults synchronize];
}

- (void)incrementNegativeEventCount {
// 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];
if (trackingVersion == nil)
{
trackingVersion = version;
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}

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

if ([trackingVersion isEqualToString:version])
{
// check if the first use date has been set. if not, set it.
NSTimeInterval timeInterval = [userDefaults doubleForKey:kAppiraterFirstUseDate];
if (timeInterval == 0)
{
timeInterval = [[NSDate date] timeIntervalSince1970];
[userDefaults setDouble:timeInterval forKey:kAppiraterFirstUseDate];
}

// increment negative event count
int negEventCount = [userDefaults integerForKey:kAppiraterNegativeEventCount];
negEventCount++;
[userDefaults setInteger:negEventCount forKey:kAppiraterNegativeEventCount];
if (_debug)
NSLog(@"APPIRATER Negative event count: %d", negEventCount);
}
else
{
// it's a new version of the app, so restart tracking
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
[userDefaults setDouble:0 forKey:kAppiraterFirstUseDate];
[userDefaults setInteger:0 forKey:kAppiraterUseCount];
[userDefaults setInteger:1 forKey:kAppiraterSignificantEventCount];
[userDefaults setInteger:0 forKey:kAppiraterNegativeEventCount];
[userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion];
[userDefaults setBool:NO forKey:kAppiraterDeclinedToRate];
[userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate];
}

[userDefaults synchronize];
}

- (void)incrementAndRate:(BOOL)canPromptForRating {
[self incrementUseCount];

Expand Down Expand Up @@ -535,6 +603,14 @@ + (void)userDidSignificantEvent:(BOOL)canPromptForRating {
});
}

+ (void)userExperiencedNegativeEvent
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
[[Appirater sharedInstance] incrementNegativeEventCount];
});
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-implementations"
+ (void)showPrompt {
Expand Down Expand Up @@ -657,7 +733,7 @@ - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

id <AppiraterDelegate> delegate = _delegate;

BOOL isDisplayingRateLater = (BOOL) alertView.tag;
switch (buttonIndex) {
case 0:
{
Expand All @@ -679,18 +755,28 @@ - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)
break;
}
case 2:
// remind them later
[userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate];
[userDefaults synchronize];
if(delegate && [delegate respondsToSelector:@selector(appiraterDidOptToRemindLater:)]){
[delegate appiraterDidOptToRemindLater:self];
}
break;
if (isDisplayingRateLater)
{
// remind them later
[userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate];
[userDefaults synchronize];
if(delegate && [delegate respondsToSelector:@selector(appiraterDidOptToRemindLater:)]){
[delegate appiraterDidOptToRemindLater:self];
}
break;
}
case 3:
// fall through to call delegate custom action from case 2 when not displaying RateLater
if(delegate && [delegate respondsToSelector:@selector(appiraterDidSelectCustomAction:)]){
[delegate appiraterDidSelectCustomAction:self];
}
break;
default:
break;
}
}


//Delegate call from the StoreKit view.
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[Appirater closeModal];
Expand Down
2 changes: 2 additions & 0 deletions AppiraterDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
-(void)appiraterDidOptToRemindLater:(Appirater *)appirater;
-(void)appiraterWillPresentModalView:(Appirater *)appirater animated:(BOOL)animated;
-(void)appiraterDidDismissModalView:(Appirater *)appirater animated:(BOOL)animated;
-(void)appiraterDidSelectCustomAction:(Appirater *)appirater;

@end