1

我在 Swift 中实现了订阅(自动续订购买)。如果用户已经下标,我想在第一个视图中显示一个按钮,如果没有,则隐藏该按钮。

我制作了订阅代码,但在第一个视图出现后它就起作用了。因此,如果在订阅有效时尝试隐藏按钮,它将在按钮已经出现后隐藏。

现在它的工作原理如下:
1. 启动应用程序
2. 执行函数“checkReceipt()”检查 AppDelegate 中的订阅是否有效(但现在即使它应该有效也总是返回“false”)
3. 第一个视图出现
4. 完成 checkReceipt() 并返回 true(有效)

我想在第一个视图出现之前检查订阅是否正确有效。我怎么解决这个问题?

(我使用这里的订阅代码)

应用委托

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
    }
//...

SK产品经理

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
    }

}
///...
4

1 回答 1

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 {块内添加,以便即使出现错误也可以加载应用程序。

接下来,在 application(:,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()
}) 

享受 :]

于 2018-07-24T12:16:12.707 回答