diff -r 92b023095153 -r 402baa6d4f64 project_files/HedgewarsMobile/Classes/Appirater/Appirater.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/HedgewarsMobile/Classes/Appirater/Appirater.m Fri Nov 06 00:12:37 2015 +0100 @@ -0,0 +1,732 @@ +/* + This file is part of Appirater. + + Copyright (c) 2012, Arash Payan + All rights reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * Appirater.m + * appirater + * + * Created by Arash Payan on 9/5/09. + * http://arashpayan.com + * Copyright 2012 Arash Payan. All rights reserved. + */ + +#import "Appirater.h" +#import +#include + +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + +NSString *const kAppiraterFirstUseDate = @"kAppiraterFirstUseDate"; +NSString *const kAppiraterUseCount = @"kAppiraterUseCount"; +NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount"; +NSString *const kAppiraterCurrentVersion = @"kAppiraterCurrentVersion"; +NSString *const kAppiraterRatedCurrentVersion = @"kAppiraterRatedCurrentVersion"; +NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate"; +NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate"; + +NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; +NSString *templateReviewURLiOS7 = @"itms-apps://itunes.apple.com/app/idAPP_ID"; +NSString *templateReviewURLiOS8 = @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"; + +static NSString *_appId; +static double _daysUntilPrompt = 30; +static NSInteger _usesUntilPrompt = 20; +static NSInteger _significantEventsUntilPrompt = -1; +static double _timeBeforeReminding = 1; +static BOOL _debug = NO; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 + static id _delegate; +#else + __weak static id _delegate; +#endif +static BOOL _usesAnimation = TRUE; +static UIStatusBarStyle _statusBarStyle; +static BOOL _modalOpen = false; +static BOOL _alwaysUseMainBundle = 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 *alertRateLaterTitle; +- (BOOL)connectedToNetwork; ++ (Appirater*)sharedInstance; +- (void)showPromptWithChecks:(BOOL)withChecks + displayRateLaterButton:(BOOL)displayRateLaterButton; +- (void)showRatingAlert:(BOOL)displayRateLaterButton; +- (void)showRatingAlert; +- (BOOL)ratingAlertIsAppropriate; +- (BOOL)ratingConditionsHaveBeenMet; +- (void)incrementUseCount; +- (void)hideRatingAlert; +@end + +@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) setCustomAlertTitle:(NSString *)title +{ + [self sharedInstance].alertTitle = title; +} + ++ (void) setCustomAlertMessage:(NSString *)message +{ + [self sharedInstance].alertMessage = message; +} + ++ (void) setCustomAlertCancelButtonTitle:(NSString *)cancelTitle +{ + [self sharedInstance].alertCancelTitle = cancelTitle; +} + ++ (void) setCustomAlertRateButtonTitle:(NSString *)rateTitle +{ + [self sharedInstance].alertRateTitle = rateTitle; +} + ++ (void) setCustomAlertRateLaterButtonTitle:(NSString *)rateLaterTitle +{ + [self sharedInstance].alertRateLaterTitle = rateLaterTitle; +} + ++ (void) setDebug:(BOOL)debug { + _debug = debug; +} ++ (void)setDelegate:(id)delegate{ + _delegate = delegate; +} ++ (void)setUsesAnimation:(BOOL)animation { + _usesAnimation = animation; +} ++ (void)setOpenInAppStore:(BOOL)openInAppStore { + [Appirater sharedInstance].openInAppStore = openInAppStore; +} ++ (void)setStatusBarStyle:(UIStatusBarStyle)style { + _statusBarStyle = style; +} ++ (void)setModalOpen:(BOOL)open { + _modalOpen = open; +} ++ (void)setAlwaysUseMainBundle:(BOOL)alwaysUseMainBundle { + _alwaysUseMainBundle = alwaysUseMainBundle; +} + ++ (NSBundle *)bundle +{ + NSBundle *bundle; + + if (_alwaysUseMainBundle) { + bundle = [NSBundle mainBundle]; + } else { + NSURL *appiraterBundleURL = [[NSBundle mainBundle] URLForResource:@"Appirater" withExtension:@"bundle"]; + + if (appiraterBundleURL) { + // Appirater.bundle will likely only exist when used via CocoaPods + bundle = [NSBundle bundleWithURL:appiraterBundleURL]; + } else { + bundle = [NSBundle mainBundle]; + } + } + + return bundle; +} + +- (NSString *)alertTitle +{ + return _alertTitle ? _alertTitle : APPIRATER_MESSAGE_TITLE; +} + +- (NSString *)alertMessage +{ + return _alertMessage ? _alertMessage : APPIRATER_MESSAGE; +} + +- (NSString *)alertCancelTitle +{ + return _alertCancelTitle ? _alertCancelTitle : APPIRATER_CANCEL_BUTTON; +} + +- (NSString *)alertRateTitle +{ + return _alertRateTitle ? _alertRateTitle : APPIRATER_RATE_BUTTON; +} + +- (NSString *)alertRateLaterTitle +{ + return _alertRateLaterTitle ? _alertRateLaterTitle : APPIRATER_RATE_LATER; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (id)init { + self = [super init]; + if (self) { + if ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) { + self.openInAppStore = YES; + } else { + self.openInAppStore = NO; + } + } + + return self; +} + +- (BOOL)connectedToNetwork { + // Create zero addy + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + // Recover reachability flags + SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); + SCNetworkReachabilityFlags flags; + + Boolean didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); + CFRelease(defaultRouteReachability); + + if (!didRetrieveFlags) + { + NSLog(@"Error. Could not recover network reachability flags"); + return NO; + } + + BOOL isReachable = flags & kSCNetworkFlagsReachable; + BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired; + BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection; + + 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]; + + return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO; +} + ++ (Appirater*)sharedInstance { + static Appirater *appirater = nil; + if (appirater == nil) + { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + appirater = [[Appirater alloc] init]; + appirater.delegate = _delegate; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name: + UIApplicationWillResignActiveNotification object:nil]; + }); + } + + return appirater; +} + +- (void)showRatingAlert:(BOOL)displayRateLaterButton { + UIAlertView *alertView = nil; + id delegate = _delegate; + + if(delegate && [delegate respondsToSelector:@selector(appiraterShouldDisplayAlert:)] && ![delegate appiraterShouldDisplayAlert:self]) { + return; + } + + if (displayRateLaterButton) { + alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle + message:self.alertMessage + delegate:self + cancelButtonTitle:self.alertCancelTitle + otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil]; + } else { + alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle + message:self.alertMessage + delegate:self + cancelButtonTitle:self.alertCancelTitle + otherButtonTitles:self.alertRateTitle, nil]; + } + + self.ratingAlert = alertView; + [alertView show]; + + if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) { + [delegate appiraterDidDisplayAlert:self]; + } +} + +- (void)showRatingAlert +{ + [self showRatingAlert:true]; +} + +// is this an ok time to show the alert? (regardless of whether the rating conditions have been met) +// +// things checked here: +// * connectivity with network +// * whether user has rated before +// * whether user has declined to rate +// * whether rating alert is currently showing visibly +// things NOT checked here: +// * time since first launch +// * number of uses of app +// * number of significant events +// * time since last reminder +- (BOOL)ratingAlertIsAppropriate { + return ([self connectedToNetwork] + && ![self userHasDeclinedToRate] + && !self.ratingAlert.visible + && ![self userHasRatedCurrentVersion]); +} + +// have the rating conditions been met/earned? (regardless of whether this would be a moment when it's appropriate to show a new rating alert) +// +// things checked here: +// * time since first launch +// * number of uses of app +// * number of significant events +// * time since last reminder +// things NOT checked here: +// * connectivity with network +// * whether user has rated before +// * whether user has declined to rate +// * whether rating alert is currently showing visibly +- (BOOL)ratingConditionsHaveBeenMet { + 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 * _daysUntilPrompt; + if (timeSinceFirstLaunch < timeUntilRate) + return NO; + + // check if the app has been used enough + NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount]; + if (useCount < _usesUntilPrompt) + return NO; + + // check if the user has done enough significant events + NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; + if (sigEventCount < _significantEventsUntilPrompt) + return NO; + + // 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 * _timeBeforeReminding; + if (timeSinceReminderRequest < timeUntilReminder) + return NO; + + return YES; +} + +- (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]; + 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 the use count + NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount]; + useCount++; + [userDefaults setInteger:useCount forKey:kAppiraterUseCount]; + if (_debug) + NSLog(@"APPIRATER Use count: %@", @(useCount)); + } + else + { + // it's a new version of the app, so restart tracking + [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; + [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterFirstUseDate]; + [userDefaults setInteger:1 forKey:kAppiraterUseCount]; + [userDefaults setInteger:0 forKey:kAppiraterSignificantEventCount]; + [userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion]; + [userDefaults setBool:NO forKey:kAppiraterDeclinedToRate]; + [userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate]; + } + + [userDefaults synchronize]; +} + +- (void)incrementSignificantEventCount { + // 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 the significant event count + NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; + sigEventCount++; + [userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount]; + if (_debug) + NSLog(@"APPIRATER Significant event count: %@", @(sigEventCount)); + } + 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 setBool:NO forKey:kAppiraterRatedCurrentVersion]; + [userDefaults setBool:NO forKey:kAppiraterDeclinedToRate]; + [userDefaults setDouble:0 forKey:kAppiraterReminderRequestDate]; + } + + [userDefaults synchronize]; +} + +- (void)incrementAndRate:(BOOL)canPromptForRating { + [self incrementUseCount]; + + if (canPromptForRating && + [self ratingConditionsHaveBeenMet] && + [self ratingAlertIsAppropriate]) + { + dispatch_async(dispatch_get_main_queue(), + ^{ + [self showRatingAlert]; + }); + } +} + +- (void)incrementSignificantEventAndRate:(BOOL)canPromptForRating { + [self incrementSignificantEventCount]; + + if (canPromptForRating && + [self ratingConditionsHaveBeenMet] && + [self ratingAlertIsAppropriate]) + { + dispatch_async(dispatch_get_main_queue(), + ^{ + [self showRatingAlert]; + }); + } +} + +- (BOOL)userHasDeclinedToRate { + return [[NSUserDefaults standardUserDefaults] boolForKey:kAppiraterDeclinedToRate]; +} + +- (BOOL)userHasRatedCurrentVersion { + return [[NSUserDefaults standardUserDefaults] boolForKey:kAppiraterRatedCurrentVersion]; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-implementations" ++ (void)appLaunched { + [Appirater appLaunched:YES]; +} +#pragma GCC diagnostic pop + ++ (void)appLaunched:(BOOL)canPromptForRating { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + Appirater *a = [Appirater sharedInstance]; + if (_debug) { + dispatch_async(dispatch_get_main_queue(), + ^{ + [a showRatingAlert]; + }); + } else { + [a incrementAndRate:canPromptForRating]; + } + }); +} + +- (void)hideRatingAlert { + if (self.ratingAlert.visible) { + if (_debug) + NSLog(@"APPIRATER Hiding Alert"); + [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; + } +} + ++ (void)appWillResignActive { + if (_debug) + NSLog(@"APPIRATER appWillResignActive"); + [[Appirater sharedInstance] hideRatingAlert]; +} + ++ (void)appEnteredForeground:(BOOL)canPromptForRating { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + [[Appirater sharedInstance] incrementAndRate:canPromptForRating]; + }); +} + ++ (void)userDidSignificantEvent:(BOOL)canPromptForRating { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + [[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating]; + }); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-implementations" ++ (void)showPrompt { + [Appirater tryToShowPrompt]; +} +#pragma GCC diagnostic pop + ++ (void)tryToShowPrompt { + [[Appirater sharedInstance] showPromptWithChecks:true + displayRateLaterButton:true]; +} + ++ (void)forceShowPrompt:(BOOL)displayRateLaterButton { + [[Appirater sharedInstance] showPromptWithChecks:false + displayRateLaterButton:displayRateLaterButton]; +} + +- (void)showPromptWithChecks:(BOOL)withChecks + displayRateLaterButton:(BOOL)displayRateLaterButton { + if (withChecks == NO || [self ratingAlertIsAppropriate]) { + [self showRatingAlert:displayRateLaterButton]; + } +} + ++ (id)getRootViewController { + UIWindow *window = [[UIApplication sharedApplication] keyWindow]; + if (window.windowLevel != UIWindowLevelNormal) { + NSArray *windows = [[UIApplication sharedApplication] windows]; + for(window in windows) { + if (window.windowLevel == UIWindowLevelNormal) { + break; + } + } + } + + return [Appirater iterateSubViewsForViewController:window]; // iOS 8+ deep traverse +} + ++ (id)iterateSubViewsForViewController:(UIView *) parentView { + for (UIView *subView in [parentView subviews]) { + UIResponder *responder = [subView nextResponder]; + if([responder isKindOfClass:[UIViewController class]]) { + return [self topMostViewController: (UIViewController *) responder]; + } + id found = [Appirater iterateSubViewsForViewController:subView]; + if( nil != found) { + return found; + } + } + return nil; +} + ++ (UIViewController *) topMostViewController: (UIViewController *) controller { + BOOL isPresenting = NO; + do { + // this path is called only on iOS 6+, so -presentedViewController is fine here. + UIViewController *presented = [controller presentedViewController]; + isPresenting = presented != nil; + if(presented != nil) { + controller = presented; + } + + } while (isPresenting); + + return controller; +} + ++ (void)rateApp { + + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; + [userDefaults synchronize]; + + //Use the in-app StoreKit view if available (iOS 6) and imported. This works in the simulator. + if (![Appirater sharedInstance].openInAppStore && NSStringFromClass([SKStoreProductViewController class]) != nil) { + + SKStoreProductViewController *storeViewController = [[SKStoreProductViewController alloc] init]; + NSNumber *appId = [NSNumber numberWithInteger:_appId.integerValue]; + [storeViewController loadProductWithParameters:@{SKStoreProductParameterITunesItemIdentifier:appId} completionBlock:nil]; + storeViewController.delegate = self.sharedInstance; + + id delegate = self.sharedInstance.delegate; + if ([delegate respondsToSelector:@selector(appiraterWillPresentModalView:animated:)]) { + [delegate appiraterWillPresentModalView:self.sharedInstance animated:_usesAnimation]; + } + [[self getRootViewController] presentViewController:storeViewController animated:_usesAnimation completion:^{ + [self setModalOpen:YES]; + //Temporarily use a black status bar to match the StoreKit view. + [self setStatusBarStyle:[UIApplication sharedApplication].statusBarStyle]; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + [[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent animated:_usesAnimation]; +#endif + }]; + + //Use the standard openUrl method if StoreKit is unavailable. + } else { + + #if TARGET_IPHONE_SIMULATOR + NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page."); + #else + NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]]; + + // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131 + // Fixes condition @see https://github.com/arashpayan/appirater/issues/205 + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) { + reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]]; + } + // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182 + else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) + { + reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]]; + } + + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; + #endif + } +} + +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + id delegate = _delegate; + + switch (buttonIndex) { + case 0: + { + // they don't want to rate it + [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate]; + [userDefaults synchronize]; + if(delegate && [delegate respondsToSelector:@selector(appiraterDidDeclineToRate:)]){ + [delegate appiraterDidDeclineToRate:self]; + } + break; + } + case 1: + { + // they want to rate it + [Appirater rateApp]; + if(delegate&& [delegate respondsToSelector:@selector(appiraterDidOptToRate:)]){ + [delegate appiraterDidOptToRate:self]; + } + 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; + default: + break; + } +} + +//Delegate call from the StoreKit view. +- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController { + [Appirater closeModal]; +} + +//Close the in-app rating (StoreKit) view and restore the previous status bar style. ++ (void)closeModal { + if (_modalOpen) { + [[UIApplication sharedApplication]setStatusBarStyle:_statusBarStyle animated:_usesAnimation]; + BOOL usedAnimation = _usesAnimation; + [self setModalOpen:NO]; + + // get the top most controller (= the StoreKit Controller) and dismiss it + UIViewController *presentingController = [UIApplication sharedApplication].keyWindow.rootViewController; + presentingController = [self topMostViewController: presentingController]; + [presentingController dismissViewControllerAnimated:_usesAnimation completion:^{ + id delegate = self.sharedInstance.delegate; + if ([delegate respondsToSelector:@selector(appiraterDidDismissModalView:animated:)]) { + [delegate appiraterDidDismissModalView:(Appirater *)self animated:usedAnimation]; + } + }]; + [self.class setStatusBarStyle:(UIStatusBarStyle)nil]; + } +} + +@end