merged iphoen changes

This commit is contained in:
Ariel Manzur 2016-04-29 10:50:56 -03:00
parent 6d22e03b96
commit b50c00ff90
6 changed files with 532 additions and 102 deletions

View file

@ -1,9 +1,7 @@
#ifdef APPIRATER_ENABLED
/* /*
This file is part of Appirater. This file is part of Appirater.
Copyright (c) 2010, Arash Payan Copyright (c) 2012, Arash Payan
All rights reserved. All rights reserved.
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
@ -33,13 +31,17 @@
* *
* Created by Arash Payan on 9/5/09. * Created by Arash Payan on 9/5/09.
* http://arashpayan.com * http://arashpayan.com
* Copyright 2010 Arash Payan. All rights reserved. * Copyright 2012 Arash Payan. All rights reserved.
*/ */
#import "Appirater.h" #import "Appirater.h"
#import <SystemConfiguration/SCNetworkReachability.h> #import <SystemConfiguration/SCNetworkReachability.h>
#include <netinet/in.h> #include <netinet/in.h>
#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 kAppiraterFirstUseDate = @"kAppiraterFirstUseDate";
NSString *const kAppiraterUseCount = @"kAppiraterUseCount"; NSString *const kAppiraterUseCount = @"kAppiraterUseCount";
NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount"; NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount";
@ -49,18 +51,175 @@ NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate";
NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate"; NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate";
NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; 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 int app_id = 0; 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<AppiraterDelegate> _delegate;
#else
__weak static id<AppiraterDelegate> _delegate;
#endif
static BOOL _usesAnimation = TRUE;
static UIStatusBarStyle _statusBarStyle;
static BOOL _modalOpen = false;
static BOOL _alwaysUseMainBundle = NO;
@interface Appirater (hidden) @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; - (BOOL)connectedToNetwork;
+ (Appirater*)sharedInstance; + (Appirater*)sharedInstance;
- (void)showPromptWithChecks:(BOOL)withChecks
displayRateLaterButton:(BOOL)displayRateLaterButton;
- (void)showRatingAlert:(BOOL)displayRateLaterButton;
- (void)showRatingAlert; - (void)showRatingAlert;
- (BOOL)ratingAlertIsAppropriate;
- (BOOL)ratingConditionsHaveBeenMet; - (BOOL)ratingConditionsHaveBeenMet;
- (void)incrementUseCount; - (void)incrementUseCount;
- (void)hideRatingAlert;
@end @end
@implementation Appirater (hidden) @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<AppiraterDelegate>)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 { - (BOOL)connectedToNetwork {
// Create zero addy // Create zero addy
@ -73,7 +232,7 @@ static int app_id = 0;
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags; SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); Boolean didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability); CFRelease(defaultRouteReachability);
if (!didRetrieveFlags) if (!didRetrieveFlags)
@ -97,61 +256,110 @@ static int app_id = 0;
static Appirater *appirater = nil; static Appirater *appirater = nil;
if (appirater == nil) if (appirater == nil)
{ {
@synchronized(self) { static dispatch_once_t onceToken;
if (appirater == nil) { dispatch_once(&onceToken, ^{
appirater = [[Appirater alloc] init]; appirater = [[Appirater alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:@"UIApplicationWillResignActiveNotification" object:nil]; appirater.delegate = _delegate;
} [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:
} UIApplicationWillResignActiveNotification object:nil];
});
} }
return appirater; return appirater;
} }
- (void)showRatingAlert { - (void)showRatingAlert:(BOOL)displayRateLaterButton {
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE UIAlertView *alertView = nil;
message:APPIRATER_MESSAGE id <AppiraterDelegate> delegate = _delegate;
delegate:self
cancelButtonTitle:APPIRATER_CANCEL_BUTTON if(delegate && [delegate respondsToSelector:@selector(appiraterShouldDisplayAlert:)] && ![delegate appiraterShouldDisplayAlert:self]) {
otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil] autorelease]; 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; self.ratingAlert = alertView;
[alertView show]; [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 { - (BOOL)ratingConditionsHaveBeenMet {
if (APPIRATER_DEBUG) if (_debug)
return YES; return YES;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]]; NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]];
NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch]; NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_DAYS_UNTIL_PROMPT; NSTimeInterval timeUntilRate = 60 * 60 * 24 * _daysUntilPrompt;
if (timeSinceFirstLaunch < timeUntilRate) if (timeSinceFirstLaunch < timeUntilRate)
return NO; return NO;
// check if the app has been used enough // check if the app has been used enough
int useCount = [userDefaults integerForKey:kAppiraterUseCount]; NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount];
if (useCount <= APPIRATER_USES_UNTIL_PROMPT) if (useCount < _usesUntilPrompt)
return NO; return NO;
// check if the user has done enough significant events // check if the user has done enough significant events
int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; NSInteger 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?
if ([userDefaults boolForKey:kAppiraterDeclinedToRate])
return NO;
// has the user already rated the app?
if ([userDefaults boolForKey:kAppiraterRatedCurrentVersion])
return NO; return NO;
// if the user wanted to be reminded later, has enough time passed? // if the user wanted to be reminded later, has enough time passed?
NSDate *reminderRequestDate = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterReminderRequestDate]]; NSDate *reminderRequestDate = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterReminderRequestDate]];
NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate]; NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate];
NSTimeInterval timeUntilReminder = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING; NSTimeInterval timeUntilReminder = 60 * 60 * 24 * _timeBeforeReminding;
if (timeSinceReminderRequest < timeUntilReminder) if (timeSinceReminderRequest < timeUntilReminder)
return NO; return NO;
@ -171,7 +379,7 @@ static int app_id = 0;
[userDefaults setObject:version forKey:kAppiraterCurrentVersion]; [userDefaults setObject:version forKey:kAppiraterCurrentVersion];
} }
if (APPIRATER_DEBUG) if (_debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion); NSLog(@"APPIRATER Tracking version: %@", trackingVersion);
if ([trackingVersion isEqualToString:version]) if ([trackingVersion isEqualToString:version])
@ -185,11 +393,11 @@ static int app_id = 0;
} }
// increment the use count // increment the use count
int useCount = [userDefaults integerForKey:kAppiraterUseCount]; NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount];
useCount++; useCount++;
[userDefaults setInteger:useCount forKey:kAppiraterUseCount]; [userDefaults setInteger:useCount forKey:kAppiraterUseCount];
if (APPIRATER_DEBUG) if (_debug)
NSLog(@"APPIRATER Use count: %d", useCount); NSLog(@"APPIRATER Use count: %@", @(useCount));
} }
else else
{ {
@ -219,7 +427,7 @@ static int app_id = 0;
[userDefaults setObject:version forKey:kAppiraterCurrentVersion]; [userDefaults setObject:version forKey:kAppiraterCurrentVersion];
} }
if (APPIRATER_DEBUG) if (_debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion); NSLog(@"APPIRATER Tracking version: %@", trackingVersion);
if ([trackingVersion isEqualToString:version]) if ([trackingVersion isEqualToString:version])
@ -233,11 +441,11 @@ static int app_id = 0;
} }
// increment the significant event count // increment the significant event count
int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
sigEventCount++; sigEventCount++;
[userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount]; [userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount];
if (APPIRATER_DEBUG) if (_debug)
NSLog(@"APPIRATER Significant event count: %d", sigEventCount); NSLog(@"APPIRATER Significant event count: %@", @(sigEventCount));
} }
else else
{ {
@ -254,105 +462,214 @@ static int app_id = 0;
[userDefaults synchronize]; [userDefaults synchronize];
} }
@end - (void)incrementAndRate:(BOOL)canPromptForRating {
@interface Appirater ()
- (void)hideRatingAlert;
@end
@implementation Appirater
@synthesize ratingAlert;
- (void)incrementAndRate:(NSNumber*)_canPromptForRating {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self incrementUseCount]; [self incrementUseCount];
if ([_canPromptForRating boolValue] == YES && if (canPromptForRating &&
[self ratingConditionsHaveBeenMet] && [self ratingConditionsHaveBeenMet] &&
[self connectedToNetwork]) [self ratingAlertIsAppropriate])
{ {
[self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; dispatch_async(dispatch_get_main_queue(),
^{
[self showRatingAlert];
});
} }
[pool release];
} }
- (void)incrementSignificantEventAndRate:(NSNumber*)_canPromptForRating { - (void)incrementSignificantEventAndRate:(BOOL)canPromptForRating {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self incrementSignificantEventCount]; [self incrementSignificantEventCount];
if ([_canPromptForRating boolValue] == YES && if (canPromptForRating &&
[self ratingConditionsHaveBeenMet] && [self ratingConditionsHaveBeenMet] &&
[self connectedToNetwork]) [self ratingAlertIsAppropriate])
{ {
[self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; dispatch_async(dispatch_get_main_queue(),
^{
[self showRatingAlert];
});
} }
[pool release];
} }
+ (void)appLaunched:(int)p_app_id { - (BOOL)userHasDeclinedToRate {
app_id = p_app_id; 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]; [Appirater appLaunched:YES];
} }
#pragma GCC diagnostic pop
+ (void)appLaunched:(BOOL)canPromptForRating app_id:(int)p_app_id { + (void)appLaunched:(BOOL)canPromptForRating {
app_id = p_app_id; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; ^{
[NSThread detachNewThreadSelector:@selector(incrementAndRate:) Appirater *a = [Appirater sharedInstance];
toTarget:[Appirater sharedInstance] if (_debug) {
withObject:_canPromptForRating]; dispatch_async(dispatch_get_main_queue(),
[_canPromptForRating release]; ^{
[a showRatingAlert];
});
} else {
[a incrementAndRate:canPromptForRating];
}
});
} }
- (void)hideRatingAlert { - (void)hideRatingAlert {
if (self.ratingAlert.visible) { if (self.ratingAlert.visible) {
if (APPIRATER_DEBUG) if (_debug)
NSLog(@"APPIRATER Hiding Alert"); NSLog(@"APPIRATER Hiding Alert");
[self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO];
} }
} }
+ (void)appWillResignActive { + (void)appWillResignActive {
if (APPIRATER_DEBUG) if (_debug)
NSLog(@"APPIRATER appWillResignActive"); NSLog(@"APPIRATER appWillResignActive");
[[Appirater sharedInstance] hideRatingAlert]; [[Appirater sharedInstance] hideRatingAlert];
} }
+ (void)appEnteredForeground:(BOOL)canPromptForRating { + (void)appEnteredForeground:(BOOL)canPromptForRating {
NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
[NSThread detachNewThreadSelector:@selector(incrementAndRate:) ^{
toTarget:[Appirater sharedInstance] [[Appirater sharedInstance] incrementAndRate:canPromptForRating];
withObject:_canPromptForRating]; });
[_canPromptForRating release];
} }
+ (void)userDidSignificantEvent:(BOOL)canPromptForRating { + (void)userDidSignificantEvent:(BOOL)canPromptForRating {
NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
[NSThread detachNewThreadSelector:@selector(incrementSignificantEventAndRate:) ^{
toTarget:[Appirater sharedInstance] [[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating];
withObject:_canPromptForRating]; });
[_canPromptForRating release]; }
#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 { + (void)rateApp {
#if TARGET_IPHONE_SIMULATOR
NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page.");
#else
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", app_id]];
[userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
[userDefaults synchronize]; [userDefaults synchronize];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
//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 <AppiraterDelegate> 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 #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 clickedButtonAtIndex:(NSInteger)buttonIndex { - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
id <AppiraterDelegate> delegate = _delegate;
switch (buttonIndex) { switch (buttonIndex) {
case 0: case 0:
@ -360,24 +677,56 @@ static int app_id = 0;
// they don't want to rate it // they don't want to rate it
[userDefaults setBool:YES forKey:kAppiraterDeclinedToRate]; [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate];
[userDefaults synchronize]; [userDefaults synchronize];
if(delegate && [delegate respondsToSelector:@selector(appiraterDidDeclineToRate:)]){
[delegate appiraterDidDeclineToRate:self];
}
break; break;
} }
case 1: case 1:
{ {
// they want to rate it // they want to rate it
[Appirater rateApp]; [Appirater rateApp];
if(delegate&& [delegate respondsToSelector:@selector(appiraterDidOptToRate:)]){
[delegate appiraterDidOptToRate:self];
}
break; break;
} }
case 2: case 2:
// remind them later // remind them later
[userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate]; [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate];
[userDefaults synchronize]; [userDefaults synchronize];
if(delegate && [delegate respondsToSelector:@selector(appiraterDidOptToRemindLater:)]){
[delegate appiraterDidOptToRemindLater:self];
}
break; break;
default: default:
break; break;
} }
} }
@end //Delegate call from the StoreKit view.
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[Appirater closeModal];
}
#endif //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 <AppiraterDelegate> delegate = self.sharedInstance.delegate;
if ([delegate respondsToSelector:@selector(appiraterDidDismissModalView:animated:)]) {
[delegate appiraterDidDismissModalView:(Appirater *)self animated:usedAnimation];
}
}];
[self.class setStatusBarStyle:(UIStatusBarStyle)nil];
}
}
@end

View file

@ -0,0 +1,23 @@
//
// AppiraterDelegate.h
// Banana Stand
//
// Created by Robert Haining on 9/25/12.
// Copyright (c) 2012 News.me. All rights reserved.
//
#import <Foundation/Foundation.h>
@class Appirater;
@protocol AppiraterDelegate <NSObject>
@optional
-(BOOL)appiraterShouldDisplayAlert:(Appirater *)appirater;
-(void)appiraterDidDisplayAlert:(Appirater *)appirater;
-(void)appiraterDidDeclineToRate:(Appirater *)appirater;
-(void)appiraterDidOptToRate:(Appirater *)appirater;
-(void)appiraterDidOptToRemindLater:(Appirater *)appirater;
-(void)appiraterWillPresentModalView:(Appirater *)appirater animated:(BOOL)animated;
-(void)appiraterDidDismissModalView:(Appirater *)appirater animated:(BOOL)animated;
@end

View file

@ -14,6 +14,7 @@ iphone_lib = [
'in_app_store.mm', 'in_app_store.mm',
'icloud.mm', 'icloud.mm',
'Appirater.m', 'Appirater.m',
'ios.mm',
] ]
#env.Depends('#core/math/vector3.h', 'vector3_psp.h') #env.Depends('#core/math/vector3.h', 'vector3_psp.h')

20
platform/iphone/ios.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef IOS_H
#define IOS_H
#include "core/object.h"
class iOS : public Object {
OBJ_TYPE(iOS, Object);
static void _bind_methods();
public:
String get_rate_url(int p_app_id) const;
iOS();
};
#endif

34
platform/iphone/ios.mm Normal file
View file

@ -0,0 +1,34 @@
#include "ios.h"
#import <UIKit/UIKit.h>
void iOS::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_rate_url","app_id"),&iOS::get_rate_url);
};
String iOS::get_rate_url(int p_app_id) const {
String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID";
String templ_iOS8 = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
//ios7 before
String ret = templ;
// iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1)
{
ret = templ_iOS7;
}
// iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
ret = templ_iOS8;
}
ret = ret.replace("APP_ID", String::num(p_app_id));
return ret;
};
iOS::iOS() {};

View file

@ -46,6 +46,8 @@
#include "sem_iphone.h" #include "sem_iphone.h"
#include "ios.h"
int OSIPhone::get_video_driver_count() const { int OSIPhone::get_video_driver_count() const {
return 1; return 1;
@ -167,6 +169,7 @@ void OSIPhone::initialize(const VideoMode& p_desired,int p_video_driver,int p_au
Globals::get_singleton()->add_singleton(Globals::Singleton("ICloud", icloud)); Globals::get_singleton()->add_singleton(Globals::Singleton("ICloud", icloud));
//icloud->connect(); //icloud->connect();
#endif #endif
Globals::get_singleton()->add_singleton(Globals::Singleton("iOS", memnew(iOS)));
}; };
MainLoop *OSIPhone::get_main_loop() const { MainLoop *OSIPhone::get_main_loop() const {