show localized in-app purchase price when I move in my controller - ios

I want to show localized price when I move in my controller.
I use this code:
if (SKPaymentQueue.canMakePayments())
{
let productID:NSSet = NSSet(object: self.product_id!)
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
print("Fetching Products")
}else{
print("Can't make purchases")
}
func productsRequest (_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let count : Int = response.products.count
if (count>0) {
let validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id) {
buyProduct(product: validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
func buyProduct(product: SKProduct){
price = localizedPriceForProduct(product)
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment);
self.loading.stopAnimating()
}
func localizedPriceForProduct(_ product:SKProduct) -> String {
let priceFormatter = NumberFormatter()
priceFormatter.formatterBehavior = NumberFormatter.Behavior.behavior10_4
priceFormatter.numberStyle = NumberFormatter.Style.currency
priceFormatter.locale = product.priceLocale
return priceFormatter.string(from: product.price)!
}
But in this case I see UIAlertView with login in the store. How to fix it and disappear this UIAlertView?

Related

App crashing on Subscription on live mode in Swift5

I was working on apple subscription and have set two subscription plan on apple store for "monthly" and "yearly" and have set up all the code which is working perfectly on development and adhoc. But after placing build on App Store app crashes.
I have used firebase crashlytic which detected crash I have attached screenshot crash logs.
https://i.stack.imgur.com/e487d.png
Here my code for Subscription:
func getProducts(productIDs: [String]){
//self.showLoader()
print("Start requesting products ...")
//showAlert(text:"Start requesting products ...")
let request = SKProductsRequest(productIdentifiers: Set(productIDs))
request.delegate = self
request.start()
}
func buyProduct(product: SKProduct){
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
if response.products.isEmpty {
NotificationAlert().NotificationAlert(titles: "No subscription plan")
}else{
self.myProducts = response.products
for invalidIdentifier in response.invalidProductIdentifiers {
print(invalidIdentifier)
//NotificationAlert().NotificationAlert(titles:"Invalid Identifire")
}
print("Did receive response")
for fetchedProduct in response.products{
//DispatchQueue.main.async {
print("fetchedProduct:",fetchedProduct)
//showAlert(text:"Product Fetched")
//NotificationAmyProductslert().NotificationAlert(titles: "fetchedProduct:\(fetchedProduct)")
DispatchQueue.main.asyncAfter(deadline: .now()+1.0, execute: {
let price = self.getPrice(item: fetchedProduct)
if price == ""{
} else{
self.myProducts.append(fetchedProduct)
if fetchedProduct.productIdentifier == "com.motivatory.monthly"{
DispatchQueue.main.asyncAfter(deadline: .now()+0.3, execute: {
self.oPriceMonthlyLbl.text = price ?? ""
self.monthlyprice = price ?? ""
print(price ?? "")
self.productIDs.insert("com.motivatory.monthly",at: 0)
self.selectedPrice = price ?? ""
})
} else {
DispatchQueue.main.asyncAfter(deadline: .now()+0.3, execute: {
self.oPriceYearlyLbl.text = price ?? ""
self.selectedPrice = price ?? ""
self.yearlyPrice = price ?? ""
print(price ?? "")
self.productIDs.insert("com.motivatory.yearly",at: 1)
})
}
}
})
}
}
}
func getPrice(item: SKProduct) -> String? {
let numberFormatter = NumberFormatter()
let price = item.price
let locale = item.priceLocale
numberFormatter.numberStyle = .currency
numberFormatter.locale = locale
return numberFormatter.string(from: price)
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
print(transaction)
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .purchased:
purchaseTokenStr = trans.transactionIdentifier ?? ""
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
if self.purchased == true {
print("Product Purchased")
print(purchaseTokenStr)
receiptValidation()
}
break;
case .failed:
purchaseTokenStr = trans.transactionIdentifier ?? ""
NotificationAlert().NotificationAlert(titles: "Payment failed")
print("Purchased Failed");
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
SVProgressHUD.dismiss()
break;
case .restored:
print("Already Purchased")
purchaseTokenStr = trans.transactionIdentifier ?? ""
// receiptValidation()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break
default:
break
}
}
}
}
//added by me
// 4:
func finishTransaction(_ transaction: SKPaymentTransaction) -> Bool {
let productId = transaction.payment.productIdentifier
//print("Product \(productId) successfully purchased”)
print("Product \(productId) successfully purchased")
return true
}
func restorePurchases() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
//If an error occurs, the code will\ go to this function
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
SVProgressHUD.dismiss()
NotificationAlert().NotificationAlert(titles: error.localizedDescription)
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
if queue.transactions.count == 0{
NotificationAlert().NotificationAlert(titles: "Please purchase a subscription plan")
SVProgressHUD.dismiss()
} else{
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
if self.purchaseTokenStr == t.transactionIdentifier {
switch prodID {
case "com.motivatory.yearly":
print("Your Yearly Plan has Restored Successfully")
NotificationAlert().NotificationAlert(titles: "Your Yearly Plan has Restored Successfully")
receiptValidation()
SVProgressHUD.dismiss()
break
case "com.motivatory.monthly":
print("Your Montly Plan has Restored Successfully")
NotificationAlert().NotificationAlert(titles: "Your Montly Plan has Restored Successfully")
receiptValidation()
SVProgressHUD.dismiss()
break
default:
if queue.transactions.count == 0 {
self.alert(message: "There are no restorable purchases. Only previously bought non-consumable products and auto-renewable subscriptions can be restored.", title: "Inspiration Daily Quotes")
break
}
SVProgressHUD.dismiss()
}
}
}
}
}
func receiptValidation(){
self.HitReceiptApi(Url: "https://buy.itunes.apple.com/verifyReceipt")
}
func HitReceiptApi(Url:String){
SVProgressHUD.setDefaultMaskType(.custom)
SVProgressHUD.setBackgroundLayerColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.2472442209))
SVProgressHUD.show(withStatus: "Please Wait")
let receiptFileURL = Bundle.main.appStoreReceiptURL
let receiptData = try? Data(contentsOf: receiptFileURL!)
let recieptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let jsonDict: [String: AnyObject] = ["receipt-data" : recieptString! as AnyObject, "password" : "fad24ce1475b4fdd9662a0ec31ef5a7d" as AnyObject]
print(jsonDict)
UserDefaults.standard.set(recieptString!, forKey: "recieptString")
do {
let requestData = try JSONSerialization.data(withJSONObject: jsonDict, options: JSONSerialization.WritingOptions.prettyPrinted)
let storeURL = URL(string: Url)!
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest, completionHandler: { [weak self] (data, response, error) in
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
print("=======>",jsonResponse)
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, AnyObject>
DispatchQueue.main.sync {
if json["status"] as? Int == 21007{
print("Sandbox User")
self?.HitReceiptApi(Url: "https://sandbox.itunes.apple.com/verifyReceipt")
return
} else {
print("Production")
if self!.purchaseTokenStr == ""{
} else {
self?.purchaseTokenStr = ""
if let receiptInfo2: NSArray = json["pending_renewal_info"] as? NSArray {
let lastReceipt2 = receiptInfo2.lastObject as! NSDictionary
if let original_transaction_id = lastReceipt2["original_transaction_id"] as? String {
self?.purchaseTokenStr = original_transaction_id
}
}
if let receiptInfo2: NSArray = json["latest_receipt_info"] as? NSArray {
let lastReceipt2 = receiptInfo2.firstObject as! NSDictionary
if let purchase_date = lastReceipt2["purchase_date"] as? String {
if let date = Formatter.customDate.date(from:purchase_date){
var startDate1 = String(describing: date)
startDate1.removeLast(6)
self?.startDate = "\(startDate1)"
}
}
}
self?.buySubscription = true
self?.subscriptionBuy()
}
SVProgressHUD.dismiss()
}
UserDefaults.standard.set(true,forKey:"SubscriptionPurchased")
}
} catch let parseError {
print(parseError)
}
})
task.resume()
} catch let parseError {
print(parseError)
}
}
}

sign in required environment sandbox issue

Everytime I build an adhoc build and runs it on ios 11 it keeps popping up the message saying "Sign-In Required - Enter the password for ***** [Environment:Sandbox]". Thus, this keep activating like a loop and not going away. tried siging out from iCloud also but didn't work.My inAppPurchase code as bellow.
class InAppPurchase: NSObject,SKProductsRequestDelegate,
SKPaymentTransactionObserver {
///Singleton
private static var inappPurChase:InAppPurchase?;
public static var shared:InAppPurchase {
if inappPurChase == nil{
inappPurChase = InAppPurchase()
}
return inappPurChase!
}
override private init() {
//Singleton complete
}
////////////
var productsList = [SKProduct]()
var productToPurchase = SKProduct()
var productID = ""
let PURCHASE_ID_PREFIX = ""//"com.purelightbeta."
var onInAppPurchaseSuccess: (()->Void )? = nil
var onInAppPurchaseError: (()->Void )? = nil
var onNoProductsIDs: (()->Void )? = nil
var appEnvironment: ((String,Bool)->Void )? = nil
var onReceiptVarificationError: (()->Void )? = nil
var onRestorePurchaseError: ((String)->Void )? = nil
var onRestoreProductsIDs: ((NSMutableArray)->Void )? = nil
public func initPayment (productID: String) {
self.productID = PURCHASE_ID_PREFIX + productID
print("Initializing Purchase Product ID: \(self.productID)")
if SKPaymentQueue.canMakePayments() {
let productID: NSSet = NSSet(objects: self.productID)
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
}else {
print("In App Purchases not enabled")
}
}
func buyProduct() {
let payment = SKPayment(product: productToPurchase)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment as SKPayment)
}
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let myProducts = response.products
print(myProducts)
productsList = []
if myProducts.count == 0 {
print("No products")
if(onNoProductsIDs != nil){
onNoProductsIDs!()
}
}
for product in myProducts {
print("Prod Details :\(product.productIdentifier) \(product.price) \(product.localizedDescription)")
productsList.append(product)
}
for product in productsList {
let productID = product.productIdentifier
if productID == self.productID {
productToPurchase = product
buyProduct()
}
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
let productIDs: NSMutableArray = []
for trasacton in queue.transactions {
if trasacton.transactionState == SKPaymentTransactionState.restored
{
let t: SKPaymentTransaction = trasacton as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
print("prodID:",prodID)
//let appStoreId = prodID.components(separatedBy: "com.purelightbeta.")
productIDs.add(prodID)
SKPaymentQueue .default().finishTransaction(trasacton)
}
}
if onRestoreProductsIDs != nil {
print(productIDs)
onRestoreProductsIDs! (productIDs)
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction: AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
print(trans.error ?? "No in app error")
switch trans.transactionState {
case .purchased:
print("buy ok, AIP unlocked for item :", productToPurchase.productIdentifier)
let productID = productToPurchase.productIdentifier
switch productID {
case self.productID:
print("Item found")
// startValidatingReceipt()
default:
print("Item not found")
}
queue.finishTransaction(trans)
if self.onInAppPurchaseSuccess != nil {
self.onInAppPurchaseSuccess! ()
self.startValidatingReceipt()
}
case .failed:
print("buy error")
if self.onInAppPurchaseError != nil {
self.onInAppPurchaseError! ()
}
queue.finishTransaction(trans)
break
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
// break
default:
print("default")
break
}
}
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("ERROR: \(error.localizedDescription)")
if onRestorePurchaseError != nil {
onRestorePurchaseError! (error.localizedDescription)
}
}
func restorePurchase() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func startValidatingReceipt() {
if let isExists = try? self.getReceiptURL()?.checkResourceIsReachable(), isExists == true {
do {
let data = try Data(contentsOf: self.getReceiptURL()!)
// self.startValidatingData(data: data)
self.sendReceiptToLocalServer(data: data)
}catch {
let appReceiptRefreshRequest = SKReceiptRefreshRequest(receiptProperties: nil)
appReceiptRefreshRequest.delegate = self
appReceiptRefreshRequest.start()
}
}else {
print("No receipt for this purchase")
}
}
func getReceiptURL() -> URL? {
return Bundle.main.appStoreReceiptURL
}
enum receiptValidationURLs:String {
case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"
case production = "https://buy.itunes.apple.com/verifyReceipt"
static var url:URL{
if isDebug {
return URL.init(string: self.sandbox.rawValue)!
}else{
return URL.init(string: self.production.rawValue)!
}
}
}
func sendReceiptToLocalServer(data:Data) {
let base64encodedReceipt = data.base64EncodedString()
if self.appEnvironment != nil {
self.appEnvironment! (base64encodedReceipt,isDebug)
}
}
func startValidatingData(data:Data){
let base64encodedReceipt = data.base64EncodedString()
print(base64encodedReceipt)
let requestDictionary = ["receipt-data":base64encodedReceipt]
guard JSONSerialization.isValidJSONObject(requestDictionary) else { print("requestDictionary is not valid JSON"); return }
do {
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
// let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt" // this works but as noted above it's best to use your own trusted server
NSLog("The Environment is: %#", receiptValidationURLs.url.absoluteString)
guard let validationURL = URL(string: receiptValidationURLs.url.absoluteString) else { print("the validation url could not be created, unlikely error"); return }
let session = URLSession(configuration: URLSessionConfiguration.default)
var request = URLRequest(url: validationURL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
print((appReceiptJSON as AnyObject).count)
print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
NSLog("Receipt is: %#", appReceiptJSON as! NSDictionary)
if let environment = (appReceiptJSON as? NSDictionary)?["environment"]{
if self.appEnvironment != nil {
self.appEnvironment! (environment as? String ?? "No value",isDebug)
}
}
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
} else {
print("the upload task returned an error: \(error)")
}
}
task.resume()
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
}
// func requestDidFinish(_ request: SKRequest) {
// // a fresh receipt should now be present at the url
// do {
//
//
//
// } catch {
// // still no receipt, possible but unlikely to occur since this is the "success" delegate method
// }
// }
func request(_ request: SKRequest, didFailWithError error: Error) {
print("app receipt refresh request did fail with error: \(error)")
// for some clues see here: https://samritchie.net/2015/01/29/the-operation-couldnt-be-completed-sserrordomain-error-100/
if self.onReceiptVarificationError != nil {
self.onReceiptVarificationError! ()
}
}
}
This may not be the answer but would be a workaround. Best is to create a new sandbox user, sign out the old one and put the new.

How to update an app already on the app store to contain in app purchases

Right now I have an app on the app store. I knew that I would late update it but I just wanted to make sure I put it up before I did that. So now I want to implement in-app purchases. However, for some reason it is not working:
This is in the ViewDidLoad()
let productIdentifiers: Set<String> = ["1000Coins"]
let productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productsRequest.delegate = self
productsRequest.start()
And this is the other function. For some reason it is not printing this
print("Product: \(product.productIdentifier), \(product.localizedTitle),\(product.price.floatValue)")
func productsRequest(_ request: SKProductsRequest, didReceive
response: SKProductsResponse) {
print("Loaded Products")
for product in response.products {
print("Product: \(product.productIdentifier), \(product.localizedTitle),\(product.price.floatValue)")
}
}
I created the in app purchase with iTunes connect but for some reason it is not working. Any suggestions?? Thanks!
Don't clutter your ViewDidLoad unnecessarily. Create an in-app-purchase helper to handle all the functions associated with IAPs e.g get product identifiers, verify receipt, restore etc.
A good helper written by Ron Buencamino is this:
import StoreKit
protocol IAPManagerDelegate {
func managerDidRestorePurchases()
}
class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver, SKRequestDelegate {
static let sharedInstance = IAPManager()
var request:SKProductsRequest!
var products:NSArray!
var delegate:IAPManagerDelegate?
//Load product identifiers for store usage
func setupInAppPurchases(){
self.validateProductIdentifiers(self.getProductIdentifiersFromMainBundle())
SKPaymentQueue.default().add(self)
}
//Get product identifiers
func getProductIdentifiersFromMainBundle() -> NSArray {
var identifiers = NSArray()
if let url = Bundle.main.url(forResource: "iap_products", withExtension: "plist"){
identifiers = NSArray(contentsOf: url)!
}
return identifiers
}
//Retrieve product information
func validateProductIdentifiers(_ identifiers:NSArray) {
let productIdentifiers = NSSet(array: identifiers as [AnyObject])
let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
self.request = productRequest
productRequest.delegate = self
productRequest.start()
}
func createPaymentRequestForProduct(_ product:SKProduct){
let payment = SKMutablePayment(product: product)
payment.quantity = 1
SKPaymentQueue.default().add(payment)
}
func verifyReceipt(_ transaction:SKPaymentTransaction?){
let receiptURL = Bundle.main.appStoreReceiptURL!
if let receipt = try? Data(contentsOf: receiptURL){
//Receipt exists
let requestContents = ["receipt-data" : receipt.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))]
//Perform request
do {
let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: JSONSerialization.WritingOptions(rawValue: 0))
//Build URL Request
//change the storeURL before you submit to the app store
let storeURL = URL(string: "https:/sandbox.itunes.apple.com/verifyReceipt")
var request = URLRequest(url: storeURL!)
request.httpMethod = "Post"
request.httpBody = requestData
let session = URLSession.shared
let task = session.dataTask(with: request , completionHandler: { (responseData, response , error) -> Void in
do {
let json = try JSONSerialization.jsonObject(with: responseData! as Data, options: .mutableLeaves) as! NSDictionary
print(json)
if (json.object(forKey: "status") as! NSNumber) == 0 {
//
let receipt_dict = json["receipt"] as! NSDictionary
if let purchases = receipt_dict["in_app"] as? NSArray {
self.validatePurchaseArray(purchases)
}
// you can add more statements to check different parts of the receipt
if transaction != nil {
SKPaymentQueue.default().finishTransaction(transaction!)
}
DispatchQueue.main.sync(execute: { () -> Void in self.delegate?.managerDidRestorePurchases() } ) }
else {
//Debug the receipt
print(json.object(forKey: "status") as! NSNumber)
}
}
catch {
print(error)
}
} )
task.resume()
}
catch {
print(error)
}
}
else {
//Receipt does not exist
print("No Receipt")
}
}
func validatePurchaseArray(_ purchases:NSArray){
for purchase in purchases as! [NSDictionary]{
self.unlockPurchasedFunctionalityForProductIdentifier(purchase["product_id"] as! String)
}
}
func unlockPurchasedFunctionalityForProductIdentifier(_ productIdentifier:String){
///here you put the functionality that you want to unlock for the user
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
func isDateExpired(_ expires_date:Double) -> Bool{
var isExpired:Bool = false
let currentDate = (Date().timeIntervalSince1970 * 1000) as TimeInterval
if currentDate > expires_date{
isExpired = true
}
return isExpired
}
//MARK: SKProductsRequestDelegate
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
//
self.products = response.products as NSArray!
print(self.products)
}
//MARK: SKPaymentTransactionObserver Delegate Protocol
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
//
for transaction in transactions as [SKPaymentTransaction]{
switch transaction.transactionState{
case .purchasing:
print("Purchasing")
UIApplication.shared.isNetworkActivityIndicatorVisible = true
case .deferred:
print("Deferrred")
UIApplication.shared.isNetworkActivityIndicatorVisible = false
case .failed:
print("Failed")
print(transaction.error?.localizedDescription)
UIApplication.shared.isNetworkActivityIndicatorVisible = false
SKPaymentQueue.default().finishTransaction(transaction)
case.purchased:
print("Purchased")
//
self.verifyReceipt(transaction)
case .restored:
print("Restored")
}
}
}
func restorePurchases(){
let request = SKReceiptRefreshRequest()
request.delegate = self
request.start()
}
func requestDidFinish(_ request: SKRequest) {
self.verifyReceipt(nil)
}
}
add a plist file named iap_products.plist to your Xcode and include the product ids. In said file include:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>product1Id</string>
<string>product2Id</string>
so on....
</array>
</plist>

In app purchase not fully firing

Whenever I conduct an in-app purchase in a sandbox environment, it acts like it is completing, when it really isn't. To be more specific, I click to buy more coins, I confirm that I want to buy them in the sandbox environment. Then it says "You're all set!" saying that I have completed the purchase. Well when I check my coins, nothing changed!
After looking through the code, I realized that the
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
...is not firing.
Full code:
import UIKit
import StoreKit
import AVFoundation
class InAppPurchases: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
#IBOutlet weak var noAdsBtn: UIButton!
var product_id: String?
let defaultsAD = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
product_id = "noads"
SKPaymentQueue.default().add(self)
//Check if product is purchased
if (defaultsAD.bool(forKey: "purchased")){
// Hide a view or show content depends on your requirement
// overlayView.hidden = true
noAdsBtn.backgroundColor = UIColor.red
noAdsBtn.isUserInteractionEnabled = false
} else {
print("false")
// noAds = true
// var NAD = UserDefaults.standard
// NAD.setValue(noAds, forKey: "nad")
// NAD.synchronize()
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func backBtnA(_ sender: Any) {
music2()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
#IBAction func Buy600(_ sender: Any) {
print("About to fetch the products")
product_id = "600coins"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
#IBAction func buy1500(_ sender: Any) {
print("About to fetch the products")
product_id = "1500coins"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
#IBAction func buy3500(_ sender: Any) {
print("About to fetch the products")
product_id = "3500coins"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
#IBAction func buy8000(_ sender: Any) {
print("About to fetch the products")
product_id = "8000coins"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
#IBAction func buy25000(_ sender: Any) {
print("About to fetch the products")
product_id = "25000coins"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
#IBAction func buyTruckload(_ sender: Any) {
print("About to fetch the products")
product_id = "truckloadofcoins"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
#IBAction func buyNoAds(_ sender: Any) {
print("About to fetch the products")
product_id = "noads"
// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}
func buyProduct(product: SKProduct){
print("Sending the Payment Request to Apple");
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment);
//
SKPaymentQueue.default().add(self)
//^^??
}
func productsRequest (_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let count : Int = response.products.count
if (count>0) {
let validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
buyProduct(product: validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Error Fetching product information");
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
print("Received Payment Transaction Response from Apple");
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .purchased:
print("Product Purchased");
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
if product_id == "noads"{
defaultsAD.set(true , forKey: "purchased")
noAdsBtn.backgroundColor = UIColor.red
noAdsBtn.isUserInteractionEnabled = false
noAds = true
var NAD = UserDefaults.standard
NAD.setValue(noAds, forKey: "nad")
NAD.synchronize()
}
//add coins
if product_id == "600coins"{
coins += 600
print("just bought 600 coins")
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
}
if product_id == "1500coins"{
coins += 1500
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
}
if product_id == "3500coins"{
coins += 3500
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
}
if product_id == "8000coins"{
coins += 8000
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
}
if product_id == "25000coins"{
coins += 25000
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
print("just boughtn 25k coins")
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
}
if product_id == "100000coins"{
coins += 100000
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
}
// overlayView.hidden = true
break;
case .failed:
print("Purchased Failed");
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break;
case .restored:
print("Already Purchased");
SKPaymentQueue.default().restoreCompletedTransactions()
if product_id == "noads"{
defaultsAD.set(true , forKey: "purchased")
noAdsBtn.backgroundColor = UIColor.red
noAdsBtn.isUserInteractionEnabled = false
noAds = true
var NAD = UserDefaults.standard
NAD.setValue(noAds, forKey: "nad")
NAD.synchronize()
}else{
//add coins
if product_id == "600coins"{
coins += 600
print("already bought")
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
}
if product_id == "1500coins"{
coins += 1500
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
}
if product_id == "3500coins"{
coins += 3500
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
}
if product_id == "8000coins"{
coins += 8000
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
}
if product_id == "25000coins"{
coins += 25000
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
}
if product_id == "100000coins"{
coins += 100000
var C = UserDefaults.standard
C.setValue(coins, forKey: "c")
C.synchronize()
}
}
default:
break;
}
}
}
}
func paymentQueue(_ queue: SKPaymentQueue,
updatedTransactions transactions: [SKPaymentTransaction]){
print("updating transactions")
}
func music2(){
// currentlyPlayingAudio = true
// print("\n\n\n\n\n\n\n\n\nview appeared...music??\n\n\n\n\n\n\n\n\n")
if soundFX == true{
let ButtonAudioURL2 = URL(fileURLWithPath: Bundle.main.path(forResource: "Click2", ofType: "mp3")!)
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
var error: NSError?
do {
audioPlayerClick = try AVAudioPlayer(contentsOf: ButtonAudioURL2)
} catch let error1 as NSError {
error = error1
}
audioPlayerClick.prepareToPlay()
// audioPlayer.volume = 0.3
audioPlayerClick.play()
}
}
func music3(){
// currentlyPlayingAudio = true
// print("\n\n\n\n\n\n\n\n\nview appeared...music??\n\n\n\n\n\n\n\n\n")
if soundFX == true{
let ButtonAudioURL3 = URL(fileURLWithPath: Bundle.main.path(forResource: "Toggle", ofType: "mp3")!)
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
var error: NSError?
do {
audioPlayerClick2 = try AVAudioPlayer(contentsOf: ButtonAudioURL3)
} catch let error1 as NSError {
error = error1
}
audioPlayerClick2.prepareToPlay()
// audioPlayer.volume = 0.3
audioPlayerClick2.play()
}
}
}
One last thing; the IAP to remove ads (with id "noads") keeps returning "nothing" in the console. Any ideas why it won't successfully trigger? It is a non-consumable and is labeled in iTunes Connect as "Waiting for Upload".

Value type is not bridged to Objective-C Error - Runtime error when using SKProductsRequest

I previously had been able to set up In-app purchases in a iOS app, however when coming back to the project and migrating to Swift 3 I am getting a run time error, there's no errors in the console but I am seeing
Value type is not bridged to Objective-C Error
This is for the following function for when I request the products
//Request Products
func requestProductInfo() {
if SKPaymentQueue.canMakePayments() {
let productIdentifiers = NSSet(array: productIDs)
//ERROR
let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productRequest.delegate = self
productRequest.start()
}
else {
print("Cannot perform In App Purchases.")
}
}
Does anyone understand what's going on ? I understand that there may be some issues using a Swift Array and Set with a NSSet but I have tried to change things around but still was getting errors.
var productIDs: Array<String?> = []
var productsArray = [SKProduct]()
func transactionAction() {
let payment = SKPayment(product: self.productsArray[self.selectedProductIndex] as SKProduct)
SKPaymentQueue.default().add(payment)
self.transactionInProgress = true
}
//Request Products
func requestProductInfo() {
if SKPaymentQueue.canMakePayments() {
let productIdentifiers = NSSet(array: productIDs)
let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productRequest.delegate = self
productRequest.start()
}
else {
print("Cannot perform In App Purchases.")
}
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
if response.products.count != 0 {
// print("\(response.products.map {p -> String in return p.localizedTitle})")
productsArray = response.products
}
else {
print("There are no products.")
}
if response.invalidProductIdentifiers.count != 0 {
print("\(response.invalidProductIdentifiers.description)")
}
print("Number of products in productsArray \(productsArray.count) - Number of products in productIDs \(productIDs.count)")
setPurchaseButton()
}
Have your tried changing this line:
var productIDs: Array<String?> = []
to this?:
var productIDs: Array<String> = []
You may need to change some other parts according to this change, but with this you have no need to use risky as!:
let productIdentifiers = Set(productIDs)
let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
If you need to pass Set<String> to a method, you'd better not use intermediate NSSet. The worst new feature of Swift 3, id-as-Any, would make something disastrous.

Resources