From 603ed707ad2ab4df37b04a0406baf8c88814ade0 Mon Sep 17 00:00:00 2001 From: Vladimir Samoylenko Date: Sun, 19 Nov 2017 19:52:54 +0200 Subject: [PATCH 1/2] Implemented feature to show button "Rate and review" if StoreKit is unavailable --- Appirater.h | 19 ++++++++++++++++-- Appirater.m | 57 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/Appirater.h b/Appirater.h index bc6467a6..2dd71234 100644 --- a/Appirater.h +++ b/Appirater.h @@ -75,7 +75,7 @@ extern NSString *const kAppiraterReminderRequestDate; #define APPIRATER_CANCEL_BUTTON NSLocalizedStringFromTableInBundle(@"No, Thanks", @"AppiraterLocalizable", [Appirater bundle], nil) /*! - Text of button that will send user to app review page. + Text of button that will send user to app rate page. */ #define APPIRATER_LOCALIZED_RATE_BUTTON NSLocalizedStringFromTableInBundle(@"Rate %@", @"AppiraterLocalizable", [Appirater bundle], nil) #define APPIRATER_RATE_BUTTON [NSString stringWithFormat:APPIRATER_LOCALIZED_RATE_BUTTON, APPIRATER_APP_NAME] @@ -85,12 +85,19 @@ extern NSString *const kAppiraterReminderRequestDate; */ #define APPIRATER_RATE_LATER NSLocalizedStringFromTableInBundle(@"Remind me later", @"AppiraterLocalizable", [Appirater bundle], nil) +/*! + Text of button that will send user to app rate & review page. + */ +#define APPIRATER_LOCALIZED_RATE_AND_REVIEW_BUTTON NSLocalizedStringFromTableInBundle(@"Rate and review %@", @"AppiraterLocalizable", [Appirater bundle], nil) +#define APPIRATER_RATE_AND_REVIEW_BUTTON [NSString stringWithFormat:APPIRATER_LOCALIZED_RATE_AND_REVIEW_BUTTON, APPIRATER_APP_NAME] + @interface Appirater : NSObject /*! UIAlertController for iOS 8 and later, otherwise UIAlertView */ @property(nonatomic, strong) id ratingAlert; +#pragma clang diagnostic pop @property(nonatomic) BOOL openInAppStore; #if __has_feature(objc_arc_weak) @property(nonatomic, weak) NSObject *delegate; @@ -174,8 +181,12 @@ extern NSString *const kAppiraterReminderRequestDate; about calling this -- instead, just call the other functions listed above, and let Appirater handle the bookkeeping of deciding when to ask the user whether to rate the app. + + @param writeReviewPage: is used if StoreKit is unavailable, used to + redirect user to write review in additional to his rating */ -+ (void)rateApp; + ++ (void)rateApp:(BOOL)writeReviewPage; /*! Tells Appirater to immediately close any open rating modals (e.g. StoreKit rating VCs). @@ -273,6 +284,10 @@ extern NSString *const kAppiraterReminderRequestDate; */ + (void) setDebug:(BOOL)debug; +/*! + 'YES' will show button 'Rate and Review' in dialog if StoreKit is unavailable. + */ ++ (void) setShowRateAndReview:(BOOL)rateAndReview; /*! Set the delegate if you want to know when Appirater does something */ diff --git a/Appirater.m b/Appirater.m index 01a2b493..604e1f30 100644 --- a/Appirater.m +++ b/Appirater.m @@ -65,12 +65,14 @@ static UIStatusBarStyle _statusBarStyle; static BOOL _modalOpen = false; static BOOL _alwaysUseMainBundle = NO; +static BOOL _rateAndReview = NO; @interface Appirater () @property (nonatomic, copy) NSString *alertTitle; @property (nonatomic, copy) NSString *alertMessage; @property (nonatomic, copy) NSString *alertCancelTitle; @property (nonatomic, copy) NSString *alertRateTitle; +@property (nonatomic, copy) NSString *alertRateAndReviewTitle; @property (nonatomic, copy) NSString *alertRateLaterTitle; @property (nonatomic, strong) NSOperationQueue *eventQueue; - (BOOL)connectedToNetwork; @@ -153,6 +155,9 @@ + (void)setModalOpen:(BOOL)open { + (void)setAlwaysUseMainBundle:(BOOL)alwaysUseMainBundle { _alwaysUseMainBundle = alwaysUseMainBundle; } ++ (void) setShowRateAndReview:(BOOL)rateAndReview { + _rateAndReview = rateAndReview; +} + (NSBundle *)bundle { @@ -199,6 +204,11 @@ - (NSString *)alertRateLaterTitle return _alertRateLaterTitle ? _alertRateLaterTitle : APPIRATER_RATE_LATER; } +- (NSString *)alertRateAndReviewTitle +{ + return _alertRateAndReviewTitle ? _alertRateAndReviewTitle : APPIRATER_RATE_AND_REVIEW_BUTTON; +} + - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -283,13 +293,20 @@ - (void)showRatingAlert:(BOOL)displayRateLaterButton { if (NSStringFromClass([SKStoreReviewController class]) != nil) { #pragma clang diagnostic pop [Appirater rateApp]; + if (@available(iOS 10.3, *)) { + [Appirater rateApp:NO]; } else { // Otherwise show a custom Alert NSMutableArray *buttons = [[NSMutableArray alloc] initWithObjects:self.alertRateTitle, nil]; + + if (_rateAndReview){ + [buttons addObject:self.alertRateAndReviewTitle]; + } if (displayRateLaterButton) { [buttons addObject:self.alertRateLaterTitle]; } if (NSStringFromClass([UIAlertController class]) != nil) { + if (@available(iOS 8.0, *)) { [buttons addObject:self.alertCancelTitle]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:self.alertTitle message:self.alertMessage preferredStyle:UIAlertControllerStyleAlert]; @@ -304,8 +321,10 @@ - (void)showRatingAlert:(BOOL)displayRateLaterButton { buttonIndex = 1; } else if ([title isEqual:self.alertRateLaterTitle]) { buttonIndex = 2; + } else if ([title isEqual:self.alertRateAndReviewTitle]) { + buttonIndex = 3; } - + [self alertViewDidDismissWithButtonIndex:buttonIndex]; }]]; } @@ -556,7 +575,7 @@ + (void)appLaunched:(BOOL)canPromptForRating { } - (BOOL)isRatingAlertVisible { - if (NSStringFromClass([UIAlertController class]) != nil) { + if (@available(iOS 8.0, *)) { return ((UIAlertController *)self.ratingAlert).view.superview != nil; } else { #pragma clang diagnostic push @@ -568,14 +587,9 @@ - (BOOL)isRatingAlertVisible { - (void)hideRatingAlert { if ([self isRatingAlertVisible]) { - if (_debug) { + if (_debug) NSLog(@"APPIRATER Hiding Alert"); - } - if ([self.ratingAlert respondsToSelector:@selector(dismissWithClickedButtonIndex:animated:)]) { - [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; - } else { - [self.ratingAlert dismissViewControllerAnimated:NO completion:nil]; - } + [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; } } @@ -666,18 +680,15 @@ + (UIViewController *) topMostViewController: (UIViewController *) controller { return controller; } -+ (void)rateApp { ++ (void)rateApp:(BOOL)writeReviewPage { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; [userDefaults synchronize]; - // Use the built SKStoreReviewController if available (available from iOS 10.3 upwards) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" - if (NSStringFromClass([SKStoreReviewController class]) != nil) { + //Use the built SKStoreReviewController if available (available from iOS 10.3 upwards) + if (@available(iOS 10.3, *)) { [SKStoreReviewController requestReview]; -#pragma clang diagnostic pop return; } @@ -716,6 +727,11 @@ + (void)rateApp { reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:_appId]; } + if (writeReviewPage) + { + reviewURL = [reviewURL stringByAppendingString:@"&action=write-review"]; + } + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; #endif } @@ -747,7 +763,7 @@ - (void)alertViewDidDismissWithButtonIndex:(NSInteger)buttonIndex { case 1: { // they want to rate it - [Appirater rateApp]; + [Appirater rateApp:NO]; if(delegate&& [delegate respondsToSelector:@selector(appiraterDidOptToRate:)]){ [delegate appiraterDidOptToRate:self]; } @@ -761,6 +777,15 @@ - (void)alertViewDidDismissWithButtonIndex:(NSInteger)buttonIndex { [delegate appiraterDidOptToRemindLater:self]; } break; + case 3: + { + // they want to rate it + [Appirater rateApp:YES]; + if(delegate&& [delegate respondsToSelector:@selector(appiraterDidOptToRate:)]){ + [delegate appiraterDidOptToRate:self]; + } + break; + } default: break; } From cb369c8e917c020ae88849de9aff54b3d10eb9dc Mon Sep 17 00:00:00 2001 From: Vladimir Samoylenko Date: Sun, 19 Nov 2017 20:01:25 +0200 Subject: [PATCH 2/2] Added localization strings for English, Ukrainian and Russian. --- en.lproj/AppiraterLocalizable.strings | 3 ++- ru.lproj/AppiraterLocalizable.strings | 1 + uk.lproj/AppiraterLocalizable.strings | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/en.lproj/AppiraterLocalizable.strings b/en.lproj/AppiraterLocalizable.strings index 66d5314a..df73694d 100644 --- a/en.lproj/AppiraterLocalizable.strings +++ b/en.lproj/AppiraterLocalizable.strings @@ -1,4 +1,5 @@ "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!" = "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!"; "Rate %@" = "Rate %@"; +"Rate and review %@" = "Rate and review %@"; "No, Thanks" = "No, thanks"; -"Remind me later" = "Remind me later"; \ No newline at end of file +"Remind me later" = "Remind me later"; diff --git a/ru.lproj/AppiraterLocalizable.strings b/ru.lproj/AppiraterLocalizable.strings index b2f84988..ce9fe710 100644 --- a/ru.lproj/AppiraterLocalizable.strings +++ b/ru.lproj/AppiraterLocalizable.strings @@ -1,4 +1,5 @@ "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!" = "Если Вам нравится %@, пожалуйста, поставьте свою оценку. Это займет у Вас не больше одной минуты.\n Спасибо за поддержку!"; "Rate %@" = "Оценить %@"; +"Rate and review %@" = "Оценить с отзывом %@"; "No, Thanks" = "Нет, спасибо"; "Remind me later" = "Напомнить позже"; diff --git a/uk.lproj/AppiraterLocalizable.strings b/uk.lproj/AppiraterLocalizable.strings index 11896d4f..e31a773c 100644 --- a/uk.lproj/AppiraterLocalizable.strings +++ b/uk.lproj/AppiraterLocalizable.strings @@ -1,4 +1,5 @@ "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!" = "Якщо вам сподобалося %@, будь ласка, поставте свою оцінку. Це займає не більше однієї хвилини.\n Дякуємо за підтримку!"; "Rate %@" = "Оцінити %@"; +"Rate and review %@" = "Оцінити з відгуком %@"; "No, Thanks" = "Ні, дякую"; "Remind me later" = "Нагадати пізніше";