44

我已经看到了一些答案,但对它们不满意并得到了一些想法,但不知道如何正确使用它,以便它以正确的方式执行,尽管我认为它应该在 App delegates 中使用didFinishLaunching,但我想确保在 Live 应用程序中实现它之前没有任何喧嚣。 SKStore​Review​Controller仅适用于我阅读的 ios 10.3,任何人都可以用 swift 和客观 c 中的一点代码来解释。

更新:

实际上我对调用该方法感到困惑request​Review(),我需要在哪里调用这个方法?在rootViewController'sviewDidLoad还是在appDelegate's didFinishlaunching

谢谢。

4

6 回答 6

77

SKStoreReviewController在 iOS 10.3 及更高版本中可用。

根据 APPLE 的文件:

您可以要求用户在使用您的应用时对其进行评分或评论,而无需将其发送到 App Store。您可以确定用户体验中调用 API 有意义的点,系统会处理其余的事情.

为了在应用程序中显示评分/评论,您必须添加StoreKit框架。

请找到两种语言的示例代码:

目标 C:

#import <StoreKit/StoreKit.h>

- (void)DisplayReviewController {
    if([SKStoreReviewController class]){
       [SKStoreReviewController requestReview] ;
    }
}

从 xCode 9 开始,您可以执行以下操作:

#import <StoreKit/StoreKit.h>

- (void)DisplayReviewController {
    if (@available(iOS 10.3, *)) {
        [SKStoreReviewController requestReview];
    }
}

迅速:

import StoreKit

func DisplayReviewController {
    if #available( iOS 10.3,*){
        SKStoreReviewController.requestReview()
    }
}

更新:仅在用户表现出与您的应用互动后才要求评分

于 2017-04-05T09:51:21.127 回答
9

对于斯威夫特,

import StoreKit

当你想问的时候,添加下面的代码来请求。

if #available(iOS 10.3, *) {
        SKStoreReviewController.requestReview()
    }

对于目标 C,

1-) 从Link Binary With Library添加了 StoreKit 框架 在此处输入图像描述

2-) 添加框架

#import <StoreKit/StoreKit.h>

3-) 在您要调用 App-Review 弹出窗口的地方添加了以下代码。在这种情况下,我在 viewDidLoad 中添加了。

  - (void)viewDidLoad {
        [super viewDidLoad];
        [SKStoreReviewController requestReview];
    }

4-) 当您在调试模式下测试时,您应该了解 Apple 的以下说明

当您在应用程序仍处于开发模式时调用此方法时,将始终显示评分/评论请求视图,以便您可以测试用户界面和体验。但是,当您在使用 TestFlight 分发的应用程序中调用此方法时,该方法无效。

于 2017-09-22T06:17:55.830 回答
6

我认为直接调用下面不是一个好主意

SKStoreReviewController.requestReview()

它可以像每当用户打开您的应用程序 10(10,20,30,...100) 的倍数时那样完成,然后您可以显示以供审核

因此,首先您需要创建一个文件,该文件将负责所有事情,例如将应用程序打开计数保存在用户默认值中、检索应用程序打开计数和显示 requestReview() 请查看以下代码片段

import Foundation
import StoreKit
class  SpsRateManager {
    
    
    private static let instance = SpsRateManager()
    
    var shareinstance: SpsRateManager{
        return .instance
    }
    static func incrementAppOpenedCount() { // called from appdelegate didfinishLaunchingWithOptions:
        let userdefault = UserDefaults.standard
        
        
        let savedvalue = userdefault.integer(forKey: Configuration.APPLICATIONOPENCOUNTSTATUS)
        if savedvalue == 0 {
            print("Not saved ")
            userdefault.set(1, forKey: Configuration.APPLICATIONOPENCOUNTSTATUS)
        }
        else{
            userdefault.set(savedvalue+1, forKey: Configuration.APPLICATIONOPENCOUNTSTATUS)
            
        }
        
    }
    
    static func checkAppopencountandProvideReview(){
        let userdefault = UserDefaults.standard
        
        
        let appopencountvalue  = userdefault.integer(forKey: Configuration.APPLICATIONOPENCOUNTSTATUS)
        if appopencountvalue % 10 == 0 {
            print("its been 10 times so ask for review ")
            SpsRateManager().requestReview()
        }
        else{
            print("not enough open count dont show ")
        }
        
    }
    
    
    
    
    fileprivate func requestReview() {
        if #available(iOS 10.3, *) {
            SKStoreReviewController.requestReview()
        } else {
            // Fallback on earlier versions
            // Try any other 3rd party or manual method here.
        }
    }
    
}
于 2018-04-03T12:30:28.140 回答
4

添加到上面korat的好答案......

如果您支持旧的 Objective-C 应用程序并且您想在几个应用程序打开后调用 DisplayReviewController,请执行以下操作:

在您的课程 AppDelegate.m 中添加以下内容:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  int count = [[NSUserDefaults standardUserDefaults] integerForKey:@"LaunchCount"];
  if(count < 0) count = 0;
  [[NSUserDefaults standardUserDefaults] setInteger:count+1 forKey:@"LaunchCount"];
}

//The application was in background and become active
- (void)applicationWillEnterForeground:(UIApplication *)application {
  int count = [[NSUserDefaults standardUserDefaults] integerForKey:@"LaunchCount"];
  if(count < 0) count = 0;
  [[NSUserDefaults standardUserDefaults] setInteger:count+1 forKey:@"LaunchCount"];
}

并在您要调用该函数的控制器中:

- (void)applicationDidBecomeActive {

  if ([[NSUserDefaults standardUserDefaults] integerForKey:@"LaunchCount"] == 5) {
     [self DisplayReviewController];
  }
}
于 2018-07-26T16:37:09.347 回答
1

这是我正在为自己的用例开发的实用程序功能,它可能会帮助很多其他人。(请随意烘烤和改进/更正我的代码:D)。我正在开发一个语音练习应用程序,我想在用户完成一些录音后要求评分。我将添加主函数,然后在它下面使用其他辅助函数。简单的逻辑是,您一年可以请求审查 3 次,所以如果1 年过去了,我将请求计数重置为 0。此外,不会针对每个请求提出审查请求。因此,在我不再允许该应用程序尝试审查请求之前,我有 30 个请求的上限。如果应用程序版本已更改,则不会考虑这一点,因为您可以再次要求对新应用程序版本进行审核。

/// Requests review from user based on certain conditions.
/// 1. Should have recorderd at least 3 recordings (if you want to force attept a review ask don't pass any parameter)
/// 2. Has not already asked for a review today
/// 3. A probabitly of 50% if will ask today
/// 4. If review has not been asked more than 30 times in the same year for the current version
/// - Parameter numberOfRecordings: If the number of recordings is greater than 3 then a review will be asked.
func askForReview(numberOfRecordings: Int = 5) {
    let defaults = UserDefaults.standard
    let lastAskedReviewAt = defaults.double(forKey: lastAskedReviewAtKey)
    let dateStringForLastReviewAsk = getDateString(from: lastAskedReviewAt)
    let dateForLastReviewAsk = getDate(from: dateStringForLastReviewAsk) ?? Date(timeIntervalSince1970: 0)
    let askedReviewToday = Calendar.current.isDateInToday(dateForLastReviewAsk)
    var appReviewRequestsCount = defaults.integer(forKey: appReviewRequestsCountKey)
    
    if Date().localDate().years(from: dateForLastReviewAsk) >= 1 {
        defaults.setValue(0, forKey: appReviewRequestsCountKey)
        appReviewRequestsCount = 0
    }
    
    var isAskingReviewForSameVersion = false
    
    if let currentlyInstalledVersion = getInstalledVersionNumber(), let lastReviewAskedForVersion = defaults.string(forKey: lastReviewAskedForVersionKey) {
        if currentlyInstalledVersion == lastReviewAskedForVersion {
            isAskingReviewForSameVersion = true
        } else {
            appReviewRequestsCount = 0
            defaults.setValue(0, forKey: appReviewRequestsCountKey)
        }
    }
    
    let askingReviewTooManyTimes = appReviewRequestsCount >= 30 && isAskingReviewForSameVersion
    
    let totalRecordingsTillDateCount = defaults.integer(forKey: totalRecordingsTillDateCountKey)
    let localNumberOfRecordings = max(numberOfRecordings, totalRecordingsTillDateCount)
    
    if localNumberOfRecordings > 3 && Bool.random() && !askedReviewToday && !askingReviewTooManyTimes {
        SKStoreReviewController.requestReview()
        defaults.setValue(Date().timeIntervalSince1970, forKey: lastAskedReviewAtKey)
        if let versionNumber = getInstalledVersionNumber() {
            defaults.setValue(versionNumber, forKey: lastReviewAskedForVersionKey)
        }
        defaults.setValue(appReviewRequestsCount + 1, forKey: appReviewRequestsCountKey)
    }
}

字典键:

let lastAskedReviewAtKey = "LastAskedReviewAt"
let appReviewRequestsCountKey = "AppReviewRequestsCount"
let lastReviewAskedForVersionKey = "AskedReviewForVersion"
let appVersionNumberKey = "CFBundleShortVersionString"

辅助功能:

/// Get a string representation in current local time for a timestamp
/// - Parameter timestamp: Timestamp to be converted to date string
/// - Returns: A date string from passed timestamp in dd MMM yyy format
func getDateString(from timestamp: Double) -> String {
    let dateFormatter = getDateFormatter()
    let date = Date(timeIntervalSince1970: timestamp)
    let dateString = dateFormatter.string(from: date)
    return dateString
}

/// Get a date from a string of date format dd MMM yyyy.
/// - Parameter dateString: Date string formated as dd MMM yyyy
/// - Returns: A date object by parsing date in dd MMM yyy format
func getDate(from dateString: String) -> Date? {
    //        print("Date String: ", dateString)
    let dateFormatter = getDateFormatter()
    return dateFormatter.date(from: dateString) ?? nil
}


//Ref: https://stackoverflow.com/questions/27182023/getting-the-difference-between-two-dates-months-days-hours-minutes-seconds-in
extension Date {
    /// Returns the amount of years from another date
    func years(from date: Date) -> Int {
        return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0
    }
    /// Returns the amount of months from another date
    func months(from date: Date) -> Int {
        return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0
    }
    /// Returns the amount of weeks from another date
    func weeks(from date: Date) -> Int {
        return Calendar.current.dateComponents([.weekOfMonth], from: date, to: self).weekOfMonth ?? 0
    }
    /// Returns the amount of days from another date
    func days(from date: Date) -> Int {
        return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
    }
    /// Returns the amount of hours from another date
    func hours(from date: Date) -> Int {
        return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
    }
    /// Returns the amount of minutes from another date
    func minutes(from date: Date) -> Int {
        return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
    }
    /// Returns the amount of seconds from another date
    func seconds(from date: Date) -> Int {
        return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0
    }
    /// Returns the a custom time interval description from another date
    func offset(from date: Date) -> String {
        if years(from: date)   > 0 { return "\(years(from: date))y"   }
        if months(from: date)  > 0 { return "\(months(from: date))M"  }
        if weeks(from: date)   > 0 { return "\(weeks(from: date))w"   }
        if days(from: date)    > 0 { return "\(days(from: date))d"    }
        if hours(from: date)   > 0 { return "\(hours(from: date))h"   }
        if minutes(from: date) > 0 { return "\(minutes(from: date))m" }
        if seconds(from: date) > 0 { return "\(seconds(from: date))s" }
        return ""
    }
}

//Ref: https://stackoverflow.com/questions/28404154/swift-get-local-date-and-time
extension Date {
    func localDate() -> Date {
        let nowUTC = Date()
        let timeZoneOffset = Double(TimeZone.current.secondsFromGMT(for: nowUTC))
        guard let localDate = Calendar.current.date(byAdding: .second, value: Int(timeZoneOffset), to: nowUTC) else {return Date()}

        return localDate
    }
}

func getInstalledVersionNumber() -> String? {
    guard let infoDictionary = Bundle.main.infoDictionary, let currentVersionNumber = infoDictionary[appVersionNumberKey] as? String else { return nil}
    return currentVersionNumber
}
于 2021-02-05T14:48:56.090 回答
1

我认为您可以在他们运行应用程序时实现一种计数方法并将其存储在 UserDefaults 中,然后如果计数为 5 或 10 或类似的值(这取决于您),则调用 requestReview(),这样您就有更多获得好评的机会。

于 2017-09-06T10:20:49.893 回答