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

Commit

Permalink
Merge pull request #2 from timehop/sync-from-main
Browse files Browse the repository at this point in the history
Sync from main
  • Loading branch information
inderdhir authored Oct 9, 2017
2 parents fff3be7 + f2e6024 commit 03bb296
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 80 deletions.
9 changes: 7 additions & 2 deletions Appirater.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
*/

#import <Foundation/Foundation.h>
#import "AppiraterDelegate.h"
#import <StoreKit/StoreKit.h>
#import "AppiraterDelegate.h"

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

@interface Appirater : NSObject <UIAlertViewDelegate, SKStoreProductViewControllerDelegate> {

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
UIAlertView *ratingAlert;
#pragma clang diagnostic pop
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@property(nonatomic, strong) UIAlertView *ratingAlert;
#pragma clang diagnostic pop
@property(nonatomic) BOOL openInAppStore;
#if __has_feature(objc_arc_weak)
@property(nonatomic, weak) NSObject <AppiraterDelegate> *delegate;
Expand Down
107 changes: 61 additions & 46 deletions Appirater.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@
* Copyright 2012 Arash Payan. All rights reserved.
*/

#import <SystemConfiguration/SystemConfiguration.h>
#import <CFNetwork/CFNetwork.h>
#import "Appirater.h"
#import <SystemConfiguration/SCNetworkReachability.h>
#include <netinet/in.h>

#if ! __has_feature(objc_arc)
Expand All @@ -60,11 +61,6 @@
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;
Expand All @@ -76,6 +72,7 @@ @interface Appirater ()
@property (nonatomic, copy) NSString *alertCancelTitle;
@property (nonatomic, copy) NSString *alertRateTitle;
@property (nonatomic, copy) NSString *alertRateLaterTitle;
@property (nonatomic, strong) NSOperationQueue *eventQueue;
- (BOOL)connectedToNetwork;
+ (Appirater*)sharedInstance;
- (void)showPromptWithChecks:(BOOL)withChecks
Expand Down Expand Up @@ -141,7 +138,7 @@ + (void) setDebug:(BOOL)debug {
_debug = debug;
}
+ (void)setDelegate:(id<AppiraterDelegate>)delegate{
_delegate = delegate;
Appirater.sharedInstance.delegate = delegate;
}
+ (void)setUsesAnimation:(BOOL)animation {
_usesAnimation = animation;
Expand Down Expand Up @@ -246,10 +243,17 @@ - (BOOL)connectedToNetwork {
BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection;

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

return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO;
NSURLSessionConfiguration* sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
sessionConfiguration.timeoutIntervalForRequest = 20.0;

NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

NSURLSessionTask *task = [session dataTaskWithURL:testURL];
[task resume];

return ((isReachable && !needsConnection) || nonWiFi) ? ( (task.state != NSURLSessionTaskStateSuspended) ? YES : NO ) : NO;
}

+ (Appirater*)sharedInstance {
Expand All @@ -259,7 +263,8 @@ + (Appirater*)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
appirater = [[Appirater alloc] init];
appirater.delegate = _delegate;
appirater.eventQueue = [[NSOperationQueue alloc] init];
appirater.eventQueue.maxConcurrentOperationCount = 1;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:
UIApplicationWillResignActiveNotification object:nil];
});
Expand All @@ -269,33 +274,39 @@ + (Appirater*)sharedInstance {
}

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

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

if (displayRateLaterButton) {
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
message:self.alertMessage
delegate:self
cancelButtonTitle:self.alertCancelTitle
otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil];
if (NSStringFromClass([SKStoreReviewController class]) != nil) {
// If SKStoreReviewController is used, skip the custom dialog and directly go the the rating
[Appirater rateApp];
} else {
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
message:self.alertMessage
delegate:self
cancelButtonTitle:self.alertCancelTitle
otherButtonTitles:self.alertRateTitle, nil];
}

self.ratingAlert = alertView;
// Otherwise show a custom Alert
UIAlertView *alertView = nil;
if (displayRateLaterButton) {
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
message:self.alertMessage
delegate:self
cancelButtonTitle:self.alertCancelTitle
otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil];
} else {
alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
message:self.alertMessage
delegate:self
cancelButtonTitle:self.alertCancelTitle
otherButtonTitles:self.alertRateTitle, nil];
}

self.ratingAlert = alertView;
[alertView show];
}

if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) {
[delegate appiraterDidDisplayAlert:self];
}
if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) {
[delegate appiraterDidDisplayAlert:self];
}
}

- (void)showRatingAlert
Expand Down Expand Up @@ -482,17 +493,17 @@ + (void)appWillResignActive {
}

+ (void)appEnteredForeground:(BOOL)canPromptForRating {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
[[Appirater sharedInstance] incrementAndRate:canPromptForRating];
});
Appirater *a = [Appirater sharedInstance];
[a.eventQueue addOperationWithBlock:^{
[[Appirater sharedInstance] incrementAndRate:canPromptForRating];
}];
}

+ (void)userDidSignificantEvent:(BOOL)canPromptForRating {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
^{
[[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating];
});
Appirater *a = [Appirater sharedInstance];
[a.eventQueue addOperationWithBlock:^{
[[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating];
}];
}

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

+ (void)rateApp {

//Use the built SKStoreReviewController if available (available from iOS 10.3 upwards)
if (NSStringFromClass([SKStoreReviewController class]) != nil) {
// Also note, that SKStoreReviewController takes care of impression limitation by itself so it's ok to not save the impression manually
// This also works in the simulator
[SKStoreReviewController requestReview];
return;
}

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
[userDefaults synchronize];



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

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

//Use the standard openUrl method if StoreKit is unavailable.
Expand All @@ -595,17 +611,17 @@ + (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
NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:_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]];
reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:_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]];
reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:_appId];
}

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
Expand Down Expand Up @@ -659,7 +675,6 @@ - (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewContr
//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];

Expand Down
8 changes: 4 additions & 4 deletions Appirater.podspec.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"name": "Appirater",
"version": "2.0.5.1",
"version": "2.2.0.1",
"platforms": {
"ios": "5.0"
},
"summary": "A utility that reminds your iPhone app's users to review the app.",
"homepage": "http://arashpayan.com/blog/2009/09/07/presenting-appirater/",
"homepage": "https://arashpayan.com/blog/2009/09/07/presenting-appirater/",
"authors": {
"Arash Payan": "[email protected]"
},
"source": {
"git": "https://github.com/timehop/appirater.git",
"tag": "2.0.5.1"
"tag": "2.2.0.1"
},
"source_files": "*.{h,m}",
"resource_bundles": {
Expand All @@ -27,6 +27,6 @@
"weak_frameworks": "StoreKit",
"license": {
"type": "MIT",
"text": "Copyright 2015. Arash Payan. This library is distributed under the terms of the MIT/X11."
"text": "Copyright 2017. Arash Payan. This library is distributed under the terms of the MIT/X11."
}
}
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
Change Log
==========

Version 2.2.0 *(2017-09-23)*
----------------------------
* Use SKStoreReviewController if available
* Available on iOS > 10.3
* You'll need to link the StoreKit Framework
* Armenian localization
* Fix delegate not being set after Appirater initialization (Issue #215)

Version 2.1.0 *(2016-11-04)*
----------------------------
* Fix and suppress various Xcode warnings
* Switch to NSURLSession
* Serialize incrementing events

Version 2.0.4 *(2014-09-18)*
----------------------------
* Change: Better URL for iOS 7.1 and 8 support
Expand Down
44 changes: 16 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
Introduction
------------
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 check out the [Appirater group] [appiratergroup].
---------------

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.

Getting Started
---------------

###Cocoapods
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.

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

###Manually
1. Add the Appirater code into your project.
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.
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.
### CocoaPods
To add Appirater to your app, add `pod "Appirater"` to your Podfile.

Configuration
-------------
Expand All @@ -37,10 +27,10 @@ Configuration
4. Call `[Appirater appEnteredForeground:YES]` in your app delegate's `applicationWillEnterForeground:` method.
5. (OPTIONAL) Call `[Appirater userDidSignificantEvent:YES]` when the user does something 'significant' in the app.
###Development
### Development
Setting `[Appirater setDebug:YES]` will ensure that the rating request is shown each time the app is launched.
###Production
### Production
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.
This example states that the rating request is only shown when the app has been launched 5 times **and** after 7 days.
Expand All @@ -67,13 +57,17 @@ If you wanted to show the request after 5 days only you can set the following:
[Appirater appLaunched:YES];
```
Help and Support Group
SKStoreReviewController
----------------------
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.
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.
Appirater automatically uses `SKStoreReviewController` if available. You'll need to manually link `StoreKit` in your App however.
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.
License
-------
Copyright 2014. [Arash Payan] [arash].
Copyright 2017. [Arash Payan] [arash].
This library is distributed under the terms of the MIT/X11.
While not required, I greatly encourage and appreciate any improvements that you make
Expand All @@ -83,16 +77,10 @@ Ports for other SDKs
--------------
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.
+ MonoTouch Port (using C#). [Github] [monotouchport]
+ MonoTouch Binding (using native Appirater). [Github] [monotouchbinding]
+ Corona SDK. [Github] [coronasdkport]
+ Titanium SDK. [Github] [titaniumport]
[appiratergroup]: http://groups.google.com/group/appirater
[homepage]: http://arashpayan.com/blog/index.php/2009/09/07/presenting-appirater/
[arash]: http://arashpayan.com
[stackoverflow]: http://stackoverflow.com/
[homepage]: https://arashpayan.com/blog/2009/09/07/presenting-appirater/
[arash]: https://arashpayan.com
[Appirater.h]: https://github.com/arashpayan/appirater/blob/master/Appirater.h
[monotouchport]: https://github.com/chebum/Appirater-for-MonoTouch
[monotouchbinding]: https://github.com/theonlylawislove/MonoTouch.Appirater
[coronasdkport]: https://github.com/aliasgar84/Appirater
[titaniumport]: https://github.com/mpociot/TiAppirater
5 changes: 5 additions & 0 deletions fa.lproj/AppiraterLocalizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!" = "اگر از استفاده برنامه %@ لذت می‌برید، زمان دارید بهش امتیاز دهید؟ بیشتر از یک دقیقه زمان نخواهد گرفت. با تشکر از اینکه ما را همایت می‌کنید.";
"Rate %@" = "ارزیابی کردن %@";
"No, Thanks" = "نه، مرسی";
"Remind me later" = "بعدا";

4 changes: 4 additions & 0 deletions hy.lproj/AppiraterLocalizable.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!" = "Եթե Դուք հաճույքով եք օգտագործում %@-ը, դեմ չե՞ք լինի տրամադրել մեկ րոպե այն գնահատելու համար: Այն չի պահանջի ձեզանից ավելի քան մեկ րոպե: Շնորհակալություն աջակցության համար:";
"Rate %@" = "Գնահատել %@-ը";
"No, Thanks" = "Ոչ, շնորհակալություն";
"Remind me later" = "Հիշեցնել ավելի ուշ";

0 comments on commit 03bb296

Please sign in to comment.