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