iRate.m 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  1. //
  2. // iRate.m
  3. //
  4. // Version 1.7.4
  5. //
  6. // Created by Nick Lockwood on 26/01/2011.
  7. // Copyright 2011 Charcoal Design
  8. //
  9. // Distributed under the permissive zlib license
  10. // Get the latest version from here:
  11. //
  12. // https://github.com/nicklockwood/iRate
  13. //
  14. // This software is provided 'as-is', without any express or implied
  15. // warranty. In no event will the authors be held liable for any damages
  16. // arising from the use of this software.
  17. //
  18. // Permission is granted to anyone to use this software for any purpose,
  19. // including commercial applications, and to alter it and redistribute it
  20. // freely, subject to the following restrictions:
  21. //
  22. // 1. The origin of this software must not be misrepresented; you must not
  23. // claim that you wrote the original software. If you use this software
  24. // in a product, an acknowledgment in the product documentation would be
  25. // appreciated but is not required.
  26. //
  27. // 2. Altered source versions must be plainly marked as such, and must not be
  28. // misrepresented as being the original software.
  29. //
  30. // 3. This notice may not be removed or altered from any source distribution.
  31. //
  32. #import "iRate.h"
  33. #if !VERSION_DMG
  34. #import <StoreKit/StoreKit.h>
  35. #endif
  36. #import <Availability.h>
  37. //#if !__has_feature(objc_arc)
  38. //#error This class requires automatic reference counting
  39. //#endif
  40. //#import "KMHomeWindowController.h"
  41. NSUInteger const iRateAppStoreGameGenreID = 6014;
  42. NSString *const iRateErrorDomain = @"iRateErrorDomain";
  43. static NSString *const iRateRatedVersionKey = @"iRateRatedVersionChecked";
  44. static NSString *const iRateDeclinedVersionKey = @"iRateDeclinedVersion";
  45. static NSString *const iRateLastRemindedKey = @"iRateLastReminded";
  46. static NSString *const iRateLastVersionUsedKey = @"iRateLastVersionUsed";
  47. static NSString *const iRateFirstUsedKey = @"iRateFirstUsed";
  48. static NSString *const iRateUseCountKey = @"iRateUseCount";
  49. static NSString *const iRateEventCountKey = @"iRateEventCount";
  50. static NSString *const iRateDeclinedCountKey = @"iRateDeclinedCount";
  51. static NSString *const iRateMacAppStoreBundleID = @"com.apple.appstore";
  52. static NSString *const iRateAppLookupURLFormat = @"http://itunes.apple.com/%@/lookup";
  53. static NSString *const iRateiOSAppStoreURLFormat = @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%u";
  54. static NSString *const iRateMacAppStoreURLFormat = @"macappstore://itunes.apple.com/app/id%u";
  55. #define SECONDS_IN_A_DAY 86400.0
  56. #define SECONDS_IN_A_WEEK 604800.0
  57. #define MAC_APP_STORE_REFRESH_DELAY 5.0
  58. #define REQUEST_TIMEOUT 60.0
  59. @interface iRate()
  60. @property (nonatomic, strong) id visibleAlert;
  61. @property (nonatomic, assign) int previousOrientation;
  62. @property (nonatomic, assign) BOOL currentlyChecking;
  63. @end
  64. @implementation iRate
  65. #pragma mark -
  66. #pragma mark Lifecycle methods
  67. + (void)load
  68. {
  69. [self performSelectorOnMainThread:@selector(sharedInstance) withObject:nil waitUntilDone:NO];
  70. }
  71. + (iRate *)sharedInstance
  72. {
  73. static iRate *sharedInstance = nil;
  74. if (sharedInstance == nil)
  75. {
  76. sharedInstance = [[iRate alloc] init];
  77. }
  78. return sharedInstance;
  79. }
  80. - (NSString *)localizedStringForKey:(NSString *)key withDefault:(NSString *)defaultString
  81. {
  82. static NSBundle *bundle = nil;
  83. if (bundle == nil)
  84. {
  85. NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"iRate" ofType:@"bundle"];
  86. bundle = [NSBundle bundleWithPath:bundlePath] ?: [NSBundle mainBundle];
  87. if (self.useAllAvailableLanguages)
  88. {
  89. //manually select the desired lproj folder
  90. for (NSString *language in [NSLocale preferredLanguages])
  91. {
  92. if ([[bundle localizations] containsObject:language])
  93. {
  94. bundlePath = [bundle pathForResource:language ofType:@"lproj"];
  95. bundle = [NSBundle bundleWithPath:bundlePath];
  96. break;
  97. }
  98. }
  99. }
  100. }
  101. defaultString = [bundle localizedStringForKey:key value:defaultString table:nil];
  102. return [[NSBundle mainBundle] localizedStringForKey:key value:defaultString table:nil];
  103. }
  104. - (iRate *)init
  105. {
  106. if ((self = [super init]))
  107. {
  108. #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
  109. //register for iphone application events
  110. if (&UIApplicationWillEnterForegroundNotification)
  111. {
  112. [[NSNotificationCenter defaultCenter] addObserver:self
  113. selector:@selector(applicationWillEnterForeground:)
  114. name:UIApplicationWillEnterForegroundNotification
  115. object:nil];
  116. }
  117. self.previousOrientation = [UIApplication sharedApplication].statusBarOrientation;
  118. [[NSNotificationCenter defaultCenter] addObserver:self
  119. selector:@selector(willRotate)
  120. name:UIDeviceOrientationDidChangeNotification
  121. object:nil];
  122. #endif
  123. //get country
  124. self.appStoreCountry = [(NSLocale *)[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
  125. //application version (use short version preferentially)
  126. self.applicationVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
  127. if ([self.applicationVersion length] == 0)
  128. {
  129. self.applicationVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
  130. }
  131. //localised application name
  132. self.applicationName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
  133. if ([self.applicationName length] == 0)
  134. {
  135. self.applicationName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey];
  136. }
  137. //bundle id
  138. self.applicationBundleID = [[NSBundle mainBundle] bundleIdentifier];
  139. //default settings
  140. self.useAllAvailableLanguages = YES;
  141. self.disableAlertViewResizing = NO;
  142. self.onlyPromptIfLatestVersion = YES;
  143. self.onlyPromptIfMainWindowIsAvailable = YES;
  144. self.displayAppUsingStorekitIfAvailable = YES;
  145. self.promptAgainForEachNewVersion = YES;
  146. self.promptAtLaunch = NO;
  147. self.usesUntilPrompt = 3;
  148. self.eventsUntilPrompt = -1;
  149. self.daysUntilPrompt = 4.0f;
  150. self.usesPerWeekForPrompt = 0.0f;
  151. self.remindPeriod = 7.0f;
  152. self.verboseLogging = NO;
  153. self.previewMode = NO;
  154. #ifdef DEBUG
  155. //enable verbose logging in debug mode
  156. self.verboseLogging = YES;
  157. #endif
  158. //app launched
  159. [self performSelectorOnMainThread:@selector(applicationLaunched) withObject:nil waitUntilDone:NO];
  160. }
  161. return self;
  162. }
  163. - (id<iRateDelegate>)delegate
  164. {
  165. if (_delegate == nil)
  166. {
  167. #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
  168. _delegate = (id<iRateDelegate>)[[UIApplication sharedApplication] delegate];
  169. #else
  170. _delegate = (id<iRateDelegate>)[[NSApplication sharedApplication] delegate];
  171. #endif
  172. }
  173. return _delegate;
  174. }
  175. - (NSString *)messageTitle
  176. {
  177. return [_messageTitle ?: [self localizedStringForKey:iRateMessageTitleKey withDefault:NSLocalizedString(@"Rate %@", nil)] stringByReplacingOccurrencesOfString:@"%@" withString:self.applicationName];
  178. }
  179. - (NSString *)message
  180. {
  181. NSString *message = _message;
  182. if (!message)
  183. {
  184. NSString *defaultMessage = [NSString stringWithFormat:NSLocalizedString(@"Share your love to %@! Please take one minute to give us a great review on the App Store.", nil),self.applicationName];
  185. message = (self.appStoreGenreID == iRateAppStoreGameGenreID)? [self localizedStringForKey:iRateGameMessageKey withDefault:defaultMessage]: [self localizedStringForKey:iRateAppMessageKey withDefault:defaultMessage];
  186. }
  187. return [message stringByReplacingOccurrencesOfString:@"%@" withString:self.applicationName];
  188. }
  189. - (NSString *)cancelButtonLabel
  190. {
  191. return _cancelButtonLabel ?: [self localizedStringForKey:iRateCancelButtonKey withDefault:NSLocalizedString(@"Give a suggestion", nil)];
  192. }
  193. - (NSString *)rateButtonLabel
  194. {
  195. return _rateButtonLabel ?: [self localizedStringForKey:iRateRateButtonKey withDefault:NSLocalizedString(@"Give a 5-Star", nil)];
  196. }
  197. - (NSString *)remindButtonLabel
  198. {
  199. return _remindButtonLabel ?: [self localizedStringForKey:iRateRemindButtonKey withDefault:NSLocalizedString(@"Remind me later", nil)];
  200. }
  201. - (NSURL *)ratingsURL
  202. {
  203. if (_ratingsURL)
  204. {
  205. return _ratingsURL;
  206. }
  207. if (!self.appStoreID)
  208. {
  209. NSLog(@"iRate could not find the App Store ID for this application. If the application is not intended for App Store release then you must specify a custom ratingsURL.");
  210. }
  211. #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
  212. return [NSURL URLWithString:[NSString stringWithFormat:iRateiOSAppStoreURLFormat, (unsigned int)self.appStoreID]];
  213. #else
  214. return [NSURL URLWithString:[NSString stringWithFormat:iRateMacAppStoreURLFormat, (unsigned int)self.appStoreID]];
  215. #endif
  216. }
  217. - (NSDate *)firstUsed
  218. {
  219. return [[NSUserDefaults standardUserDefaults] objectForKey:iRateFirstUsedKey];
  220. }
  221. - (void)setFirstUsed:(NSDate *)date
  222. {
  223. [[NSUserDefaults standardUserDefaults] setObject:date forKey:iRateFirstUsedKey];
  224. [[NSUserDefaults standardUserDefaults] synchronize];
  225. }
  226. - (NSDate *)lastReminded
  227. {
  228. return [[NSUserDefaults standardUserDefaults] objectForKey:iRateLastRemindedKey];
  229. }
  230. - (void)setLastReminded:(NSDate *)date
  231. {
  232. [[NSUserDefaults standardUserDefaults] setObject:date forKey:iRateLastRemindedKey];
  233. [[NSUserDefaults standardUserDefaults] synchronize];
  234. }
  235. - (NSUInteger)usesCount
  236. {
  237. return [[NSUserDefaults standardUserDefaults] integerForKey:iRateUseCountKey];
  238. }
  239. - (void)setUsesCount:(NSUInteger)count
  240. {
  241. [[NSUserDefaults standardUserDefaults] setInteger:count forKey:iRateUseCountKey];
  242. [[NSUserDefaults standardUserDefaults] synchronize];
  243. }
  244. - (NSUInteger)eventCount;
  245. {
  246. return [[NSUserDefaults standardUserDefaults] integerForKey:iRateEventCountKey];
  247. }
  248. - (void)setEventCount:(NSUInteger)count
  249. {
  250. [[NSUserDefaults standardUserDefaults] setInteger:count forKey:iRateEventCountKey];
  251. [[NSUserDefaults standardUserDefaults] synchronize];
  252. }
  253. - (NSUInteger)declinedCount
  254. {
  255. return [[NSUserDefaults standardUserDefaults] integerForKey:iRateDeclinedCountKey];
  256. }
  257. - (void)setDeclinedCount:(NSUInteger)count
  258. {
  259. [[NSUserDefaults standardUserDefaults] setInteger:count forKey:iRateDeclinedCountKey];
  260. [[NSUserDefaults standardUserDefaults] synchronize];
  261. }
  262. - (float)usesPerWeek
  263. {
  264. return (float)self.usesCount / ([[NSDate date] timeIntervalSinceDate:self.firstUsed] / SECONDS_IN_A_WEEK);
  265. }
  266. - (BOOL)declinedThisVersion
  267. {
  268. return [[[NSUserDefaults standardUserDefaults] objectForKey:iRateDeclinedVersionKey] isEqualToString:self.applicationVersion];
  269. }
  270. - (void)setDeclinedThisVersion:(BOOL)declined
  271. {
  272. [[NSUserDefaults standardUserDefaults] setObject:(declined? self.applicationVersion: nil) forKey:iRateDeclinedVersionKey];
  273. [[NSUserDefaults standardUserDefaults] synchronize];
  274. }
  275. - (BOOL)declinedAnyVersion
  276. {
  277. return [[[NSUserDefaults standardUserDefaults] objectForKey:iRateDeclinedVersionKey] length];
  278. }
  279. - (BOOL)ratedThisVersion
  280. {
  281. return [[[NSUserDefaults standardUserDefaults] objectForKey:iRateRatedVersionKey] isEqualToString:self.applicationVersion];
  282. }
  283. - (void)setRatedThisVersion:(BOOL)rated
  284. {
  285. [[NSUserDefaults standardUserDefaults] setObject:(rated? self.applicationVersion: nil) forKey:iRateRatedVersionKey];
  286. [[NSUserDefaults standardUserDefaults] synchronize];
  287. }
  288. - (BOOL)ratedAnyVersion
  289. {
  290. return [[[NSUserDefaults standardUserDefaults] objectForKey:iRateRatedVersionKey] length];
  291. }
  292. - (void)dealloc
  293. {
  294. [[NSNotificationCenter defaultCenter] removeObserver:self];
  295. }
  296. #pragma mark -
  297. #pragma mark Methods
  298. - (void)incrementUseCount
  299. {
  300. self.usesCount ++;
  301. }
  302. - (void)incrementEventCount
  303. {
  304. self.eventCount ++;
  305. }
  306. - (BOOL)shouldPromptForRating
  307. {
  308. //preview mode?
  309. if (self.previewMode)
  310. {
  311. NSLog(@"iRate preview mode is enabled - make sure you disable this for release");
  312. return YES;
  313. }
  314. //check if we've rated this version
  315. else if (self.ratedThisVersion)
  316. {
  317. if (self.verboseLogging)
  318. {
  319. NSLog(@"iRate did not prompt for rating because the user has already rated this version");
  320. }
  321. return NO;
  322. }
  323. //check if we've rated any version
  324. else if (!self.promptAgainForEachNewVersion && self.ratedAnyVersion)
  325. {
  326. if (self.verboseLogging)
  327. {
  328. NSLog(@"iRate did not prompt for rating because the user has already rated this app, and promptAgainForEachNewVersion is disabled");
  329. }
  330. return NO;
  331. }
  332. //check if we've declined to rate this version
  333. else if (self.declinedThisVersion)
  334. {
  335. if (self.verboseLogging)
  336. {
  337. NSLog(@"iRate did not prompt for rating because the user has declined to rate this version");
  338. }
  339. return NO;
  340. }
  341. //check for first launch
  342. else if ((self.daysUntilPrompt > 0.0f || self.usesPerWeekForPrompt) && self.firstUsed == nil)
  343. {
  344. if (self.verboseLogging)
  345. {
  346. NSLog(@"iRate did not prompt for rating because this is the first time the app has been launched");
  347. }
  348. return NO;
  349. }
  350. //check how long we've been using this version
  351. else if ([[NSDate date] timeIntervalSinceDate:self.firstUsed] < self.daysUntilPrompt * SECONDS_IN_A_DAY)
  352. {
  353. if (self.verboseLogging)
  354. {
  355. NSLog(@"iRate did not prompt for rating because the app was first used less than %g days ago", self.daysUntilPrompt);
  356. }
  357. return NO;
  358. }
  359. //check how many times we've used it and the number of significant events
  360. // else if (self.usesCount < self.usesUntilPrompt && self.eventCount < self.eventsUntilPrompt)
  361. else if (self.usesCount < self.usesUntilPrompt)
  362. {
  363. if (self.verboseLogging)
  364. {
  365. NSLog(@"iRate did not prompt for rating because the app has only been used %i times and only %i events have been logged", (int)self.usesCount, (int)self.eventCount);
  366. }
  367. return NO;
  368. }
  369. // //check if usage frequency is high enough
  370. // else if (self.usesPerWeek < self.usesPerWeekForPrompt)
  371. // {
  372. // if (self.verboseLogging)
  373. // {
  374. // NSLog(@"iRate did not prompt for rating because the app has only been used %g times per week on average since it was installed", self.usesPerWeek);
  375. // }
  376. // return NO;
  377. // }
  378. //check if within the reminder period
  379. else if (self.lastReminded != nil && [[NSDate date] timeIntervalSinceDate:self.lastReminded] < self.remindPeriod * SECONDS_IN_A_DAY)
  380. {
  381. if (self.verboseLogging)
  382. {
  383. NSLog(@"iRate did not prompt for rating because the user last asked to be reminded less than %g days ago", self.remindPeriod);
  384. }
  385. return NO;
  386. }
  387. //lets prompt!
  388. return YES;
  389. }
  390. - (NSString *)valueForKey:(NSString *)key inJSON:(NSString *)json
  391. {
  392. NSRange keyRange = [json rangeOfString:[NSString stringWithFormat:@"\"%@\"", key]];
  393. if (keyRange.location != NSNotFound)
  394. {
  395. NSInteger start = keyRange.location + keyRange.length;
  396. NSRange valueStart = [json rangeOfString:@":" options:0 range:NSMakeRange(start, [json length] - start)];
  397. if (valueStart.location != NSNotFound)
  398. {
  399. start = valueStart.location + 1;
  400. NSRange valueEnd = [json rangeOfString:@"," options:0 range:NSMakeRange(start, [json length] - start)];
  401. if (valueEnd.location != NSNotFound)
  402. {
  403. NSString *value = [json substringWithRange:NSMakeRange(start, valueEnd.location - start)];
  404. value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  405. while ([value hasPrefix:@"\""] && ![value hasSuffix:@"\""])
  406. {
  407. if (valueEnd.location == NSNotFound)
  408. {
  409. break;
  410. }
  411. NSInteger newStart = valueEnd.location + 1;
  412. valueEnd = [json rangeOfString:@"," options:0 range:NSMakeRange(newStart, [json length] - newStart)];
  413. value = [json substringWithRange:NSMakeRange(start, valueEnd.location - start)];
  414. value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  415. }
  416. value = [value stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\""]];
  417. value = [value stringByReplacingOccurrencesOfString:@"\\\\" withString:@"\\"];
  418. value = [value stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
  419. value = [value stringByReplacingOccurrencesOfString:@"\\\"" withString:@"\""];
  420. value = [value stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
  421. value = [value stringByReplacingOccurrencesOfString:@"\\r" withString:@"\r"];
  422. value = [value stringByReplacingOccurrencesOfString:@"\\t" withString:@"\t"];
  423. value = [value stringByReplacingOccurrencesOfString:@"\\f" withString:@"\f"];
  424. value = [value stringByReplacingOccurrencesOfString:@"\\b" withString:@"\f"];
  425. while (YES)
  426. {
  427. NSRange unicode = [value rangeOfString:@"\\u"];
  428. if (unicode.location == NSNotFound)
  429. {
  430. break;
  431. }
  432. uint32_t c = 0;
  433. NSString *hex = [value substringWithRange:NSMakeRange(unicode.location + 2, 4)];
  434. NSScanner *scanner = [NSScanner scannerWithString:hex];
  435. [scanner scanHexInt:&c];
  436. if (c <= 0xffff)
  437. {
  438. value = [value stringByReplacingCharactersInRange:NSMakeRange(unicode.location, 6) withString:[NSString stringWithFormat:@"%C", (unichar)c]];
  439. }
  440. else
  441. {
  442. //convert character to surrogate pair
  443. uint16_t x = (uint16_t)c;
  444. uint16_t u = (c >> 16) & ((1 << 5) - 1);
  445. uint16_t w = (uint16_t)u - 1;
  446. unichar high = 0xd800 | (w << 6) | x >> 10;
  447. unichar low = (uint16_t)(0xdc00 | (x & ((1 << 10) - 1)));
  448. value = [value stringByReplacingCharactersInRange:NSMakeRange(unicode.location, 6) withString:[NSString stringWithFormat:@"%C%C", high, low]];
  449. }
  450. }
  451. return value;
  452. }
  453. }
  454. }
  455. return nil;
  456. }
  457. - (void)setAppStoreIDOnMainThread:(NSString *)appStoreIDString
  458. {
  459. self.appStoreID = (NSUInteger)[appStoreIDString longLongValue];
  460. }
  461. - (void)connectionSucceeded
  462. {
  463. //no longer checking
  464. self.currentlyChecking = NO;
  465. if ([self ratingConditionsHaveBeenMet]) {
  466. //confirm with delegate
  467. if ([self.delegate respondsToSelector:@selector(iRateShouldPromptForRating)])
  468. {
  469. if (![self.delegate iRateShouldPromptForRating])
  470. {
  471. if (self.verboseLogging)
  472. {
  473. NSLog(@"iRate did not display the rating prompt because the iRateShouldPromptForRating delegate method returned NO");
  474. }
  475. return;
  476. }
  477. }
  478. //prompt user
  479. [self promptForRating];
  480. }
  481. }
  482. - (BOOL)ratingConditionsHaveBeenMet {
  483. // check if the app has been used enough
  484. NSInteger useCount = [self usesCount];
  485. if (useCount <= APPIRATER_USES_UNTIL_PROMPT) {
  486. return NO;
  487. } else if ((useCount % 5) == 0) {
  488. // Apple审核被拒,网页端显示 Free Download 按钮,每开10个文档后检测一次
  489. // [[KMHomeWindowController homeWindow] checkDefaultOpen];
  490. }
  491. NSDate *dateOfFirstLaunch = [self firstUsed];
  492. NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
  493. NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_DAYS_UNTIL_PROMPT;
  494. if (timeSinceFirstLaunch < timeUntilRate)
  495. return NO;
  496. // has the user previously declined to rate this version of the app?
  497. NSInteger declinedCount = [self declinedCount];
  498. if (declinedCount > APPIRATER_PROMPT_TIMES_UNTIL_REJECT)
  499. return NO;
  500. // has the user already rated the app?
  501. if (self.ratedThisVersion)
  502. return NO;
  503. // if the user wanted to be reminded later, has enough time passed?
  504. NSDate *reminderRequestDate = [self lastReminded];
  505. NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate];
  506. NSTimeInterval timeUntilReminder = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING;
  507. if (timeSinceReminderRequest < timeUntilReminder)
  508. return NO;
  509. return YES;
  510. }
  511. - (void)connectionError:(NSError *)error
  512. {
  513. //no longer checking
  514. self.currentlyChecking = NO;
  515. //log the error
  516. if (error)
  517. {
  518. NSLog(@"iRate rating process failed because: %@", [error localizedDescription]);
  519. }
  520. else
  521. {
  522. NSLog(@"iRate rating process failed because an unknown error occured");
  523. }
  524. //could not connect
  525. if ([self.delegate respondsToSelector:@selector(iRateCouldNotConnectToAppStore:)])
  526. {
  527. [self.delegate iRateCouldNotConnectToAppStore:error];
  528. }
  529. }
  530. - (void)checkForConnectivityInBackground
  531. {
  532. @synchronized (self)
  533. {
  534. @autoreleasepool
  535. {
  536. //first check iTunes
  537. NSString *iTunesServiceURL = [NSString stringWithFormat:iRateAppLookupURLFormat, self.appStoreCountry];
  538. if (self.appStoreID)
  539. {
  540. iTunesServiceURL = [iTunesServiceURL stringByAppendingFormat:@"?id=%u", (unsigned int)self.appStoreID];
  541. }
  542. else
  543. {
  544. iTunesServiceURL = [iTunesServiceURL stringByAppendingFormat:@"?bundleId=%@", self.applicationBundleID];
  545. }
  546. if (self.verboseLogging)
  547. {
  548. NSLog(@"iRate is checking %@ to retrieve the App Store details...", iTunesServiceURL);
  549. }
  550. NSError *error = nil;
  551. NSURLResponse *response = nil;
  552. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:iTunesServiceURL] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:REQUEST_TIMEOUT];
  553. NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
  554. if (data)
  555. {
  556. //convert to string
  557. NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  558. //check bundle ID matches
  559. NSString *bundleID = [self valueForKey:@"bundleId" inJSON:json];
  560. if (bundleID)
  561. {
  562. if ([bundleID isEqualToString:self.applicationBundleID])
  563. {
  564. //get genre
  565. if (self.appStoreGenreID == 0)
  566. {
  567. _appStoreGenreID = [[self valueForKey:@"primaryGenreId" inJSON:json] integerValue];
  568. }
  569. //get app id
  570. if (!self.appStoreID)
  571. {
  572. NSString *appStoreIDString = [self valueForKey:@"trackId" inJSON:json];
  573. [self performSelectorOnMainThread:@selector(setAppStoreIDOnMainThread:) withObject:appStoreIDString waitUntilDone:YES];
  574. if (self.verboseLogging)
  575. {
  576. NSLog(@"iRate found the app on iTunes. The App Store ID is %@", appStoreIDString);
  577. }
  578. }
  579. //check version
  580. if (self.onlyPromptIfLatestVersion && !self.previewMode)
  581. {
  582. NSString *latestVersion = [self valueForKey:@"version" inJSON:json];
  583. if ([latestVersion compare:self.applicationVersion options:NSNumericSearch] == NSOrderedDescending)
  584. {
  585. if (self.verboseLogging)
  586. {
  587. NSLog(@"iRate found that the installed application version (%@) is not the latest version on the App Store, which is %@",
  588. self.applicationVersion, latestVersion);
  589. }
  590. error = [NSError errorWithDomain:iRateErrorDomain code:iRateErrorApplicationIsNotLatestVersion userInfo:@{NSLocalizedDescriptionKey: @"Installed app is not the latest version available"}];
  591. }
  592. }
  593. }
  594. else
  595. {
  596. if (self.verboseLogging)
  597. {
  598. NSLog(@"iRate found that the application bundle ID (%@) does not match the bundle ID of the app found on iTunes (%@) with the specified App Store ID (%i)", self.applicationBundleID, bundleID, (int)self.appStoreID);
  599. }
  600. error = [NSError errorWithDomain:iRateErrorDomain code:iRateErrorBundleIdDoesNotMatchAppStore userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Application bundle ID does not match expected value of %@", bundleID]}];
  601. }
  602. }
  603. else if (self.appStoreID || !self.ratingsURL)
  604. {
  605. if (self.verboseLogging)
  606. {
  607. NSLog(@"iRate could not find this application on iTunes. If your app is not intended for App Store release then you must specify a custom ratingsURL. If this is the first release of your application then it's not a problem that it cannot be found on the store yet");
  608. }
  609. error = [NSError errorWithDomain:iRateErrorDomain
  610. code:iRateErrorApplicationNotFoundOnAppStore
  611. userInfo:@{NSLocalizedDescriptionKey: @"The application could not be found on the App Store."}];
  612. }
  613. else if (!self.appStoreID && self.verboseLogging)
  614. {
  615. NSLog(@"iRate could not find your app on iTunes. If your app is not yet on the store or is not intended for App Store release then don't worry about this");
  616. }
  617. }
  618. if (error && !(error.code == EPERM && [error.domain isEqualToString:NSPOSIXErrorDomain] && self.appStoreID))
  619. {
  620. [self performSelectorOnMainThread:@selector(connectionError:) withObject:error waitUntilDone:YES];
  621. }
  622. else if (self.appStoreID || self.previewMode)
  623. {
  624. //show prompt
  625. [self performSelectorOnMainThread:@selector(connectionSucceeded) withObject:nil waitUntilDone:YES];
  626. }
  627. }
  628. }
  629. }
  630. - (void)promptIfNetworkAvailable
  631. {
  632. if (!self.currentlyChecking)
  633. {
  634. self.currentlyChecking = YES;
  635. [self performSelectorInBackground:@selector(checkForConnectivityInBackground) withObject:nil];
  636. }
  637. }
  638. - (void)promptForRating
  639. {
  640. if (!self.visibleAlert)
  641. {
  642. #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
  643. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:self.messageTitle
  644. message:self.message
  645. delegate:(id <UIAlertViewDelegate>)self
  646. cancelButtonTitle:[self.cancelButtonLabel length] ? self.cancelButtonLabel: nil
  647. otherButtonTitles:self.rateButtonLabel, nil];
  648. // if ([self.remindButtonLabel length])
  649. // {
  650. // [alert addButtonWithTitle:self.remindButtonLabel];
  651. // }
  652. self.visibleAlert = alert;
  653. [self.visibleAlert show];
  654. #else
  655. //only show when main window is available
  656. if (self.onlyPromptIfMainWindowIsAvailable && ![[NSApplication sharedApplication] mainWindow])
  657. {
  658. [self performSelector:@selector(promptForRating) withObject:nil afterDelay:0.5];
  659. return;
  660. }
  661. self.visibleAlert = [NSAlert alertWithMessageText:self.messageTitle
  662. defaultButton:self.rateButtonLabel
  663. alternateButton:self.cancelButtonLabel
  664. otherButton:nil
  665. informativeTextWithFormat:@"%@", self.message];
  666. // if ([self.remindButtonLabel length])
  667. // {
  668. // [self.visibleAlert addButtonWithTitle:self.remindButtonLabel];
  669. // }
  670. [self.visibleAlert beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
  671. modalDelegate:self
  672. didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
  673. contextInfo:nil];
  674. #endif
  675. //inform about prompt
  676. if ([self.delegate respondsToSelector:@selector(iRateDidPromptForRating)])
  677. {
  678. [self.delegate iRateDidPromptForRating];
  679. }
  680. }
  681. }
  682. - (void)applicationLaunched
  683. {
  684. //check if this is a new version
  685. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  686. if (![[defaults objectForKey:iRateLastVersionUsedKey] isEqualToString:self.applicationVersion])
  687. {
  688. // has the user already rated the app?
  689. if (self.ratedAnyVersion)
  690. {
  691. NSDate *dateOfFirstLaunch = [self firstUsed];
  692. NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
  693. NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING * (APPIRATER_PROMPT_TIMES_UNTIL_REJECT * 3);
  694. if (timeSinceFirstLaunch >= timeUntilRate)
  695. {
  696. [defaults removeObjectForKey:iRateRatedVersionKey];
  697. [defaults setObject:[NSDate date] forKey:iRateFirstUsedKey];
  698. }
  699. } else {
  700. [defaults setObject:[NSDate date] forKey:iRateFirstUsedKey];
  701. // has the user previously declined to rate this version of the app?
  702. NSInteger declinedCount = [self declinedCount];
  703. if (declinedCount > APPIRATER_PROMPT_TIMES_UNTIL_REJECT)
  704. {
  705. NSDate *dateOfFirstLaunch = [self lastReminded];
  706. NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
  707. NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING * (APPIRATER_DAYS_UNTIL_PROMPT * 3);
  708. if (timeSinceFirstLaunch >= timeUntilRate)
  709. {
  710. [self setDeclinedCount:0];
  711. [defaults setObject:nil forKey:iRateLastRemindedKey];
  712. }
  713. } else {
  714. [defaults setObject:nil forKey:iRateLastRemindedKey];
  715. }
  716. }
  717. //reset counts
  718. [defaults setObject:self.applicationVersion forKey:iRateLastVersionUsedKey];
  719. [defaults setInteger:0 forKey:iRateUseCountKey];
  720. [defaults setInteger:0 forKey:iRateEventCountKey];
  721. [defaults synchronize];
  722. self.declinedThisVersion = NO;
  723. //inform about app update
  724. if ([self.delegate respondsToSelector:@selector(iRateDidDetectAppUpdate)])
  725. {
  726. [self.delegate iRateDidDetectAppUpdate];
  727. }
  728. }
  729. [self incrementUseCount];
  730. if (self.promptAtLaunch && [self shouldPromptForRating])
  731. {
  732. [self promptIfNetworkAvailable];
  733. }
  734. }
  735. - (void)appLaunched {
  736. [self incrementUseCount];
  737. if ([self shouldPromptForRating])
  738. {
  739. [self promptIfNetworkAvailable];
  740. }
  741. }
  742. #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
  743. - (void)applicationWillEnterForeground:(NSNotification *)notification
  744. {
  745. if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
  746. {
  747. [self incrementUseCount];
  748. if (self.promptAtLaunch && [self shouldPromptForRating])
  749. {
  750. [self promptIfNetworkAvailable];
  751. }
  752. }
  753. }
  754. #endif
  755. #pragma mark -
  756. #pragma mark UIAlertView methods
  757. #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
  758. - (void)openRatingsPageInAppStore
  759. {
  760. if (_displayAppUsingStorekitIfAvailable && [SKStoreProductViewController class])
  761. {
  762. if (self.verboseLogging)
  763. {
  764. NSLog(@"iRate will attempt to open the StoreKit in-app product page using the following app store ID: %i", self.appStoreID);
  765. }
  766. //create store view controller
  767. SKStoreProductViewController *productController = [[SKStoreProductViewController alloc] init];
  768. productController.delegate = (id<SKStoreProductViewControllerDelegate>)self;
  769. //load product details
  770. NSDictionary *productParameters = @{SKStoreProductParameterITunesItemIdentifier: [@(_appStoreID) description]};
  771. [productController loadProductWithParameters:productParameters completionBlock:^(BOOL result, NSError *error) {
  772. if (!result)
  773. {
  774. //log the error
  775. if (error)
  776. {
  777. NSLog(@"iRate rating process failed because: %@", [error localizedDescription]);
  778. }
  779. else
  780. {
  781. NSLog(@"iRate rating process failed because an unknown error occured");
  782. }
  783. self.ratedThisVersion = NO;
  784. if ([self.delegate respondsToSelector:@selector(iRateCouldNotConnectToAppStore:)])
  785. {
  786. [self.delegate iRateCouldNotConnectToAppStore:error];
  787. }
  788. }
  789. }];
  790. //get root view controller
  791. UIViewController *rootViewController = nil;
  792. id appDelegate = [[UIApplication sharedApplication] delegate];
  793. if ([appDelegate respondsToSelector:@selector(viewController)])
  794. {
  795. rootViewController = [appDelegate valueForKey:@"viewController"];
  796. }
  797. if (!rootViewController && [appDelegate respondsToSelector:@selector(window)])
  798. {
  799. UIWindow *window = [appDelegate valueForKey:@"window"];
  800. rootViewController = window.rootViewController;
  801. }
  802. if (!rootViewController)
  803. {
  804. UIWindow *window = [UIApplication sharedApplication].keyWindow;
  805. rootViewController = window.rootViewController;
  806. }
  807. if (!rootViewController)
  808. {
  809. if (self.verboseLogging)
  810. {
  811. NSLog(@"iRate couldn't find root view controller from which to display StoreKit product page");
  812. }
  813. }
  814. else
  815. {
  816. while (rootViewController.presentedViewController)
  817. {
  818. rootViewController = rootViewController.presentedViewController;
  819. }
  820. //present product view controller
  821. [rootViewController presentViewController:productController animated:YES completion:nil];
  822. if ([self.delegate respondsToSelector:@selector(iRateDidPresentStoreKitModal)])
  823. {
  824. [self.delegate iRateDidPresentStoreKitModal];
  825. }
  826. return;
  827. }
  828. }
  829. if (self.verboseLogging)
  830. {
  831. NSLog(@"iRate will open the App Store ratings page using the following URL: %@", self.ratingsURL);
  832. }
  833. [[UIApplication sharedApplication] openURL:self.ratingsURL];
  834. }
  835. - (void)productViewControllerDidFinish:(SKStoreProductViewController *)controller
  836. {
  837. [controller.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
  838. if ([self.delegate respondsToSelector:@selector(iRateDidDismissStoreKitModal)])
  839. {
  840. [self.delegate iRateDidDismissStoreKitModal];
  841. }
  842. }
  843. - (void)resizeAlertView:(UIAlertView *)alertView
  844. {
  845. if (!self.disableAlertViewResizing)
  846. {
  847. NSInteger imageCount = 0;
  848. CGFloat offset = 0.0f;
  849. CGFloat messageOffset = 0.0f;
  850. for (UIView *view in alertView.subviews)
  851. {
  852. CGRect frame = view.frame;
  853. if ([view isKindOfClass:[UILabel class]])
  854. {
  855. UILabel *label = (UILabel *)view;
  856. if ([label.text isEqualToString:alertView.title])
  857. {
  858. [label sizeToFit];
  859. offset = label.frame.size.height - fmax(0.0f, 45.f - label.frame.size.height);
  860. if (label.frame.size.height > frame.size.height)
  861. {
  862. offset = messageOffset = label.frame.size.height - frame.size.height;
  863. frame.size.height = label.frame.size.height;
  864. }
  865. }
  866. else if ([label.text isEqualToString:alertView.message])
  867. {
  868. label.lineBreakMode = NSLineBreakByWordWrapping;
  869. label.numberOfLines = 0;
  870. label.alpha = 1.0f;
  871. [label sizeToFit];
  872. offset += label.frame.size.height - frame.size.height;
  873. frame.origin.y += messageOffset;
  874. frame.size.height = label.frame.size.height;
  875. }
  876. }
  877. else if ([view isKindOfClass:[UITextView class]])
  878. {
  879. view.alpha = 0.0f;
  880. }
  881. else if ([view isKindOfClass:[UIImageView class]])
  882. {
  883. if (imageCount++ > 0)
  884. {
  885. view.alpha = 0.0f;
  886. }
  887. }
  888. else if ([view isKindOfClass:[UIControl class]])
  889. {
  890. frame.origin.y += offset;
  891. }
  892. view.frame = frame;
  893. }
  894. CGRect frame = alertView.frame;
  895. frame.origin.y -= roundf(offset/2.0f);
  896. frame.size.height += offset;
  897. alertView.frame = frame;
  898. }
  899. }
  900. - (void)willRotate
  901. {
  902. [self performSelectorOnMainThread:@selector(didRotate) withObject:nil waitUntilDone:NO];
  903. }
  904. - (void)didRotate
  905. {
  906. if (self.previousOrientation != [UIApplication sharedApplication].statusBarOrientation)
  907. {
  908. self.previousOrientation = [UIApplication sharedApplication].statusBarOrientation;
  909. [self resizeAlertView:self.visibleAlert];
  910. }
  911. }
  912. - (void)willPresentAlertView:(UIAlertView *)alertView
  913. {
  914. [self resizeAlertView:alertView];
  915. }
  916. - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
  917. {
  918. if (buttonIndex == alertView.cancelButtonIndex)
  919. {
  920. //ignore this version
  921. self.declinedThisVersion = YES;
  922. //log event
  923. if ([self.delegate respondsToSelector:@selector(iRateUserDidDeclineToRateApp)])
  924. {
  925. [self.delegate iRateUserDidDeclineToRateApp];
  926. }
  927. }
  928. else if (([self.cancelButtonLabel length] && buttonIndex == 2) ||
  929. ([self.cancelButtonLabel length] == 0 && buttonIndex == 1))
  930. {
  931. //remind later
  932. self.lastReminded = [NSDate date];
  933. //log event
  934. if ([self.delegate respondsToSelector:@selector(iRateUserDidRequestReminderToRateApp)])
  935. {
  936. [self.delegate iRateUserDidRequestReminderToRateApp];
  937. }
  938. }
  939. else
  940. {
  941. //mark as rated
  942. self.ratedThisVersion = YES;
  943. //log event
  944. if ([self.delegate respondsToSelector:@selector(iRateUserDidAttemptToRateApp)])
  945. {
  946. [self.delegate iRateUserDidAttemptToRateApp];
  947. }
  948. if (![self.delegate respondsToSelector:@selector(iRateShouldOpenAppStore)] || [_delegate iRateShouldOpenAppStore])
  949. {
  950. //go to ratings page
  951. [self openRatingsPageInAppStore];
  952. }
  953. }
  954. //release alert
  955. self.visibleAlert = nil;
  956. }
  957. #else
  958. - (void)openAppPageWhenAppStoreLaunched
  959. {
  960. //check if app store is running
  961. ProcessSerialNumber psn = { kNoProcess, kNoProcess };
  962. while (GetNextProcess(&psn) == noErr)
  963. {
  964. CFDictionaryRef cfDict = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
  965. NSString *bundleID = [(__bridge NSDictionary *)cfDict objectForKey:(__bridge NSString *)kCFBundleIdentifierKey];
  966. if ([iRateMacAppStoreBundleID isEqualToString:[bundleID lowercaseString]])
  967. {
  968. //open app page
  969. [[NSWorkspace sharedWorkspace] performSelector:@selector(openURL:) withObject:self.ratingsURL afterDelay:MAC_APP_STORE_REFRESH_DELAY];
  970. CFRelease(cfDict);
  971. return;
  972. }
  973. CFRelease(cfDict);
  974. }
  975. //try again
  976. [self performSelector:@selector(openAppPageWhenAppStoreLaunched) withObject:nil afterDelay:0.0];
  977. }
  978. - (void)openRatingsPageInAppStore
  979. {
  980. if (self.verboseLogging)
  981. {
  982. NSLog(@"iRate will open the App Store ratings page using the following URL: %@", self.ratingsURL);
  983. }
  984. [[NSWorkspace sharedWorkspace] openURL:self.ratingsURL];
  985. [self openAppPageWhenAppStoreLaunched];
  986. }
  987. - (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
  988. {
  989. switch (returnCode)
  990. {
  991. case NSAlertAlternateReturn:
  992. {
  993. //remind later
  994. self.lastReminded = [NSDate date];
  995. NSInteger declinedCount = [self declinedCount];
  996. declinedCount++;
  997. [self setDeclinedCount:declinedCount];
  998. //log event
  999. if ([self.delegate respondsToSelector:@selector(iRateUserDidDeclineToRateApp)])
  1000. {
  1001. [self.delegate iRateUserDidDeclineToRateApp];
  1002. }
  1003. break;
  1004. }
  1005. case NSAlertDefaultReturn:
  1006. {
  1007. //remind later
  1008. self.lastReminded = [NSDate date];
  1009. //mark as rated
  1010. self.ratedThisVersion = YES;
  1011. //log event
  1012. if ([self.delegate respondsToSelector:@selector(iRateUserDidAttemptToRateApp)])
  1013. {
  1014. [self.delegate iRateUserDidAttemptToRateApp];
  1015. }
  1016. if (![self.delegate respondsToSelector:@selector(iRateShouldOpenAppStore)] || [_delegate iRateShouldOpenAppStore])
  1017. {
  1018. //launch mac app store
  1019. [self openRatingsPageInAppStore];
  1020. }
  1021. break;
  1022. }
  1023. default:
  1024. {
  1025. //remind later
  1026. self.lastReminded = [NSDate date];
  1027. //log event
  1028. if ([self.delegate respondsToSelector:@selector(iRateUserDidRequestReminderToRateApp)])
  1029. {
  1030. [self.delegate iRateUserDidRequestReminderToRateApp];
  1031. }
  1032. }
  1033. }
  1034. //release alert
  1035. self.visibleAlert = nil;
  1036. }
  1037. #endif
  1038. - (void)logEvent:(BOOL)deferPrompt
  1039. {
  1040. [self incrementEventCount];
  1041. if (!deferPrompt && [self shouldPromptForRating])
  1042. {
  1043. [self promptIfNetworkAvailable];
  1044. }
  1045. }
  1046. - (void)remindMeLater
  1047. {
  1048. //remind later
  1049. self.lastReminded = [NSDate date];
  1050. NSInteger declinedCount = [self declinedCount];
  1051. declinedCount++;
  1052. [self setDeclinedCount:declinedCount];
  1053. //log event
  1054. if ([self.delegate respondsToSelector:@selector(iRateUserDidDeclineToRateApp)])
  1055. {
  1056. [self.delegate iRateUserDidDeclineToRateApp];
  1057. }
  1058. }
  1059. @end