Merge pull request #112 from MartinMoizard/master

Improve iOS 10.3 review dialog support + documentation
This commit is contained in:
Matt Coneybeare
2017-11-10 12:16:23 -05:00
committed by GitHub
2 changed files with 65 additions and 55 deletions

View File

@@ -284,6 +284,14 @@ Armchair.shouldPromptIfRated() -> Bool
Armchair.shouldPromptIfRated(shouldPromptIfRated: Bool)
```
The `useStoreKitReviewPrompt` configuration determines wether or not to try showing the SKStoreReviewController's requestReview() prompt instead of the default prompt. This setting has some effects only on iOS version >= 10.3. It's default value is `false`.
```swift
// GETTER
Armchair.useStoreKitReviewPrompt() -> Bool
// SETTER
Armchair.useStoreKitReviewPrompt(useStoreKitReviewPrompt: Bool)
```
The `useMainAppBundleForLocalizations` configuration is a way to tell Armchair that you are providing your own translations for the review prompt popup strings. This may be because you are just customizing them, or that you have set your own text for the popup. If set to `true`, the main bundle will always be used to load localized strings. You have to include the translations either in a file called `ArmchairLocalizable.strings` or the standard `Localizable.strings`. If set to `false` Armchair will look in its own translation bundle for the translating strings. It's default value is `false`.
```swift

View File

@@ -243,6 +243,13 @@ public func shouldPromptIfRated(_ shouldPromptIfRated: Bool) {
Manager.defaultManager.shouldPromptIfRated = shouldPromptIfRated
}
/*
* Return whether Armchair will try and present the Storekit review prompt (useful for custom dialog modification)
*/
public var shouldTryStoreKitReviewPrompt : Bool {
return Manager.defaultManager.shouldTryStoreKitReviewPrompt
}
/*
* If set to true, the main bundle will always be used to load localized strings.
* Set this to true if you have provided your own custom localizations in
@@ -742,7 +749,7 @@ public struct AppiraterKey {
// MARK: PRIVATE Interface
#if os(iOS)
open class ArmchairManager : NSObject, UIAlertViewDelegate, SKStoreProductViewControllerDelegate { }
open class ArmchairManager : NSObject, SKStoreProductViewControllerDelegate { }
#elseif os(OSX)
open class ArmchairManager : NSObject, NSAlertDelegate { }
#else
@@ -762,7 +769,7 @@ open class Manager : ArmchairManager {
// MARK: Review Alert & Properties
#if os(iOS)
fileprivate var ratingAlert: UIAlertView? = nil
fileprivate var ratingAlert: UIAlertController? = nil
fileprivate let reviewURLTemplate = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&id=APP_ID&at=AFFILIATE_CODE&ct=AFFILIATE_CAMPAIGN_CODE&action=write-review"
fileprivate let reviewURLTemplateiOS11 = "https://itunes.apple.com/us/app/idAPP_ID?ls=1&mt=8&at=AFFILIATE_CODE&ct=AFFILIATE_CAMPAIGN_CODE&action=write-review"
#elseif os(OSX)
@@ -1207,24 +1214,46 @@ open class Manager : ArmchairManager {
return (daysBeforeReminding > 0 && remindButtonTitle != nil)
}
public var shouldTryStoreKitReviewPrompt : Bool {
if #available(iOS 10.3, *), useStoreKitReviewPrompt { return true }
return false
}
fileprivate func requestStoreKitReviewPrompt() -> Bool {
if #available(iOS 10.3, *), useStoreKitReviewPrompt {
SKStoreReviewController.requestReview()
// Assume this version is rated. There is no API to tell if the user actaully rated.
userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion))
userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.RatedAnyVersion))
userDefaultsObject?.synchronize()
closeModalPanel()
return true
}
return false
}
fileprivate func showRatingAlert() {
if let customClosure = customAlertClosure {
customClosure({[weak self] in self?._rateApp()}, {[weak self] in self?.remindMeLater()}, {[weak self] in self?.dontRate()})
customClosure({[weak self] in
if let result = self?.requestStoreKitReviewPrompt(), result {
///Showed storekit prompt, all done
} else {
/// Didn't show storekit prompt, present app store manually
self?._rateApp()
}
}, {[weak self] in self?.remindMeLater()}, {[weak self] in self?.dontRate()})
if let closure = self.didDisplayAlertClosure {
closure()
}
} else {
#if os(iOS)
if #available(iOS 10.3, *), useStoreKitReviewPrompt {
SKStoreReviewController.requestReview()
// Assume this version is rated. There is no API to tell if the user actaully rated.
userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion))
userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.RatedAnyVersion))
userDefaultsObject?.synchronize()
return
}
if (operatingSystemVersion >= 8 && usesAlertController) || operatingSystemVersion >= 9 {
/* iOS 8 uses new UIAlertController API*/
if requestStoreKitReviewPrompt() {
///Showed storekit prompt, all done
} else {
/// Didn't show storekit prompt, present app store manually
let alertView : UIAlertController = UIAlertController(title: reviewTitle, message: reviewMessage, preferredStyle: UIAlertControllerStyle.alert)
alertView.addAction(UIAlertAction(title: cancelButtonTitle, style:UIAlertActionStyle.default, handler: {
(alert: UIAlertAction!) in
@@ -1240,7 +1269,9 @@ open class Manager : ArmchairManager {
(alert: UIAlertAction!) in
self._rateApp()
}))
ratingAlert = alertView
// get the top most controller (= the StoreKit Controller) and dismiss it
if let presentingController = UIApplication.shared.keyWindow?.rootViewController {
if let topController = Manager.topMostViewController(presentingController) {
@@ -1254,25 +1285,6 @@ open class Manager : ArmchairManager {
// note that tint color has to be set after the controller is presented in order to take effect (last checked in iOS 9.3)
alertView.view.tintColor = tintColor
}
} else {
/* Otherwise we use UIAlertView still */
var alertView: UIAlertView
if (showsRemindButton()) {
alertView = UIAlertView(title: reviewTitle, message: reviewMessage, delegate: self, cancelButtonTitle: cancelButtonTitle, otherButtonTitles: remindButtonTitle!, rateButtonTitle)
} else {
alertView = UIAlertView(title: reviewTitle, message: reviewMessage, delegate: self, cancelButtonTitle: cancelButtonTitle, otherButtonTitles: rateButtonTitle)
}
// If we have a remind button, show it first. Otherwise show the rate button
// If we have a remind button, show the rate button next. Otherwise stop adding buttons.
alertView.cancelButtonIndex = -1
ratingAlert = alertView
alertView.show()
if let closure = didDisplayAlertClosure {
closure()
}
}
#elseif os(OSX)
@@ -1310,19 +1322,6 @@ open class Manager : ArmchairManager {
// MARK: PRIVATE Alert View / StoreKit Delegate Methods
#if os(iOS)
open func alertView(_ alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) {
// cancelButtonIndex is set to -1 to show the cancel button up top, but a tap on it ends up here with index 0
if (alertView.cancelButtonIndex == buttonIndex || 0 == buttonIndex) {
// they don't want to rate it
dontRate()
} else if (showsRemindButton() && 1 == buttonIndex) {
// remind them later
remindMeLater()
} else {
// they want to rate it
_rateApp()
}
}
//Delegate call from the StoreKit view.
open func productViewControllerDidFinish(_ viewController: SKStoreProductViewController!) {
@@ -1331,23 +1330,23 @@ open class Manager : ArmchairManager {
//Close the in-app rating (StoreKit) view and restore the previous status bar style.
fileprivate func closeModalPanel() {
let usedAnimation = usesAnimation
if modalPanelOpen {
UIApplication.shared.setStatusBarStyle(currentStatusBarStyle, animated:usesAnimation)
let usedAnimation = usesAnimation
modalPanelOpen = false
// get the top most controller (= the StoreKit Controller) and dismiss it
if let presentingController = UIApplication.shared.keyWindow?.rootViewController {
if let topController = Manager.topMostViewController(presentingController) {
topController.dismiss(animated: usesAnimation) {
if let closure = self.didDismissModalViewClosure {
closure(usedAnimation)
}
}
topController.dismiss(animated: usesAnimation) {}
currentStatusBarStyle = UIStatusBarStyle.default
}
}
}
if let closure = self.didDismissModalViewClosure {
closure(usedAnimation)
}
}
#elseif os(OSX)
@@ -1758,8 +1757,11 @@ open class Manager : ArmchairManager {
if let alert = ratingAlert {
debugLog("Hiding Alert")
#if os(iOS)
if alert.isVisible {
alert.dismiss(withClickedButtonIndex: alert.cancelButtonIndex, animated: false)
let isAlertVisible = alert.isViewLoaded && alert.view.window != nil
if isAlertVisible {
alert.dismiss(animated: false, completion: {
self.dontRate()
})
}
#elseif os(OSX)
if let window = NSApplication.shared.keyWindow {
@@ -1767,7 +1769,7 @@ open class Manager : ArmchairManager {
parent.endSheet(window)
}
}
#else
#endif
ratingAlert = nil