update app_review
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface AppReviewPlugin : NSObject<FlutterPlugin>
|
||||
@end
|
||||
@@ -1,8 +0,0 @@
|
||||
#import "AppReviewPlugin.h"
|
||||
#import <app_review/app_review-Swift.h>
|
||||
|
||||
@implementation AppReviewPlugin
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
[SwiftAppReviewPlugin registerWithRegistrar:registrar];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,134 @@
|
||||
import Flutter
|
||||
import StoreKit
|
||||
import SwiftUI
|
||||
|
||||
public class AppReviewPlugin: NSObject, FlutterPlugin, AppReviewApi {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let instance = AppReviewPlugin()
|
||||
AppReviewApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance)
|
||||
}
|
||||
|
||||
public func requestReview(testMode: Bool, completion: @escaping (Result<String?, Error>) -> Void) {
|
||||
if #available(iOS 16.0, *) {
|
||||
DispatchQueue.main.async {
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
|
||||
let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }) {
|
||||
let controller = UIHostingController(rootView: ReviewRequestView())
|
||||
controller.view.isHidden = true
|
||||
controller.view.frame = .zero
|
||||
keyWindow.addSubview(controller.view)
|
||||
// Triggering onAppear by adding to hierarchy.
|
||||
}
|
||||
}
|
||||
completion(.success("Requested Review via SwiftUI"))
|
||||
} else if #available(iOS 14.0, *) {
|
||||
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
|
||||
SKStoreReviewController.requestReview(in: scene)
|
||||
completion(.success("Requested Review"))
|
||||
} else {
|
||||
SKStoreReviewController.requestReview()
|
||||
completion(.success("Requested Review"))
|
||||
}
|
||||
} else if #available(iOS 10.3, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
completion(.success("Requested Review"))
|
||||
} else {
|
||||
completion(.success("Review not available"))
|
||||
}
|
||||
}
|
||||
|
||||
public func isRequestReviewAvailable(completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
if #available(iOS 10.3, *) {
|
||||
completion(.success(true))
|
||||
} else {
|
||||
completion(.success(false))
|
||||
}
|
||||
}
|
||||
|
||||
public func getBundleId(completion: @escaping (Result<String, Error>) -> Void) {
|
||||
completion(.success(Bundle.main.bundleIdentifier ?? ""))
|
||||
}
|
||||
|
||||
public func openStoreListing(storeId: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let storeId = storeId, !storeId.isEmpty else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 404, userInfo: [NSLocalizedDescriptionKey: "Store ID is missing"])))
|
||||
return
|
||||
}
|
||||
let urlString = "https://apps.apple.com/app/id\(storeId)"
|
||||
if let url = URL(string: urlString) {
|
||||
UIApplication.shared.open(url, options: [:]) { success in
|
||||
if success {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 500, userInfo: [NSLocalizedDescriptionKey: "Failed to open store listing"])))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 400, userInfo: [NSLocalizedDescriptionKey: "Invalid Store URL"])))
|
||||
}
|
||||
}
|
||||
|
||||
public func openAppStoreReview(storeId: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let storeId = storeId, !storeId.isEmpty else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 404, userInfo: [NSLocalizedDescriptionKey: "Store ID is missing"])))
|
||||
return
|
||||
}
|
||||
let urlString = "https://apps.apple.com/app/id\(storeId)?action=write-review"
|
||||
if let url = URL(string: urlString) {
|
||||
UIApplication.shared.open(url, options: [:]) { success in
|
||||
if success {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 500, userInfo: [NSLocalizedDescriptionKey: "Failed to open App Store review"])))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 400, userInfo: [NSLocalizedDescriptionKey: "Invalid Store URL"])))
|
||||
}
|
||||
}
|
||||
|
||||
public func lookupAppId(bundleId: String, countryCode: String?, completion: @escaping (Result<String?, Error>) -> Void) {
|
||||
let country = countryCode ?? ""
|
||||
let urlString = "https://itunes.apple.com/\(country)/lookup?bundleId=\(bundleId)"
|
||||
guard let url = URL(string: urlString) else {
|
||||
completion(.failure(NSError(domain: "AppReview", code: 400, userInfo: [NSLocalizedDescriptionKey: "Invalid Lookup URL"])))
|
||||
return
|
||||
}
|
||||
|
||||
let task = URLSession.shared.dataTask(with: url) { data, response, error in
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
guard let data = data else {
|
||||
completion(.success(nil))
|
||||
return
|
||||
}
|
||||
do {
|
||||
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
||||
let results = json["results"] as? [[String: Any]],
|
||||
let firstResult = results.first,
|
||||
let trackId = firstResult["trackId"] as? Int {
|
||||
completion(.success(String(trackId)))
|
||||
} else {
|
||||
completion(.success(nil))
|
||||
}
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
struct ReviewRequestView: View {
|
||||
@Environment(\.requestReview) var requestReview
|
||||
|
||||
var body: some View {
|
||||
EmptyView()
|
||||
.onAppear {
|
||||
requestReview()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
/// Error class for passing custom error details to Dart side.
|
||||
final class PigeonError: Error {
|
||||
let code: String
|
||||
let message: String?
|
||||
let details: Sendable?
|
||||
|
||||
init(code: String, message: String?, details: Sendable?) {
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.details = details
|
||||
}
|
||||
|
||||
var localizedDescription: String {
|
||||
return
|
||||
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
|
||||
}
|
||||
}
|
||||
|
||||
private func wrapResult(_ result: Any?) -> [Any?] {
|
||||
return [result]
|
||||
}
|
||||
|
||||
private func wrapError(_ error: Any) -> [Any?] {
|
||||
if let pigeonError = error as? PigeonError {
|
||||
return [
|
||||
pigeonError.code,
|
||||
pigeonError.message,
|
||||
pigeonError.details,
|
||||
]
|
||||
}
|
||||
if let flutterError = error as? FlutterError {
|
||||
return [
|
||||
flutterError.code,
|
||||
flutterError.message,
|
||||
flutterError.details,
|
||||
]
|
||||
}
|
||||
return [
|
||||
"\(error)",
|
||||
"\(type(of: error))",
|
||||
"Stacktrace: \(Thread.callStackSymbols)",
|
||||
]
|
||||
}
|
||||
|
||||
private func isNullish(_ value: Any?) -> Bool {
|
||||
return value is NSNull || value == nil
|
||||
}
|
||||
|
||||
private func nilOrValue<T>(_ value: Any?) -> T? {
|
||||
if value is NSNull { return nil }
|
||||
return value as! T?
|
||||
}
|
||||
|
||||
|
||||
private class MessagesPigeonCodecReader: FlutterStandardReader {
|
||||
}
|
||||
|
||||
private class MessagesPigeonCodecWriter: FlutterStandardWriter {
|
||||
}
|
||||
|
||||
private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter {
|
||||
override func reader(with data: Data) -> FlutterStandardReader {
|
||||
return MessagesPigeonCodecReader(data: data)
|
||||
}
|
||||
|
||||
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
|
||||
return MessagesPigeonCodecWriter(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter())
|
||||
}
|
||||
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol AppReviewApi {
|
||||
/// Request review.
|
||||
///
|
||||
/// Tells StoreKit / Play Store to ask the user to rate or review your app, if appropriate.
|
||||
/// Supported only in iOS 10.3+ and Android with Play Services installed (see [isRequestReviewAvailable]).
|
||||
///
|
||||
/// Returns string with details message.
|
||||
func requestReview(testMode: Bool, completion: @escaping (Result<String?, Error>) -> Void)
|
||||
/// Check if [requestReview] feature available.
|
||||
func isRequestReviewAvailable(completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
/// Opens the store listing for the specified app.
|
||||
///
|
||||
/// [storeId] is the package name (Android) or App ID (iOS/macOS).
|
||||
func openStoreListing(storeId: String?, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
/// Opens the App Store review page (iOS/macOS only).
|
||||
///
|
||||
/// [storeId] is the App ID.
|
||||
func openAppStoreReview(storeId: String?, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
/// Returns package name for application.
|
||||
func getBundleId(completion: @escaping (Result<String, Error>) -> Void)
|
||||
/// Requests the App ID from the App Store (via iTunes Lookup API).
|
||||
///
|
||||
/// [bundleId] is the bundle identifier to look up.
|
||||
/// [countryCode] is the optional country code.
|
||||
func lookupAppId(bundleId: String, countryCode: String?, completion: @escaping (Result<String?, Error>) -> Void)
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
class AppReviewApiSetup {
|
||||
static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared }
|
||||
/// Sets up an instance of `AppReviewApi` to handle messages through the `binaryMessenger`.
|
||||
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: AppReviewApi?, messageChannelSuffix: String = "") {
|
||||
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
|
||||
/// Request review.
|
||||
///
|
||||
/// Tells StoreKit / Play Store to ask the user to rate or review your app, if appropriate.
|
||||
/// Supported only in iOS 10.3+ and Android with Play Services installed (see [isRequestReviewAvailable]).
|
||||
///
|
||||
/// Returns string with details message.
|
||||
let requestReviewChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.app_review.AppReviewApi.requestReview\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
requestReviewChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let testModeArg = args[0] as! Bool
|
||||
api.requestReview(testMode: testModeArg) { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestReviewChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// Check if [requestReview] feature available.
|
||||
let isRequestReviewAvailableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.app_review.AppReviewApi.isRequestReviewAvailable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
isRequestReviewAvailableChannel.setMessageHandler { _, reply in
|
||||
api.isRequestReviewAvailable { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isRequestReviewAvailableChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// Opens the store listing for the specified app.
|
||||
///
|
||||
/// [storeId] is the package name (Android) or App ID (iOS/macOS).
|
||||
let openStoreListingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.app_review.AppReviewApi.openStoreListing\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
openStoreListingChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let storeIdArg: String? = nilOrValue(args[0])
|
||||
api.openStoreListing(storeId: storeIdArg) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
reply(wrapResult(nil))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
openStoreListingChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// Opens the App Store review page (iOS/macOS only).
|
||||
///
|
||||
/// [storeId] is the App ID.
|
||||
let openAppStoreReviewChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.app_review.AppReviewApi.openAppStoreReview\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
openAppStoreReviewChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let storeIdArg: String? = nilOrValue(args[0])
|
||||
api.openAppStoreReview(storeId: storeIdArg) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
reply(wrapResult(nil))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
openAppStoreReviewChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// Returns package name for application.
|
||||
let getBundleIdChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.app_review.AppReviewApi.getBundleId\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
getBundleIdChannel.setMessageHandler { _, reply in
|
||||
api.getBundleId { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getBundleIdChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// Requests the App ID from the App Store (via iTunes Lookup API).
|
||||
///
|
||||
/// [bundleId] is the bundle identifier to look up.
|
||||
/// [countryCode] is the optional country code.
|
||||
let lookupAppIdChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.app_review.AppReviewApi.lookupAppId\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
lookupAppIdChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let bundleIdArg = args[0] as! String
|
||||
let countryCodeArg: String? = nilOrValue(args[1])
|
||||
api.lookupAppId(bundleId: bundleIdArg, countryCode: countryCodeArg) { result in
|
||||
switch result {
|
||||
case .success(let res):
|
||||
reply(wrapResult(res))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lookupAppIdChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
public class SwiftAppReviewPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "app_review", binaryMessenger: registrar.messenger())
|
||||
let instance = SwiftAppReviewPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
var appID = "com.apple.appstore" //Your App ID on App Store
|
||||
if let bundleIdentifier = Bundle.main.bundleIdentifier {
|
||||
appID = bundleIdentifier
|
||||
}
|
||||
|
||||
switch (call.method) {
|
||||
case "requestReview":
|
||||
//App Store Review
|
||||
if #available(iOS 10.3, *) {
|
||||
SKStoreReviewController.requestReview() // Requesting alert view for getting rating from the user.
|
||||
result("Later than iOS 10.3 the App will Request the App Review, Users can turn this off in settings for all apps, Apple will manage when to request the review from the user. In Debug it will always show. Requesting review for: " + appID)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
result("Prior to iOS 10.3 App Review from App is not available. You should go to Store Page of App: " + appID + ". If the app is not published the app will not be found.")
|
||||
}
|
||||
case "isRequestReviewAvailable":
|
||||
if #available(iOS 10.3, *) {
|
||||
result("1")
|
||||
} else {
|
||||
result("0")
|
||||
}
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user