目前正致力于向Apple提交我的申请,我遇到了一个奇怪的问题,即我的产品标识符没有立即加载。有时,如果您加载商店足够快,价格不会加载,您无法进行购买。 Apple报告称他们无法在他们的ipv6网络上进行购买,但我也在ipv4上看到了这个问题。
您是否看到任何可能导致此问题和/或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
}
答案 0 :(得分:0)
经过大力测试后,我意识到我并没有推迟商店的负荷,因此产品没有满载。这不是ipv6问题。
要解决此问题,我添加了一个活动指示器并实施了一个计时器,以便在显示商店之前检查产品是否已加载。