延迟加载商店中的产品 Swift v3

Delayed Load of Products in Store Swift v3

目前正在向 Apple 提交我的申请,但我遇到了一个奇怪的问题,即我的产品标识符没有立即加载。有时,如果您加载商店的速度足够快,价格不会加载,您就无法购买。 Apple 报告说他们无法在他们的 ipv6 网络上进行购买,但我在 ipv4 上也看到了这个问题。

您是否看到任何导致此问题的逻辑问题 and/or ipv6 非友好代码?谢谢。

代码如下:

IAPManager:

import Foundation
import StoreKit
import RealmSwift
import AVFoundation

class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver
{
static let sharedInstance = IAPManager()

private let realm = try! Realm()

// SKRequest
var request:SKProductsRequest!
// Array of SKProducts
var products:[SKProduct] = []

// Audio
var audioPlayer: AVAudioPlayer!

// Received Response From Store
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

    // Check for Response
    self.products = response.products
}

// Creates String Array of Product Identifiers
func getStoreProductIdentifiers() -> [String]
{
    var identifiers: [String] = []

    let hintsPackXS = "net.identitywithheld.HintsPackXS"

    let hintsPackS = "net.identitywithheld.HintsPackS"

    let hintsPackM = "net.identitywithheld.HintsPackM"

    let hintsPackL = "net.identitywithheld.HintsPackL"

    let hintsPackXL = "net.identitywithheld.HintsPackXL"

    let removeAds = "net.identitywithheld.RemoveAds"

    identifiers.append(hintsPackXS)
    identifiers.append(hintsPackS)
    identifiers.append(hintsPackM)
    identifiers.append(hintsPackL)
    identifiers.append(hintsPackXL)
    identifiers.append(removeAds)

    return identifiers
}

// Perform Request with Identifiers
func performProductRequestForIdentifiers(identifiers:[String]){

    // Create Set Out of String Array and Type Cast as Set<String>
    // Note that sets are not in any paritcular order.
    let products = NSSet(array: identifiers) as! Set<String>

    // Set request to call based on products identifier
    self.request = SKProductsRequest(productIdentifiers: products)

    // Set Delegate to self (this class)
    self.request.delegate = self

    // Start Request
    self.request.start()
}

// Request Store Products
// Gets Identifiers and Performs Requests (Ties Together)
func requestStoreProducts(){
    self.performProductRequestForIdentifiers(identifiers: self.getStoreProductIdentifiers())
}

// Checks to see if user can make purchases
func setupPurchases() -> Bool
{
    // Check to See if Can Make Payments
    if SKPaymentQueue.canMakePayments(){

        // Sets Self As Observer
        SKPaymentQueue.default().add(self)

        return true
    }
    else
    {
        return false
    }
}

// Requests Payment Request
func createPaymentRequestForProduct(product:SKProduct)
{
    let payment = SKMutablePayment(product: product)
    payment.quantity = 1

    // Starts Payment Process
    SKPaymentQueue.default().add(payment)
}

// Payment Observer - Is Updated Whenever There's An Update
//MARK - Transaction Observer
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {

    // For Each Transaction in Transaction Array [SKPaymentTransaction]
    for transaction in transactions {
        switch transaction.transactionState{
        case .purchasing:
            print("purchasing")
            break
        case .purchased:
            print("purchased")
            complete(transaction: transaction)
            queue.finishTransaction(transaction) // Marks as Finished to Remove from Queue
            break
        case .deferred:
            print("deferred")
            break
        case .failed:
            print("failed")
            fail(transaction: transaction)
            queue.finishTransaction(transaction) // Marks as Finished to Remove from Queue
            break
        case .restored:
            print("restored")
            restore(transaction: transaction)
            queue.finishTransaction(transaction) // Marks as Finished to Remove from Queue
            break
        }
    }
}

// Complete Purchase
private func complete(transaction: SKPaymentTransaction) {
    print("complete...")
    deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)

    // Check Whether Sounds Are On
    let gameDataObj = realm.objects(GameData.self).filter("id == 0").first
    let soundOn = gameDataObj!.sound

    if(soundOn == true)
    {
        let hintSound = NSURL(fileURLWithPath: Bundle.main.path(forResource: "purchaseComplete", ofType: "caf")!)
        try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try? AVAudioSession.sharedInstance().setActive(true)

        try? audioPlayer = AVAudioPlayer(contentsOf: hintSound as URL)
        audioPlayer.volume = 0.5
        audioPlayer!.prepareToPlay()
        audioPlayer!.play()
    }

    // If On, Play Sound
}


// Restore Purchases
private func restore(transaction: SKPaymentTransaction) {
    guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
    print("restore... \(productIdentifier)")
    deliverPurchaseNotificationFor(identifier: productIdentifier)
    SKPaymentQueue.default().finishTransaction(transaction)
}

private func fail(transaction: SKPaymentTransaction) {
    print("fail...")
    if let transactionError = transaction.error as? NSError {
        if transactionError.code != SKError.paymentCancelled.rawValue {
            print("Transaction Error: \(transaction.error?.localizedDescription)")
        }
    }
    SKPaymentQueue.default().finishTransaction(transaction)
}

private func deliverPurchaseNotificationFor(identifier: String?) {
    guard let identifier = identifier else { return }

    let gameDataObj = realm.objects(GameData.self).filter("id == 0")
    var hints = gameDataObj.first!.hints
    var hintsPurchase = false

    if(identifier == "net.identitywithheld.HintsPackXS")
    {
        print("Extra Small Pack")
        hints = hints + 10
        hintsPurchase = true
    }
    else if(identifier == "net.identitywithheld.HintsPackS")
    {
        print("Small Pack")
        hints = hints + 45
        hintsPurchase = true
    }
    else if(identifier == "net.identitywithheld.HintsPackM")
    {
        print("Medium Pack")
        hints = hints + 125
        hintsPurchase = true
    }
    else if(identifier == "net.identitywithheld.HintsPackL")
    {
        print("Large Pack")
        hints = hints + 200
        hintsPurchase = true
    }
    else if(identifier == "net.identitywithheld.HintsPackXL")
    {
        print("Extra Large Pack")
        hints = hints + 500
        hintsPurchase = true
    }
    else if(identifier == "net.identitywithheld.RemoveAds")
    {
        print("Remove Ads")
        try! realm.write {
            gameDataObj.first!.ads = false
        }
    }

    // If Hint Purchase, Set Hints
    if(hintsPurchase == true)
    {
        try! realm.write {
            gameDataObj.first!.hints = hints
        }
    }
}

func getTextWidth(font: UIFont, text: String) -> CGFloat
{
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: 17, height: 17))
    label.text = text
    label.font = font
    label.sizeToFit()

    return label.frame.width
}

func getTextHeight(font: UIFont, text: String, width: CGFloat) -> CGFloat
{
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: 17))
    label.text = text
    label.font = font
    label.numberOfLines = 0
    label.sizeToFit()

    return label.frame.height
}
}

应用程序委托

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    openRealm()
    // Override point for customization after application launch.
    // Use Firebase library to configure APIs
    FIRApp.configure()
    GADMobileAds.configure(withApplicationID: adMobAppID)

    // Store Call to Get Products if Can Make Purchases

    if (IAPManager.sharedInstance.setupPurchases() == true)
    {
        IAPManager.sharedInstance.requestStoreProducts()

        UserDefaults.standard.set(true, forKey: "IAPCapable")
        UserDefaults.standard.synchronize()
    }
    else
    {
        UserDefaults.standard.set(false, forKey: "IAPCapable")
        UserDefaults.standard.synchronize()
    }

    // Initialize the Chartboost library
    Chartboost.start(withAppId: "identitywithheld", appSignature: "identitywithheld", delegate: nil)

    return true
}

经过大力测试后,我意识到我并没有延迟商店加载,因此产品没有完全加载。这不是 ipv6 问题。

为了解决这个问题,我添加了一个 activity 指示器并实现了一个计时器来检查并查看是否在显示商店之前加载了产品。