StoreKit: ошибка при преобразовании из Swift 1.2 в Swift 3

Я получил пример проекта, чтобы узнать, как работать с StoreKit от Apple, чтобы я мог научиться применять автоматически обновляемую подписку на мое приложение и любое другое приложение в целом.

Проблема в том, что пример проекта пришел на Swift 1.2, и, преобразовав его в Swift 3, я обнаружил несколько ошибок, но застрял в двух предупреждениях и двух ошибках. Надеюсь, кто-нибудь может мне помочь.

Кроме того, будет ли работать код при преобразовании кода в Swift 3? Так как он старый? Изменились ли покупки в приложении каким-либо существенным образом?

Код с предупреждениями и ошибками

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

    var products = response.products

    if (products.count != 0) {
        for i in 0 ..< products.count
        {
            self.product = products[i] as? SKProduct
            self.productsArray.append(product!)
        }
        self.tableView.reloadData()
    } else {
        print("No products found")
    }

    products = response.invalidProductIdentifiers

    for product in products
    {
        print("Product not found: \(product)")
    }
}

func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    print("Transactions Restored")

    var purchasedItemIDS = []
    for transaction:SKPaymentTransaction in queue.transactions {

        if transaction.payment.productIdentifier == "com.brianjcoleman.testiap1"
        {
            print("Consumable Product Purchased")
            // Unlock Feature
        }
        else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap2"
        {
            print("Non-Consumable Product Purchased")
            // Unlock Feature
        }
        else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap3"
        {
            print("Auto-Renewable Subscription Product Purchased")
            // Unlock Feature
        }
        else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap4"
        {
            print("Free Subscription Product Purchased")
            // Unlock Feature
        }
        else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap5"
        {
            print("Non-Renewing Subscription Product Purchased")
            // Unlock Feature
        }
    }

    let alert = UIAlertView(title: "Thank You", message: "Your purchase(s) were restored.", delegate: nil, cancelButtonTitle: "OK")
    alert.show()
}

вторая ошибка

первая ошибка

Остальная часть кода для Store Kit

var tableView = UITableView()
let productIdentifiers = Set(["com.brianjcoleman.testiap1", "com.brianjcoleman.testiap2", "com.brianjcoleman.testiap3", "com.brianjcoleman.testiap4", "com.brianjcoleman.testiap5"])
var product: SKProduct?
var productsArray = Array<SKProduct>()

func requestProductData()
{
    if SKPaymentQueue.canMakePayments() {
        let request = SKProductsRequest(productIdentifiers:
            self.productIdentifiers as Set<String>)
        request.delegate = self
        request.start()
    } else {
        let alert = UIAlertController(title: "In-App Purchases Not Enabled", message: "Please enable In App Purchase in Settings", preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Settings", style: UIAlertActionStyle.default, handler: { alertAction in
            alert.dismiss(animated: true, completion: nil)

            let url: URL? = URL(string: UIApplicationOpenSettingsURLString)
            if url != nil
            {
                UIApplication.shared.openURL(url!)
            }

        }))
        alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { alertAction in
            alert.dismiss(animated: true, completion: nil)
        }))
        self.present(alert, animated: true, completion: nil)
    }
}

func buyProduct(_ sender: UIButton) {
    let payment = SKPayment(product: productsArray[sender.tag])
    SKPaymentQueue.default().add(payment)
}

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {

    for transaction in transactions {

        switch transaction.transactionState {

        case SKPaymentTransactionState.purchased:
            print("Transaction Approved")
            print("Product Identifier: \(transaction.payment.productIdentifier)")
            self.deliverProduct(transaction)
            SKPaymentQueue.default().finishTransaction(transaction)

        case SKPaymentTransactionState.failed:
            print("Transaction Failed")
            SKPaymentQueue.default().finishTransaction(transaction)
        default:
            break
        }
    }
}

func deliverProduct(_ transaction:SKPaymentTransaction) {

    if transaction.payment.productIdentifier == "com.brianjcoleman.testiap1"
    {
        print("Consumable Product Purchased")
        // Unlock Feature
    }
    else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap2"
    {
        print("Non-Consumable Product Purchased")
        // Unlock Feature
    }
    else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap3"
    {
        print("Auto-Renewable Subscription Product Purchased")
        // Unlock Feature
    }
    else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap4"
    {
        print("Free Subscription Product Purchased")
        // Unlock Feature
    }
    else if transaction.payment.productIdentifier == "com.brianjcoleman.testiap5"
    {
        print("Non-Renewing Subscription Product Purchased")
        // Unlock Feature
    }
}

func restorePurchases(_ sender: UIButton) {
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().restoreCompletedTransactions()
}

TableView

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cellFrame = CGRect(x: 0, y: 0, width: self.tableView.frame.width, height: 52.0)
    let retCell = UITableViewCell(frame: cellFrame)

    if self.productsArray.count != 0
    {
        if indexPath.row == 5
        {
            let restoreButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: UIScreen.main.bounds.width - 20.0, height: 44.0))
            restoreButton.titleLabel!.font = UIFont (name: "HelveticaNeue-Bold", size: 20)
            restoreButton.addTarget(self, action: #selector(ViewController.restorePurchases(_:)), for: UIControlEvents.touchUpInside)
            restoreButton.backgroundColor = UIColor.black
            restoreButton.setTitle("Restore Purchases", for: UIControlState())
            retCell.addSubview(restoreButton)
        }
        else
        {
            let singleProduct = productsArray[indexPath.row]

            let titleLabel = UILabel(frame: CGRect(x: 10.0, y: 0.0, width: UIScreen.main.bounds.width - 20.0, height: 25.0))
            titleLabel.textColor = UIColor.black
            titleLabel.text = singleProduct.localizedTitle
            titleLabel.font = UIFont (name: "HelveticaNeue", size: 20)
            retCell.addSubview(titleLabel)

            let descriptionLabel = UILabel(frame: CGRect(x: 10.0, y: 10.0, width: UIScreen.main.bounds.width - 70.0, height: 40.0))
            descriptionLabel.textColor = UIColor.black
            descriptionLabel.text = singleProduct.localizedDescription
            descriptionLabel.font = UIFont (name: "HelveticaNeue", size: 12)
            retCell.addSubview(descriptionLabel)

            let buyButton = UIButton(frame: CGRect(x: UIScreen.main.bounds.width - 60.0, y: 5.0, width: 50.0, height: 20.0))
            buyButton.titleLabel!.font = UIFont (name: "HelveticaNeue", size: 12)
            buyButton.tag = indexPath.row
            buyButton.addTarget(self, action: #selector(ViewController.buyProduct(_:)), for: UIControlEvents.touchUpInside)
            buyButton.backgroundColor = UIColor.black
            let numberFormatter = NumberFormatter()
            numberFormatter.numberStyle = .currency
            numberFormatter.locale = Locale.current
            buyButton.setTitle(numberFormatter.string(from: singleProduct.price), for: UIControlState())
            retCell.addSubview(buyButton)
        }
    }

    return retCell
}

1 ответ

Решение

Обе проблемы в первой функции связаны с тем, что до Swift 3 NSArrays импортировались без их общего типа (т.е. [Any], скорее, чем [SKProduct]).

Просто избавиться от as? SKProduct part исправит предупреждение, но будет проще добавить все содержимое за один вызов:

// Old
for i in 0 ..< products.count
{
    self.product = products[i] as? SKProduct
    self.productsArray.append(product!)
}

// New:
productsArray.append(contentsOf: products)

Ошибка в том, что в то время как оба response.products а также response.invalidProductIdentifiers были импортированы как [Any] изначально они сейчас набираются ([SKProduct] а также [String]). Самое простое решение - использовать массив напрямую:

// Old:
products = response.invalidProductIdentifiers

for product in products

// New:
for product in response.invalidProductIdentifiers

Так как это только печать, я бы, вероятно, просто распечатал массив напрямую.

Ошибка во второй функции заключается в том, что компилятору необходимо знать, каким типом массива должна быть переменная. Из названия, я думаю, это было задумано как [String], но он не используется (как указывает предупреждение в той же строке), так что вы можете просто удалить строку.


Полный обновленный / модернизированный / унифицированный контроллер вида:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, SKProductsRequestDelegate, SKPaymentTransactionObserver {

    enum Product: String {
        case test1 = "com.brianjcoleman.testiap1"
        case test2 = "com.brianjcoleman.testiap2"
        case test3 = "com.brianjcoleman.testiap3"
        case test4 = "com.brianjcoleman.testiap4"
        case test5 = "com.brianjcoleman.testiap5"

        static var allValues: [Product] {
             return [.test1, .test2, .test3, .test4, .test5]
        }
    }

    let tableView = UITableView()
    var productsArray = [SKProduct]()

    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.frame = self.view.frame

        tableView.separatorColor = .clear

        tableView.dataSource = self
        tableView.delegate = self

        self.view.addSubview(tableView)

        SKPaymentQueue.default().add(self)
        self.requestProductData()
    }

    override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)

        SKPaymentQueue.default().remove(self)
    }

    // In-App Purchase Methods

    func requestProductData()
    {
        if SKPaymentQueue.canMakePayments() {
            let productIdentifiers = Set(Product.allValues.map { $0.rawValue })
            let request = SKProductsRequest(productIdentifiers: productIdentifiers)
            request.delegate = self
            request.start()
        } else {
            let alert = UIAlertController(title: "In-App Purchases Not Enabled",
                                          message: "Please enable In App Purchase in Settings",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Settings", style: .default, handler: { _ in
                alert.dismiss(animated: true, completion: nil)

                if let url = URL(string: UIApplicationOpenSettingsURLString) {
                    UIApplication.shared.openURL(url)
                }
            }))
            alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { _ in
                alert.dismiss(animated: true, completion: nil)
            }))
            self.present(alert, animated: true, completion: nil)
        }
    }

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse)
    {
        let products = response.products

        if products.count != 0 {
            productsArray.append(contentsOf: products)
            self.tableView.reloadData()
        } else {
            print("No products found")
        }

        let invalidIdentifiers = response.invalidProductIdentifiers
        if invalidIdentifiers.count > 0 {
            print("Invalid product identifiers: \(invalidIdentifiers)")
        }
    }

    func buyProduct(_ sender: UIButton)
    {
        let payment = SKPayment(product: productsArray[sender.tag])
        SKPaymentQueue.default().add(payment)
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
    {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased,
                 .restored:
                print("Transaction Approved")
                print("Product Identifier: \(transaction.payment.productIdentifier)")
                self.deliverProduct(transaction)
                SKPaymentQueue.default().finishTransaction(transaction)

            case .failed:
                print("Transaction Failed")
                SKPaymentQueue.default().finishTransaction(transaction)

            case .deferred,
                 .purchasing:
                break
            }
        }
    }

    func deliverProduct(_ transaction:SKPaymentTransaction)
    {
    }

    func restorePurchases(_ sender: UIButton)
    {
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue)
    {
        print("Transactions Restored")

        for transaction in queue.transactions {
            processTransaction(transaction: transaction)
        }

        let alert = UIAlertController(title: "Thank You",
                                      message: "Your purchase(s) were restored.",
                                      preferredStyle: .alert)
        present(alert, animated: true)
    }

    private func processTransaction(transaction: SKPaymentTransaction)
    {
        guard let product = Product(rawValue: transaction.payment.productIdentifier) else {
            print("Unknown product identifier: \(transaction.payment.productIdentifier)")
            return
        }
        switch product {
        case .test1:
            print("Consumable Product Purchased")
            // Unlock Feature

        case .test2:
            print("Non-Consumable Product Purchased")
            // Unlock Feature

        case .test3:
            print("Auto-Renewable Subscription Product Purchased")
            // Unlock Feature

        case .test4:
            print("Free Subscription Product Purchased")
            // Unlock Feature

        case .test5:
            print("Non-Renewing Subscription Product Purchased")
            // Unlock Feature
        }
    }

    // Screen Layout Methods

    func numberOfSections(in tableView: UITableView) -> Int
    {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return self.productsArray.count + 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cellFrame = CGRect(x: 0, y: 0, width: self.tableView.frame.width, height: 52.0)
        let retCell = UITableViewCell(frame: cellFrame)

        if self.productsArray.count != 0 {
            if indexPath.row == Product.allValues.count
            {
                let restoreButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: UIScreen.main.bounds.width - 20.0, height: 44.0))
                restoreButton.titleLabel?.font = UIFont(name: "HelveticaNeue-Bold", size: 20)
                restoreButton.addTarget(self, action: #selector(ViewController.restorePurchases(_:)), for: .touchUpInside)
                restoreButton.backgroundColor = .black
                restoreButton.setTitle("Restore Purchases", for: .normal)
                retCell.addSubview(restoreButton)
            } else {
                let singleProduct = productsArray[indexPath.row]

                let titleLabel = UILabel(frame: CGRect(x: 10.0, y: 0.0, width: UIScreen.main.bounds.width - 20.0, height: 25.0))
                titleLabel.textColor = .black
                titleLabel.text = singleProduct.localizedTitle
                titleLabel.font = UIFont(name: "HelveticaNeue", size: 20)
                retCell.addSubview(titleLabel)

                let descriptionLabel = UILabel(frame: CGRect(x: 10.0, y: 10.0, width: UIScreen.main.bounds.width - 70.0, height: 40.0))
                descriptionLabel.textColor = .black
                descriptionLabel.text = singleProduct.localizedDescription
                descriptionLabel.font = UIFont(name: "HelveticaNeue", size: 12)
                retCell.addSubview(descriptionLabel)

                let buyButton = UIButton(frame: CGRect(x: UIScreen.main.bounds.width - 60.0, y: 5.0, width: 50.0, height: 20.0))
                buyButton.titleLabel?.font = UIFont(name: "HelveticaNeue", size: 12)
                buyButton.tag = indexPath.row
                buyButton.addTarget(self, action: #selector(ViewController.buyProduct(_:)), for: .touchUpInside)
                buyButton.backgroundColor = .black
                let numberFormatter = NumberFormatter()
                numberFormatter.numberStyle = .currency
                numberFormatter.locale = .current
                buyButton.setTitle(numberFormatter.string(from: singleProduct.price), for: .normal)
                retCell.addSubview(buyButton)
            }
        }

        return retCell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return 52.0
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        tableView.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
    {
        if section == 0 {
            return 64.0
        }

        return 32.0
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
    {
        let ret = UILabel(frame: CGRect(x: 10, y: 0, width: self.tableView.frame.width - 20, height: 32.0))
        ret.backgroundColor = .clear
        ret.text = "In-App Purchases"
        ret.textAlignment = .center
        return ret
    }
}
Другие вопросы по тегам