Storekit и Spritekit - пустой массив продуктов - PullRequest
0 голосов
/ 17 марта 2019

Вот код для менеджера магазина:

import Foundation
import StoreKit

//MARK: Protocols

protocol StoreManagerDelegate: class {
  func updateWithProducts(products:[SKProduct])
  func refreshPurchaseStatus()
}

//MARK: Store Manager class

class StoreManager: NSObject {

  weak var delegate: StoreManagerDelegate?
  var loadedProducts: [SKProduct] = []

  override init() {
    super.init()
    SKPaymentQueue.default().add(self)
    getProductList()
  }

  //MARK: Request products

  func getProductList() {
    if SKPaymentQueue.canMakePayments() {
      let products = NSSet(array: ProductList.products)
      let request = SKProductsRequest(productIdentifiers: products as! Set<String>)
      request.delegate = self
      request.start()
    }
  }

  //MARK: Purchasing

  func restoreCompletedTransactions() {
    SKPaymentQueue.default().restoreCompletedTransactions()
  }

  func purchaseProduct(product: SKProduct) {
    let payment = SKPayment(product: product)
    SKPaymentQueue.default().add(payment)
  }

}

extension StoreManager: SKProductsRequestDelegate {

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    loadedProducts = response.products
    if loadedProducts.count != 0 {
        delegate?.updateWithProducts(products: loadedProducts)
    }
  }

}

extension StoreManager: SKPaymentTransactionObserver {

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
    for transaction:AnyObject in transactions {
      if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
        switch trans.transactionState {
        case .purchased:
          //validateReceipt(transaction as! SKPaymentTransaction)

            ProductDelivery.deliverProduct(product: trans.payment.productIdentifier)
            SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
          self.delegate?.refreshPurchaseStatus()
          break
        case .failed:
            SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
          break
        case .restored:
          //validateReceipt(transaction as! SKPaymentTransaction)

            ProductDelivery.deliverProduct(product: trans.payment.productIdentifier)
            SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
          self.delegate?.refreshPurchaseStatus()
          break
        default:
          break
        }
      }
    }
  }

}

extension SKProduct {

  func localizedPrice() -> String {
    let formatter = NumberFormatter()
    //formatter.numberStyle = .CurrencyStyle
    formatter.locale = self.priceLocale
    return formatter.string(from: self.price)!
  }

}

Вот код магазина товаров:

import Foundation
import StoreKit

//MARK: Add your products here
var core = UserDefaults.standard.integer(forKey: "cores")

struct ProductList {
    static let buy50    : String = "com.AlienBeing.lightslicer.buy50"
    static let buy150    : String = "com.AlienBeing.lightslicer.buy150"
    static let buy400    : String = "com.AlienBeing.lightslicer.buy400"
    static let buy750    : String = "com.AlienBeing.lightslicer.buy750"
    static let buy1200    : String = "com.AlienBeing.lightslicer.buy1201"
    static let buy2500    : String = "com.AlienBeing.lightslicer.buy2500"
    static let buy5500    : String = "com.AlienBeing.lightslicer.buy5500"


  static let products = [buy50, buy150, buy400, buy750, buy1200, buy2500, buy5500]
}

//MARK: Deliver your products here

struct ProductDelivery {

  static let validatePurchase:Bool = true

  static func deliverProduct(product: String) {
    switch product {
    case ProductList.buy50:
        deliverConsumable(identifier: ProductList.buy50, units: 50)
    case ProductList.buy150:
        deliverConsumable(identifier: ProductList.buy150, units: 150)
    case ProductList.buy400:
        deliverConsumable(identifier: ProductList.buy400, units: 400)
    case ProductList.buy750:
        deliverConsumable(identifier: ProductList.buy750, units: 750)
    case ProductList.buy1200:
        deliverConsumable(identifier: ProductList.buy1200, units: 1200)
    case ProductList.buy2500:
        deliverConsumable(identifier: ProductList.buy2500, units: 2500)
    case ProductList.buy5500:
        deliverConsumable(identifier: ProductList.buy5500, units: 5500)
        break

    default:
      break
    }
  }



  //MARK: Non-consumable products

  static func deliverNonconsumable(identifier: String) {
    UserDefaults.standard.set(true, forKey: identifier)
    UserDefaults.standard.synchronize()
  }

  static func isProductAvailable(identifier: String) -> Bool {
    if UserDefaults.standard.bool(forKey: identifier) == true {
      return true
    } else if (identifier == "com.AlienBeing.lightslicer.buy50") {
      return true
    }
    else if (identifier == "com.AlienBeing.lightslicer.buy150") {
        return true
    }
    else if (identifier == "com.AlienBeing.lightslicer.buy400") {
        return true
    }
    else if (identifier == "com.AlienBeing.lightslicer.buy750") {
        return true
    }
    else if (identifier == "com.AlienBeing.lightslicer.buy1201") {
        return true
    }
    else if (identifier == "com.AlienBeing.lightslicer.buy2500") {
        return true
    }
    else if (identifier == "com.AlienBeing.lightslicer.buy5500") {
        return true
    }
    else {
      return false
    }
  }

  //MARK: Consumable product

    static func deliverConsumable(identifier: String, units: Int) {
        let currentUnits:Int = UserDefaults.standard.integer(forKey: "cores")
        UserDefaults.standard.set(currentUnits + units, forKey: identifier)
        UserDefaults.standard.set(currentUnits + units, forKey: "cores")
        UserDefaults.standard.synchronize()
    }

    static func remainingUnits(identifier: String) -> Int {
        return UserDefaults.standard.integer(forKey: identifier)
    }


}

Вот код для содержимого приложения:

import Foundation

enum ProductType {
  case nonconsumable
  case consumable
}

struct AppContent {
  static let main = [
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy50", purchaseType: .consumable, content: ""),
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy150", purchaseType: .consumable, content: ""),
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy400", purchaseType: .consumable, content: ""),
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy750", purchaseType: .consumable, content: ""),
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy1201", purchaseType: .consumable, content: ""),
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy2500", purchaseType: .consumable, content: ""),
    ContentItem(identifier: "com.AlienBeing.lightslicer.buy5500", purchaseType: .consumable, content: "")
  ]
}

struct ContentItem {
  var identifier:String
  var purchaseType:ProductType
  var content:String
}

Вот код магазина:

import UIKit
import SpriteKit
import Darwin
import KeychainAccess
import StoreKit




class coreStore: SKScene{
    let core = UserDefaults.standard.integer(forKey: "cores")

    let storeManager = StoreManager()

    var coreLabelNode: SKLabelNode!
    var mainMenuButton: SKSpriteNode!

    var buy50: SKSpriteNode!
    var buy150: SKSpriteNode!
    var buy400: SKSpriteNode!
    var buy750: SKSpriteNode!
    var buy1200: SKSpriteNode!
    var buy2500: SKSpriteNode!
    var buy5500: SKSpriteNode!
    //var helper: IAPHelper

    //var coreStoreView = coreStore()

    var products = [SKProduct]()


    override func didMove(to view: SKView) {
        storeManager.delegate = self
        let possibleProducts = ["com.AlienBeing.lightslicer.buy50", "com.AlienBeing.lightslicer.buy150", "com.AlienBeing.lightslicer.buy400", "com.AlienBeing.lightslicer.buy750", "com.AlienBeing.lightslicer.buy1201", "com.AlienBeing.lightslicer.buy2500", "com.AlienBeing.lightslicer.buy5500"]
        //let possibleValues = [50, 150, 400, 750, 1200, 2500, 5500]

        let core = UserDefaults.standard.integer(forKey: "cores")
        coreLabelNode = self.childNode(withName: "coreLabel") as! SKLabelNode
        coreLabelNode.text = "\(core)"

        mainMenuButton = self.childNode(withName: "mainMenuButton") as! SKSpriteNode
        mainMenuButton.texture = SKTexture(imageNamed: "MainMenuButton")

        buy50 = self.childNode(withName: "buy50") as! SKSpriteNode
        buy150 = self.childNode(withName: "buy150") as! SKSpriteNode
        buy400 = self.childNode(withName: "buy400") as! SKSpriteNode
        buy750 = self.childNode(withName: "buy750") as! SKSpriteNode
        buy1200 = self.childNode(withName: "buy1200") as! SKSpriteNode
        buy2500 = self.childNode(withName: "buy2500") as! SKSpriteNode
        buy5500 = self.childNode(withName: "buy5500") as! SKSpriteNode

        buy50.texture = SKTexture(imageNamed: "buy50")
        buy150.texture = SKTexture(imageNamed: "buy150")
        buy400.texture = SKTexture(imageNamed: "buy400")
        buy750.texture = SKTexture(imageNamed: "buy750")
        buy1200.texture = SKTexture(imageNamed: "buy1200")
        buy2500.texture = SKTexture(imageNamed: "buy2500")
        buy5500.texture = SKTexture(imageNamed: "buy5500")


        buy50.userData = ["Index":0, "Product": possibleProducts[0]]
        buy150.userData = ["Index":1, "Product": possibleProducts[1]]
        buy400.userData = ["Index":2, "Product": possibleProducts[2]]
        buy750.userData = ["Index":3, "Product": possibleProducts[3]]
        buy1200.userData = ["Index":4, "Product": possibleProducts[4]]
        buy2500.userData = ["Index":5, "Product": possibleProducts[5]]
        buy5500.userData = ["Index":6, "Product": possibleProducts[6]]

        buy50.name = "C\(0)"
        buy150.name = "C\(1)"
        buy400.name = "C\(2)"
        buy750.name = "C\(3)"
        buy1200.name = "C\(4)"
        buy2500.name = "C\(5)"
        buy5500.name = "C\(6)"

        //storeManager.delegate?.updateWithProducts(products: products)

        refreshPurchaseStatus()

//        products.append(buy50.userData!["Product"] as! SKProduct)
//        products.append(buy150.userData!["Product"] as! SKProduct)
//        products.append(buy400.userData!["Product"] as! SKProduct)
//        products.append(buy750.userData!["Product"] as! SKProduct)
//        products.append(buy1200.userData!["Product"] as! SKProduct)
//        products.append(buy2500.userData!["Product"] as! SKProduct)
//        products.append(buy5500.userData!["Product"] as! SKProduct)



    }






    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        for touch: AnyObject in touches {
            print("gottotouch")
            let location = touch.location(in: self)
            for node in nodes(at: location) {
                print("gottoloop")
                if let nodename = node.name {
                    print("gottonodename")
                    if nodename.hasPrefix("C") {
                        print("gottoprefix")
                        print(nodename)
                        print(node.userData!["Product"] as! String)
                        print(products)



                        for product in products {
                            print(product.productIdentifier)
                            if product.productIdentifier == node.userData!["Product"] as! String {
                                print("gototpayment")
                                storeManager.purchaseProduct(product: product)
                                coreLabelNode.text = "\(core)"
                            }
                        }
                    }
                }
            }
        }
        let touch = touches.first
        //interstitialAd = createAndLoadInterstitial()

        if let location = touch?.location(in: self){
            let nodesArray = self.nodes(at: location)
            if nodesArray.first?.name == "mainMenuButton"{
                let transition = SKTransition.flipHorizontal(withDuration: 0.5)
                let menuScene = SKScene(fileNamed: "MenuScene")
                menuScene?.scaleMode = .aspectFit
                self.view!.presentScene(menuScene!, transition: transition)
            }

        }
    }
}
extension coreStore: StoreManagerDelegate {

    func updateWithProducts(products:[SKProduct]) {
        self.products = products
    }

    func refreshPurchaseStatus() {
        for node in children {
            if let nodename = node.name {
                if nodename.hasPrefix("C") {
                    if ProductDelivery.isProductAvailable(identifier: node.userData!["Product"] as! String) {
                        node.alpha = 1.0
                    } else {
                        node.alpha = 0.2
                    }
                }
            }
        }
    }

}

Я считаю, что проблема здесь, в магазине:

for product in products {
    print(product.productIdentifier)
    if product.productIdentifier == node.userData!["Product"] as! String {
          print("gototpayment")
          storeManager.purchaseProduct(product: product)
          coreLabelNode.text = "\(core)"
 }

нажатие одного из узлов SKSpriteNode, например, buy150, выведет:

gottoloop gottotouch gottoloop gottonodename gottoprefix С1 com.AlienBeing.lightslicer.buy150 []

Основная проблема заключается в том, что он должен открывать окно покупки itunes в среде «песочницы», но он никогда не обращается к вызову storeManager.purchaseProduct (product: product) потому что массив продуктов пуст ... я не знаю

, так как печать (продукты) непосредственно перед всегда заканчивается [], массив SKProduct продуктов не заполняется правильными продуктами. Я не верю, что он должен быть пустым, но я мог бы что-то упустить при его заполнении. Я застрял на этом некоторое время. Мог бы действительно использовать некоторую помощь :)

Я должен добавить, что я не очень хороший программист, или, скорее, я все еще учусь в колледже и все еще тренируюсь, поэтому любые полезные советы, где я могу узнать больше лучших практик, также будут оценены, спасибо вам, мастера будущего. (Это серьезно, что вы, ребята, здесь, и не забывайте это)

...