Skip to content
This repository was archived by the owner on Mar 24, 2023. It is now read-only.

Commit 03bb296

Browse files
authored
Merge pull request #2 from timehop/sync-from-main
Sync from main
2 parents fff3be7 + f2e6024 commit 03bb296

File tree

7 files changed

+111
-80
lines changed

7 files changed

+111
-80
lines changed

Appirater.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
*/
3636

3737
#import <Foundation/Foundation.h>
38-
#import "AppiraterDelegate.h"
3938
#import <StoreKit/StoreKit.h>
39+
#import "AppiraterDelegate.h"
4040

4141
extern NSString *const kAppiraterFirstUseDate;
4242
extern NSString *const kAppiraterUseCount;
@@ -86,11 +86,16 @@ extern NSString *const kAppiraterReminderRequestDate;
8686
#define APPIRATER_RATE_LATER NSLocalizedStringFromTableInBundle(@"Remind me later", @"AppiraterLocalizable", [Appirater bundle], nil)
8787

8888
@interface Appirater : NSObject <UIAlertViewDelegate, SKStoreProductViewControllerDelegate> {
89-
89+
#pragma clang diagnostic push
90+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
9091
UIAlertView *ratingAlert;
92+
#pragma clang diagnostic pop
9193
}
9294

95+
#pragma clang diagnostic push
96+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
9397
@property(nonatomic, strong) UIAlertView *ratingAlert;
98+
#pragma clang diagnostic pop
9499
@property(nonatomic) BOOL openInAppStore;
95100
#if __has_feature(objc_arc_weak)
96101
@property(nonatomic, weak) NSObject <AppiraterDelegate> *delegate;

Appirater.m

Lines changed: 61 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434
* Copyright 2012 Arash Payan. All rights reserved.
3535
*/
3636

37+
#import <SystemConfiguration/SystemConfiguration.h>
38+
#import <CFNetwork/CFNetwork.h>
3739
#import "Appirater.h"
38-
#import <SystemConfiguration/SCNetworkReachability.h>
3940
#include <netinet/in.h>
4041

4142
#if ! __has_feature(objc_arc)
@@ -60,11 +61,6 @@
6061
static NSInteger _significantEventsUntilPrompt = -1;
6162
static double _timeBeforeReminding = 1;
6263
static BOOL _debug = NO;
63-
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
64-
static id<AppiraterDelegate> _delegate;
65-
#else
66-
__weak static id<AppiraterDelegate> _delegate;
67-
#endif
6864
static BOOL _usesAnimation = TRUE;
6965
static UIStatusBarStyle _statusBarStyle;
7066
static BOOL _modalOpen = false;
@@ -76,6 +72,7 @@ @interface Appirater ()
7672
@property (nonatomic, copy) NSString *alertCancelTitle;
7773
@property (nonatomic, copy) NSString *alertRateTitle;
7874
@property (nonatomic, copy) NSString *alertRateLaterTitle;
75+
@property (nonatomic, strong) NSOperationQueue *eventQueue;
7976
- (BOOL)connectedToNetwork;
8077
+ (Appirater*)sharedInstance;
8178
- (void)showPromptWithChecks:(BOOL)withChecks
@@ -141,7 +138,7 @@ + (void) setDebug:(BOOL)debug {
141138
_debug = debug;
142139
}
143140
+ (void)setDelegate:(id<AppiraterDelegate>)delegate{
144-
_delegate = delegate;
141+
Appirater.sharedInstance.delegate = delegate;
145142
}
146143
+ (void)setUsesAnimation:(BOOL)animation {
147144
_usesAnimation = animation;
@@ -246,10 +243,17 @@ - (BOOL)connectedToNetwork {
246243
BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection;
247244

248245
NSURL *testURL = [NSURL URLWithString:@"http://www.apple.com/"];
249-
NSURLRequest *testRequest = [NSURLRequest requestWithURL:testURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0];
250-
NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self];
251246

252-
return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO;
247+
NSURLSessionConfiguration* sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
248+
sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
249+
sessionConfiguration.timeoutIntervalForRequest = 20.0;
250+
251+
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
252+
253+
NSURLSessionTask *task = [session dataTaskWithURL:testURL];
254+
[task resume];
255+
256+
return ((isReachable && !needsConnection) || nonWiFi) ? ( (task.state != NSURLSessionTaskStateSuspended) ? YES : NO ) : NO;
253257
}
254258

255259
+ (Appirater*)sharedInstance {
@@ -259,7 +263,8 @@ + (Appirater*)sharedInstance {
259263
static dispatch_once_t onceToken;
260264
dispatch_once(&onceToken, ^{
261265
appirater = [[Appirater alloc] init];
262-
appirater.delegate = _delegate;
266+
appirater.eventQueue = [[NSOperationQueue alloc] init];
267+
appirater.eventQueue.maxConcurrentOperationCount = 1;
263268
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:
264269
UIApplicationWillResignActiveNotification object:nil];
265270
});
@@ -269,33 +274,39 @@ + (Appirater*)sharedInstance {
269274
}
270275

271276
- (void)showRatingAlert:(BOOL)displayRateLaterButton {
272-
UIAlertView *alertView = nil;
273277
id <AppiraterDelegate> delegate = _delegate;
274278

275279
if(delegate && [delegate respondsToSelector:@selector(appiraterShouldDisplayAlert:)] && ![delegate appiraterShouldDisplayAlert:self]) {
276280
return;
277281
}
278282

279-
if (displayRateLaterButton) {
280-
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
281-
message:self.alertMessage
282-
delegate:self
283-
cancelButtonTitle:self.alertCancelTitle
284-
otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil];
283+
if (NSStringFromClass([SKStoreReviewController class]) != nil) {
284+
// If SKStoreReviewController is used, skip the custom dialog and directly go the the rating
285+
[Appirater rateApp];
285286
} else {
286-
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
287-
message:self.alertMessage
288-
delegate:self
289-
cancelButtonTitle:self.alertCancelTitle
290-
otherButtonTitles:self.alertRateTitle, nil];
291-
}
292-
293-
self.ratingAlert = alertView;
287+
// Otherwise show a custom Alert
288+
UIAlertView *alertView = nil;
289+
if (displayRateLaterButton) {
290+
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
291+
message:self.alertMessage
292+
delegate:self
293+
cancelButtonTitle:self.alertCancelTitle
294+
otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil];
295+
} else {
296+
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
297+
message:self.alertMessage
298+
delegate:self
299+
cancelButtonTitle:self.alertCancelTitle
300+
otherButtonTitles:self.alertRateTitle, nil];
301+
}
302+
303+
self.ratingAlert = alertView;
294304
[alertView show];
305+
}
295306

296-
if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) {
297-
[delegate appiraterDidDisplayAlert:self];
298-
}
307+
if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) {
308+
[delegate appiraterDidDisplayAlert:self];
309+
}
299310
}
300311

301312
- (void)showRatingAlert
@@ -482,17 +493,17 @@ + (void)appWillResignActive {
482493
}
483494

484495
+ (void)appEnteredForeground:(BOOL)canPromptForRating {
485-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
486-
^{
487-
[[Appirater sharedInstance] incrementAndRate:canPromptForRating];
488-
});
496+
Appirater *a = [Appirater sharedInstance];
497+
[a.eventQueue addOperationWithBlock:^{
498+
[[Appirater sharedInstance] incrementAndRate:canPromptForRating];
499+
}];
489500
}
490501

491502
+ (void)userDidSignificantEvent:(BOOL)canPromptForRating {
492-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
493-
^{
494-
[[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating];
495-
});
503+
Appirater *a = [Appirater sharedInstance];
504+
[a.eventQueue addOperationWithBlock:^{
505+
[[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating];
506+
}];
496507
}
497508

498509
#pragma GCC diagnostic push
@@ -564,10 +575,20 @@ + (UIViewController *) topMostViewController: (UIViewController *) controller {
564575

565576
+ (void)rateApp {
566577

578+
//Use the built SKStoreReviewController if available (available from iOS 10.3 upwards)
579+
if (NSStringFromClass([SKStoreReviewController class]) != nil) {
580+
// Also note, that SKStoreReviewController takes care of impression limitation by itself so it's ok to not save the impression manually
581+
// This also works in the simulator
582+
[SKStoreReviewController requestReview];
583+
return;
584+
}
585+
567586
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
568587
[userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
569588
[userDefaults synchronize];
570589

590+
591+
571592
//Use the in-app StoreKit view if available (iOS 6) and imported. This works in the simulator.
572593
if (![Appirater sharedInstance].openInAppStore && NSStringFromClass([SKStoreProductViewController class]) != nil) {
573594

@@ -582,11 +603,6 @@ + (void)rateApp {
582603
}
583604
[[self getRootViewController] presentViewController:storeViewController animated:_usesAnimation completion:^{
584605
[self setModalOpen:YES];
585-
//Temporarily use a black status bar to match the StoreKit view.
586-
[self setStatusBarStyle:[UIApplication sharedApplication].statusBarStyle];
587-
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
588-
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent animated:_usesAnimation];
589-
#endif
590606
}];
591607

592608
//Use the standard openUrl method if StoreKit is unavailable.
@@ -595,17 +611,17 @@ + (void)rateApp {
595611
#if TARGET_IPHONE_SIMULATOR
596612
NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page.");
597613
#else
598-
NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
614+
NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:_appId];
599615

600616
// iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
601617
// Fixes condition @see https://github.com/arashpayan/appirater/issues/205
602618
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
603-
reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
619+
reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:_appId];
604620
}
605621
// iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
606622
else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
607623
{
608-
reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
624+
reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:_appId];
609625
}
610626

611627
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
@@ -659,7 +675,6 @@ - (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewContr
659675
//Close the in-app rating (StoreKit) view and restore the previous status bar style.
660676
+ (void)closeModal {
661677
if (_modalOpen) {
662-
[[UIApplication sharedApplication]setStatusBarStyle:_statusBarStyle animated:_usesAnimation];
663678
BOOL usedAnimation = _usesAnimation;
664679
[self setModalOpen:NO];
665680

Appirater.podspec.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
22
"name": "Appirater",
3-
"version": "2.0.5.1",
3+
"version": "2.2.0.1",
44
"platforms": {
55
"ios": "5.0"
66
},
77
"summary": "A utility that reminds your iPhone app's users to review the app.",
8-
"homepage": "http://arashpayan.com/blog/2009/09/07/presenting-appirater/",
8+
"homepage": "https://arashpayan.com/blog/2009/09/07/presenting-appirater/",
99
"authors": {
1010
"Arash Payan": "arash@ara.sh"
1111
},
1212
"source": {
1313
"git": "https://github.com/timehop/appirater.git",
14-
"tag": "2.0.5.1"
14+
"tag": "2.2.0.1"
1515
},
1616
"source_files": "*.{h,m}",
1717
"resource_bundles": {
@@ -27,6 +27,6 @@
2727
"weak_frameworks": "StoreKit",
2828
"license": {
2929
"type": "MIT",
30-
"text": "Copyright 2015. Arash Payan. This library is distributed under the terms of the MIT/X11."
30+
"text": "Copyright 2017. Arash Payan. This library is distributed under the terms of the MIT/X11."
3131
}
3232
}

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
Change Log
22
==========
33

4+
Version 2.2.0 *(2017-09-23)*
5+
----------------------------
6+
* Use SKStoreReviewController if available
7+
* Available on iOS > 10.3
8+
* You'll need to link the StoreKit Framework
9+
* Armenian localization
10+
* Fix delegate not being set after Appirater initialization (Issue #215)
11+
12+
Version 2.1.0 *(2016-11-04)*
13+
----------------------------
14+
* Fix and suppress various Xcode warnings
15+
* Switch to NSURLSession
16+
* Serialize incrementing events
17+
418
Version 2.0.4 *(2014-09-18)*
519
----------------------------
620
* Change: Better URL for iOS 7.1 and 8 support

README.md

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
Introduction
2-
------------
3-
Appirater is a class that you can drop into any iPhone app (iOS 4.0 or later) that will help remind your users
4-
to review your app on the App Store. The code is released under the MIT/X11, so feel free to
5-
modify and share your changes with the world. Read on below for how to get started. If you need any help using,
6-
the library check out the [Appirater group] [appiratergroup].
2+
---------------
73

4+
Appirater is a class that you can drop into any iPhone app (iOS 4.0 or later) that will help remind your users to review your app on the App Store. The code is released under the MIT/X11, so feel free to modify and share your changes with the world. Read on below for how to get started. If you need any help using, the library, post your questions on [Stack Overflow] [stackoverflow] under the `appirater` tag.
85

96
Getting Started
107
---------------
118

12-
###Cocoapods
13-
If you're new to Cocoapods [watch this](http://nsscreencast.com/episodes/5-cocoapods). To add Appirater to your app, add `pod "Appirater"` to your Podfile.
14-
15-
Cocoapods support is still experimental, and might not work in all use cases. If you experience problems, open an issue and install via Git submodule
16-
17-
###Manually
18-
1. Add the Appirater code into your project.
19-
2. If your project doesn't use ARC, add the `-fobjc-arc` compiler flag to `Appirater.m` in your target's Build Phases » Compile Sources section.
20-
3. Add the `CFNetwork`, `SystemConfiguration`, and `StoreKit` frameworks to your project. Be sure to **change Required to Optional** for StoreKit in your target's Build Phases » Link Binary with Libraries section.
9+
### CocoaPods
10+
To add Appirater to your app, add `pod "Appirater"` to your Podfile.
2111

2212
Configuration
2313
-------------
@@ -37,10 +27,10 @@ Configuration
3727
4. Call `[Appirater appEnteredForeground:YES]` in your app delegate's `applicationWillEnterForeground:` method.
3828
5. (OPTIONAL) Call `[Appirater userDidSignificantEvent:YES]` when the user does something 'significant' in the app.
3929
40-
###Development
30+
### Development
4131
Setting `[Appirater setDebug:YES]` will ensure that the rating request is shown each time the app is launched.
4232
43-
###Production
33+
### Production
4434
Make sure you set `[Appirater setDebug:NO]` to ensure the request is not shown every time the app is launched. Also make sure that each of these components are set in the `application:didFinishLaunchingWithOptions:` method.
4535
4636
This example states that the rating request is only shown when the app has been launched 5 times **and** after 7 days.
@@ -67,13 +57,17 @@ If you wanted to show the request after 5 days only you can set the following:
6757
[Appirater appLaunched:YES];
6858
```
6959
70-
Help and Support Group
60+
SKStoreReviewController
7161
----------------------
72-
Requests for help, questions about usage, suggestions and other relevant topics should be posted at the [Appirater group] [appiratergroup]. As much as I'd like to help everyone who emails me, I can't respond to private emails, but I'll respond to posts on the group where others can benefit from the Q&As.
62+
In iOS 10.3, [SKStoreReviewController](https://developer.apple.com/library/content/releasenotes/General/WhatsNewIniOS/Articles/iOS10_3.html) was introduced which allows rating directly within the app without any additional setup.
63+
64+
Appirater automatically uses `SKStoreReviewController` if available. You'll need to manually link `StoreKit` in your App however.
65+
66+
If `SKStoreReviewController` is used, Appirater is used only to decide when to show the rating dialog to the user. Keep in mind, that `SKStoreReviewController` automatically limits the number of impressions, so the dialog might be displayed less frequently than your configured conditions might suggest.
7367
7468
License
7569
-------
76-
Copyright 2014. [Arash Payan] [arash].
70+
Copyright 2017. [Arash Payan] [arash].
7771
This library is distributed under the terms of the MIT/X11.
7872
7973
While not required, I greatly encourage and appreciate any improvements that you make
@@ -83,16 +77,10 @@ Ports for other SDKs
8377
--------------
8478
A few people have ported Appirater to other SDKs. The ports are listed here in hopes that they may assist developers of those SDKs. I don't know how closesly (if at all) they track the Objective-C version of Appirater. If you need support for any of the libraries, please contact the maintainer of the port.
8579
86-
+ MonoTouch Port (using C#). [Github] [monotouchport]
8780
+ MonoTouch Binding (using native Appirater). [Github] [monotouchbinding]
88-
+ Corona SDK. [Github] [coronasdkport]
89-
+ Titanium SDK. [Github] [titaniumport]
9081
91-
[appiratergroup]: http://groups.google.com/group/appirater
92-
[homepage]: http://arashpayan.com/blog/index.php/2009/09/07/presenting-appirater/
93-
[arash]: http://arashpayan.com
82+
[stackoverflow]: http://stackoverflow.com/
83+
[homepage]: https://arashpayan.com/blog/2009/09/07/presenting-appirater/
84+
[arash]: https://arashpayan.com
9485
[Appirater.h]: https://github.com/arashpayan/appirater/blob/master/Appirater.h
95-
[monotouchport]: https://github.com/chebum/Appirater-for-MonoTouch
9686
[monotouchbinding]: https://github.com/theonlylawislove/MonoTouch.Appirater
97-
[coronasdkport]: https://github.com/aliasgar84/Appirater
98-
[titaniumport]: https://github.com/mpociot/TiAppirater
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!" = "اگر از استفاده برنامه %@ لذت می‌برید، زمان دارید بهش امتیاز دهید؟ بیشتر از یک دقیقه زمان نخواهد گرفت. با تشکر از اینکه ما را همایت می‌کنید.";
2+
"Rate %@" = "ارزیابی کردن %@";
3+
"No, Thanks" = "نه، مرسی";
4+
"Remind me later" = "بعدا";
5+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!" = "Եթե Դուք հաճույքով եք օգտագործում %@-ը, դեմ չե՞ք լինի տրամադրել մեկ րոպե այն գնահատելու համար: Այն չի պահանջի ձեզանից ավելի քան մեկ րոպե: Շնորհակալություն աջակցության համար:";
2+
"Rate %@" = "Գնահատել %@-ը";
3+
"No, Thanks" = "Ոչ, շնորհակալություն";
4+
"Remind me later" = "Հիշեցնել ավելի ուշ";

0 commit comments

Comments
 (0)