Как проверить статус подписки, прежде чем показывать первый вид в Swift
Я реализовал подписку (автоматическое продление покупки) в Swift. Я хочу показать кнопку в первом представлении, если пользователь уже подписан, а если нет, скрыть кнопку.
Я сделал код подписки, но после первого просмотра он заработал. Поэтому, если попытаться скрыть кнопку, когда подписка действительна, она будет скрыта после того, как кнопка уже появилась.
Теперь это работает так:
1. Запустите приложение
2. Выполните функцию "checkReceipt()", которая проверяет, действительна ли подписка в AppDelegate (но теперь всегда возвращает "ложь", даже если она должна быть действительной)
3. появился первый вид
4. Закончил checkReceipt () и вернул true (действует)
Я хочу проверить правильность подписки, прежде чем появился первый вид. Как я могу решить эту проблему?
(Я использую код для подписки отсюда)
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, SKPaymentManagerDelegate {
var window: UIWindow?
var isValid: Bool!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// check receipt
SKPaymentManager.shared().delegate = self
SKPaymentQueue.default().add(SKPaymentManager.shared())
isValid = SKPaymentManager.checkReceipt()
return true
}
//...
SKProductManager
import Foundation
import StoreKit
fileprivate var productManagers : Set<SKProductManager> = Set()
class SKProductManager: NSObject, SKProductsRequestDelegate {
static var subscriptionProduct : SKProduct? = nil
fileprivate var completion : (([SKProduct]?,NSError?) -> Void)?
static func getProducts(withProductIdentifiers productIdentifiers : [String],completion:(([SKProduct]?,NSError?) -> Void)?){
let productManager = SKProductManager()
productManager.completion = completion
let request = SKProductsRequest(productIdentifiers: Set(productIdentifiers))
request.delegate = productManager
request.start()
productManagers.insert(productManager)
}
static func getSubscriptionProduct(completion:(() -> Void)? = nil) {
guard SKProductManager.subscriptionProduct == nil else {
if let completion = completion {
completion()
}
return
}
let productIdentifier = "secret"
SKProductManager.getProducts(withProductIdentifiers: [productIdentifier], completion: { (_products, error) -> Void in
if let product = _products?.first {
SKProductManager.subscriptionProduct = product
}
if let completion = completion {
completion()
}
})
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
var error : NSError? = nil
if response.products.count == 0 {
error = NSError(domain: "ProductsRequestErrorDomain", code: 0, userInfo: [NSLocalizedDescriptionKey:"couldn't get product"])
}
completion?(response.products, error)
}
func request(_ request: SKRequest, didFailWithError error: Error) {
let error = NSError(domain: "ProductsRequestErrorDomain", code: 0, userInfo: [NSLocalizedDescriptionKey:"couldn't get product "])
completion?(nil,error)
productManagers.remove(self)
}
func requestDidFinish(_ request: SKRequest) {
productManagers.remove(self)
}
}
SKPaymentManager
///...
public static func checkReceipt() -> Bool {
var date = NSDate()
var check = false
do {
let reqeust = try getReceiptRequest()
let session = URLSession.shared
let task = session.dataTask(with: reqeust, completionHandler: {(data, response, error) -> Void in
guard let jsonData = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: .init(rawValue: 0)) as AnyObject
receiptStatus = ReceiptStatusError.statusForErrorCode(json.object(forKey: "status"))
guard let latest_receipt_info = (json as AnyObject).object(forKey: "latest_receipt_info") else { return }
guard let receipts = latest_receipt_info as? [[String: AnyObject]] else { return }
updateStatus(receipts: receipts)
var latest = receipts.last
date = NSDate()
if let result = latest!["expires_date"] as? String {
let expireDate = result
check = checkDifference(now: date, expireDate: expireDate)
let appDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.isValid = check
}
} catch _ {
}
})
task.resume()
} catch let error {
print("SKPaymentManager : Failure to process payment from Apple store: \(error)")
checkReceiptInLocal()
}
return check
}
/// check subscription is valid or not
fileprivate static func checkDifference(now: NSDate, expireDate: String) -> Bool{
// convert string to Date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
let expire = dateFormatter.date(from: expireDate)
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.year,.month,.weekOfMonth,.day,.hour,.minute,.second]
dateComponentsFormatter.maximumUnitCount = 1
dateComponentsFormatter.unitsStyle = .full
dateComponentsFormatter.string(from: now as Date, to: Date(timeIntervalSinceNow: 4000000)) // "1 month"
dateComponentsFormatter.string(from: expire!, to: Date(timeIntervalSinceNow: 4000000)) // "1 month"
let seconds = expire?.seconds(from: now as Date)
if seconds! > 0 {
return true
}else{
return false
}
}
///...
1 ответ
Проблема в том, что checkReceipt() не является асинхронным вызовом, поэтому при установке isValid ничего не происходит. Вы можете достичь желаемого, программно установив начальный вид приложения в AppDelegate и изменив функцию checkReceipt(), чтобы она принимала замыкание типа (Bool)->(). Если вы используете раскадровку, вам сначала нужно удалить начальный контроллер представления, сняв флажок "Начальный контроллер вида" под заголовком "Вид контроллера" в инспекторе атрибутов.
В SKPaymentManager измените checkReceipt() -> Bool {
в checkReceipt(handler:(Bool)->()) {
а потом:
check = checkDifference(now: date, expireDate: expireDate)
//delete the two lines that follow and replace them with the asynchronous call:
handler(check)
ВАЖНО: также не забудьте добавить handler(false)
внутри каждого из catch
блоки и guard
заявления else {
заблокировать, чтобы приложение загружалось даже при возникновении ошибки.
Далее в приложении (:,didFinishLaunchingWithOptions:):
//step 1: make variables for window and storyboard. Storyboard name parameter is the storyboard's filename.
var window = UIWindow()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
//step 2: instantiate your initial view controller.
let initialViewController = storyboard.instantiateViewController(withIdentifier:"identifier") as InitialViewController()!
//step 3: make the asynchronous call
checkReceipts(handler: { valid in
self.isValid = valid
initialViewController.subscribeButton.isHidden = valid == false //hide if valid
//step 4: set the window's root view controller
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
})
Наслаждаться:]