This issue has had me going for days.
I have a simple app, that displays banners and interstitial adverts.
I am using a single view application, have main view controller (ViewController.swift) and have set up another view controller (InAppViewController.swift) to handle a pop-up page that:
Allows the user to make an in-app purchase to remove all ads (AdBanners & InterstitialAds); or
Restore purchases.
My code is error-free when I run it.
In-app purchases are running ok, but occasionally I get a sign-in request to iTunes twice.
But my restore button and associated functionality seem to be the problem.
I have set-up numerous sandbox tester accounts to test, and a new user that has not bought the app is able to restore purchases successfully. Which should not be possible, so I definitely have done something wrong here.
Here is my code:
Main View Controller:
// ViewController.swift
import UIKit
import MessageUI
import Social
import iAd
import StoreKit
class ViewController: UIViewController, MFMailComposeViewControllerDelegate, MFMessageComposeViewControllerDelegate, ADBannerViewDelegate, ADInterstitialAdDelegate
{
let defaults = NSUserDefaults.standardUserDefaults()
var product_id: NSString?;
override func viewDidLoad() {
product_id = "some.product.id";
super.viewDidLoad()
//Check if product is purchased
if (defaults.boolForKey("purchased")){
print("already purchased")
// Hide or show banner ads is purchased/not purchased.
// Advertising Banner:
self.canDisplayBannerAds = false
}
else if (!defaults.boolForKey("stonerPurchased")){
print("not yet purchased")
// Advertising Banner:
self.canDisplayBannerAds = true
}
This code seems to work perfectly. When the app loads, it is able to determine who has paid to remove ads and those who have not paid, and ad banners are shown appropriately.
It is in the second view controller (InAppPViewController.swift) That I am having problems.
here is my code:
Second View Controller - InAppViewController.swift:
// InAppPViewController.swift
import UIKit
import StoreKit
import iAd
class InAppPViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
let defaults = NSUserDefaults.standardUserDefaults()
var product_id: NSString?;
#IBOutlet weak var unlockAction: UIButton!
#IBOutlet var adBannerView: ADBannerView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func restorePurchases(sender: UIButton) {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("Transactions Restored")
let alert = UIAlertView(title: "Thank You", message: "Your purchase(s) were restored.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
#IBAction func unlockAction(sender: AnyObject) {
product_id = "some.product.id";
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
//Check if product is purchased
if (defaults.boolForKey("purchased")){
}
else if (!defaults.boolForKey("stonerPurchased")){
print("false")
}
print("About to fetch the products");
// We check that we are allowed to make the purchase.
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("Fething Products");
}else{
print("can't make purchases");
}
}
func buyProduct(product: SKProduct){
print("Sending the Payment Request to Apple");
let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}
//Delegate Methods for IAP
func productsRequest (request: SKProductsRequest, didReceiveResponse 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(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
func request(request: SKRequest, didFailWithError error: NSError) {
print("Error Fetching product information");
}
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
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.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
defaults.setBool(true , forKey: "purchased")
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
break;
case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
default:
break;
}
}
}
}
}
Where am I going wrong?
Questions:
Is my above code correct?
What should I modify and why?
Apologies in advance, I am new to this wonderful world of coding ... but loving every minute of it!
SKPaymentQueue.defaultQueue().addTransactionObserver(self) should be in viewDidLoad() not in restore func
You can put SKPaymentQueue.defaultQueue().restoreCompletedTransactions() like this
override func viewWillDisappear(animated: Bool) {
SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
}
login twice in sandbox is normal.
hope I helped with something.
I have amended my code for the InAppPViewController.swift file as follows:
// InAppPViewController.swift
import UIKit
import StoreKit
class InAppPViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
let defaults = NSUserDefaults.standardUserDefaults()
var product_id: NSString?;
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func restorePurchases(sender: UIButton) {
// Set up the observer
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
//Check if user can make payments and then proceed to restore purchase
if (SKPaymentQueue.canMakePayments()) {
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
}
#IBAction func unlockAction(sender: AnyObject) {
product_id = "some.product.id";
// Adding the observer
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
//Check if product is purchased
if (defaults.boolForKey("purchased")){
print("User already purchased this")
// Hide a view or show content depends on your requirement
}
else if (!defaults.boolForKey("Purchased")){
print("User has not yet pur hased this")
}
print("About to fetch the products");
// Check if user can make payments and then proceed to make the purchase.
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("User can make purchases and will fetch products from Apple Store now");
}else{
print("User can't make purchases");
}
}
func buyProduct(product: SKProduct){
print("Sending the Payment Request to Apple");
let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}
func productsRequest (request: SKProductsRequest, didReceiveResponse 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(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
func request(request: SKRequest, didFailWithError error: NSError) {
print("Error Fetching product information");
}
// Allowing for all possible outcomes:
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
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")
let alert = UIAlertView(title: "Thank You", message: "Thank you for your purchase!", delegate: nil, cancelButtonTitle: "OK")
alert.show();
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
defaults.setBool(true , forKey: "purchased")
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
break;
case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
break;
default:
break;
}
}
}
}
}
I left the ViewController.swift file as is.
Product purchases seem to work now.
But regarding Restore Purchases, I can run the code on my physical device, but cannot test the Restore Purchases function.
I am caught with previous Restore Purchases that are still unresolved and looping in the system. I am unable to clear my SKPaymentsQueue manually. Thus my code refuses to entertain anymore new Restore Purchase requests.
Related
I implemented Google admob with Firebase integration for the analytics. I want to get the user to pay in order to remove the ads. The code for this works but some issues.
I get the message "You have already purchased this item would you like to buy it again" even if the user is purchasing it for the first time.
The ads were able to be removed eventually but the message above is confusing to the user.
1) I read online that the issue can be resolved if I place this code
SKPaymentQueue.default().add(self)
before the Firebase statement in the AppDelegate
FirebaseApp.configure()
But I cannot compile the code if SKPaymentQueue.default().add(self) this is the AppDelegate class.
2) I commented out FirebaseApp.configure() after reading that Firebase also add its own TransactionObserver. But this did not work either.
3) I placed SKPaymentQueue.default().add(self) in the GameScene class in the sceneDidLoad() function but this did not solve the problem.
4) I have queue.finishTransaction(transaction) in the func paymentQueue function for each of the SKPaymentTransactionState state.
Update with code used in the sandbox app:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
btnRemoveBannerAds.isEnabled = false
SKPaymentQueue.default().add(self)
getPurchaseInfo()
}
#IBAction func removeBannerAds(_ sender: UIButton) {
let skPayment = SKPayment(product: skProduct!)
SKPaymentQueue.default().add(skPayment)
}
extension PurchaseViewController : SKPaymentTransactionObserver, SKProductsRequestDelegate {
func getPurchaseInfo() {
if SKPaymentQueue.canMakePayments() {
let request = SKProductsRequest(productIdentifiers: NSSet(objects: self.productID) as! Set<String>) // dont understand this?
request.delegate = self
request.start()
} else {
print("In App purchase not enabled")
}
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
var products = response.products
if products.count == 0 {
print("Product not found")
} else {
skProduct = products[0]
print("product is \(String(describing: skProduct))")
btnRemoveBannerAds.isEnabled = true
}
let invalids = response.invalidProductIdentifiers
for product in invalids {
print("Product not found \(product)")
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case SKPaymentTransactionState.purchased:
SKPaymentQueue.default().finishTransaction(transaction)
btnRemoveBannerAds.isEnabled = false
userPref.setValue(true, forKey: "AdRemovalPurchase")
userPref.synchronize()
case SKPaymentTransactionState.restored:
SKPaymentQueue.default().finishTransaction(transaction)
btnRemoveBannerAds.isEnabled = false
userPref.setValue(true, forKey: "AdRemovalPurchase")
userPref.synchronize()
case SKPaymentTransactionState.failed:
SKPaymentQueue.default().finishTransaction(transaction)
btnRemoveBannerAds.isEnabled = false
default:
break
}
}
}
}
Please advise how to implement this. Thank you.
So I know the problem is the ViewController with the IAP, but unsure how to fix it.
I add SKPaymentQueue.defaultQueue().addTransactionObserver(self) in viewDidLoad and have the delegates for the class; SKProductsRequestDelegate, SKPaymentTransactionObserver.
I use the methods to carry out transactions for purchases, restore any purchases and make any changes needed for non consumable purchases.
But regardless what the user does, after loading the IAPViewController the app keeps prompting the user to login. Its mostly when the user exits the app then opens it back up.
I tried adding SKPaymentQueue.defaultQueue().removeTransactionObserver(self) when dismissing the view however that didn't seem to work.
Does anyone have any suggestions ?? I had to pull my app from review because of this bug :(
Here's the full code;
class IAPViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
var productIDs: Array<String!> = []
var productsArray: Array<SKProduct!> = []
var selectedProductIndex: Int!
var transactionInProgress = false
override func viewDidLoad() {
super.viewDidLoad()
productIDs.append("com.COMPANY.NAME.BUY01")
productIDs.append("com.COMPANY.NAME.BUY02")
productIDs.append("com.COMPANY.NAME.BUY03")
productIDs.append("com.COMPANY.NAME.BUY04")
requestProductInfo()
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
#IBAction func purchase04(sender: AnyObject) {
selectedProductIndex = 0
transactionAction()
}
#IBAction func restoreAction(sender: AnyObject) {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
//StoreKit
func transactionAction() {
let payment = SKPayment(product: self.productsArray[self.selectedProductIndex] as SKProduct)
SKPaymentQueue.defaultQueue().addPayment(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, didReceiveResponse response: SKProductsResponse) {
if response.products.count != 0 {
for product in response.products {
print("\(product.localizedTitle)")
productsArray.append(product)
}
}
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)")
}
//Payment Observer
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case SKPaymentTransactionState.Purchased:
print("Transaction completed successfully.", terminator: "")
self.deliverProduct(transaction)
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
transactionInProgress = false
case SKPaymentTransactionState.Failed:
print("Transaction Failed", terminator: "");
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
transactionInProgress = false
default:
print(transaction.transactionState.rawValue, terminator: "")
}
}
}
func deliverProduct(transaction:SKPaymentTransaction) {
if transaction.payment.productIdentifier == "com.COMPANY.NAME.BUY01"
{
print("Consumable Product 1 Purchased")
}
else if transaction.payment.productIdentifier == "com.COMPANY.NAME.BUY02"
{
print("Consumable Product 2 Purchased")
}
else if transaction.payment.productIdentifier == "com.COMPANY.NAME.BUY03"
{
print("Product 3 purchased")
}
else if transaction.payment.productIdentifier == "com.COMPANY.NAME.BUY04"
{
print("Product 4 purchased")
}
}
//Restore Purchases
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("Transactions Restored")
let purchasedItemIDS = []
for transaction:SKPaymentTransaction in queue.transactions {
if transaction.payment.productIdentifier == "com.COMPANY.NAME.BUY04"
{
print("Consumable Product Purchased")
}
}
let alertController = UIAlertController(title: "Restored Purchases", message: "Your purchases have been restored. ", preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "OK", style: .Default) { (action) in
// ...
}
alertController.addAction(OKAction)
self.presentViewController(alertController, animated: true) {
// ...
}
}
#IBAction func exit(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
}
}
It sounds like you have not called finishTransaction as per the documentation here:
Transactions stay in the payment queue until they are removed.
StoreKit will call your observer’s paymentQueue: updatedTransactions:
every time that your app launches or resumes from background until
they are removed. To that effect, your customers may be repeatedly
asked to authenticate their purchases or be prevented from purchasing
your products.
Call finishTransaction: on your transaction to remove it from the
queue. Finished transactions are not recoverable. Therefore, be sure
to provide your content, to download all Apple-hosted content of a
product, or complete your purchase process before finishing your
transaction. See Finishing the Transaction for more information.
Do you know that one of the two calls to finishTransaction is actually running?
I have a simple single view application project - a one (1) page app that displays some random text to the user.
I have successfully incorporated Ad Banners and Interstitial Ads.
I have set up another view controller (InAppViewController.swift) to handle a pop-up page that allows the user to make an in-app purchase to remove all ads (AdBanners & InterstitialAds).
In my second view controller (InAppViewController.swift), I have the following code:
AMENDED CODE:
// InAppPViewController.swift
import UIKit
import StoreKit
import iAd
class InAppPViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
let defaults = NSUserDefaults.standardUserDefaults()
var product_id: NSString?;
#IBOutlet weak var unlockAction: UIButton!
#IBOutlet var adBannerView: ADBannerView?
override func viewDidLoad() {
product_id = "holymoly.iap.removeads";
super.viewDidLoad()
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
//Check if product is purchased
if (defaults.boolForKey("purchased")){
self.adBannerView?.hidden = true
}
else if (!defaults.boolForKey("stonerPurchased")){
print("false")
self.adBannerView?.hidden = false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func unlockAction(sender: AnyObject) {
print("About to fetch the products");
// We check that we are allow to make the purchase.
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("Fething Products");
}else{
print("can't make purchases");
}
}
func buyProduct(product: SKProduct){
print("Sending the Payment Request to Apple");
let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}
//Delegate Methods for IAP
func productsRequest (request: SKProductsRequest, didReceiveResponse 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(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
func request(request: SKRequest, didFailWithError error: NSError) {
print("Error Fetching product information");
}
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
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.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
defaults.setBool(true , forKey: "purchased")
self.adBannerView?.hidden = true
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
self.adBannerView?.hidden = false
break;
case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
default:
self.adBannerView?.hidden = true
break;
}
}
}
}
}
And in my 'original' view controller (ViewController.swift) I have added this code:
// ViewController.swift
import UIKit
import MessageUI
import Social
import iAd
class ViewController: UIViewController, MFMailComposeViewControllerDelegate, MFMessageComposeViewControllerDelegate, ADBannerViewDelegate, ADInterstitialAdDelegate
{
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
#IBOutlet var adBannerView: ADBannerView?
#IBAction func someFunkyButton(sender: AnyObject) {
//Interstitial Ad:
let rand = Int(arc4random_uniform(4))
print(rand)
let adNo = 2
if(adNo == rand)
{
loadInterstitialAd()
}
}
let defaults = NSUserDefaults.standardUserDefaults()
override func viewDidLoad() {
super.viewDidLoad()
//Check if product is purchased
if (defaults.boolForKey("purchased")){
// Advertising Banner:
self.canDisplayBannerAds = true
self.adBannerView?.delegate = self
self.adBannerView?.hidden = true
}
else if (!defaults.boolForKey("stonerPurchased")){
print("false")
// Advertising Banner:
self.canDisplayBannerAds = true
self.adBannerView?.delegate = self
self.adBannerView?.hidden = false
}
The code shows as error-free.
It runs on my actual iPhone (simulator) and the in-app purchases work.
But the ad banners still show. I'm trying to show the ad banners with:
(i) In ViewController.swift:
self.canDisplayBannerAds = true
self.adBannerView?.delegate = self
self.adBannerView?.hidden = false
(ii) In InAppViewController.swift:
self.adBannerView?.hidden = false
and not show the ad banners with:
(i) In ViewController.swift:
self.canDisplayBannerAds = true
self.adBannerView?.delegate = self
self.adBannerView?.hidden = true
(ii) In InAppViewController.swift:
self.adBannerView?.hidden = true
but it's obviously not working.
Questions:
How can I amend my code and stop these ad banners from showing?
How can I also stop my interstitial ads from showing?
I feel I'm close, very close ... but that cigar is yet to land!
If you're implementing your own ADBannerView then you need to remove self.canDisplayBannerAds = true.
self.canDisplayBannerAds = true can be used for a no hassle way of implementing iAd banners in your application. This will create an ADBannerView for you and show or hide the ADBannerView on the bottom of your view depending on whether it receives an ad or not from the iAd network.
You either implement your own ADBannerView or use self.canDisplayBannerAds = true, not both.
First of all removeAds is inside btnRemoveAds so move it outside. Secondly, one approach would be to save the product identifiers of the purchased products in UserDefaults so that on quit and launch of the app the ads will not appear since the user has purchased the inapp. So what you will do on viewDidLoad is check if the product identifier is purchased (from NSUserDefaults, if it was purchased earlier it will be set), if it is purchased disable ads from the beginning else show ads. And when item is purchased then set the state in NSUserDefaults and update UI accordingly.
I'm creating an Application in Swift with a list of in-app purchase. What I'm trying to do is to obtain/retrieve a list of "Already Purchased" Product Identifiers.
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
}
For Example, in the above method we can get a list of available products. Instead of getting that list, I want to obtain the list of Already Purchased Product IDs. Then I would loop through that list and enable all the features for those products.
Is there any way to obtain that list? Or some other ways you would get that list?
Too little information in your question and about your code. I suggest you check my question which I answered a while ago: My IAP isn't working. Bugs at func Paymentqueue
It contains working IAP code and a few tips. I hope that helps you !
Here is the full IAP code :
import UIKit
import StoreKit
class GameViewController: UIViewController, ADBannerViewDelegate, SKProductsRequestDelegate, SKPaymentTransactionObserver, GKGameCenterControllerDelegate,GADBannerViewDelegate{
#IBOutlet var outRemoveAds: UIButton!
#IBOutlet var outRestorePurchases: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if NSUserDefaults.standardUserDefaults().objectForKey("val") != nil {
print("Has a value.")
banner.removeFromSuperview()
bannerGoogle.removeFromSuperview()
outRemoveAds.removeFromSuperview()
outRestorePurchases.removeFromSuperview()
removeInterFrom = 1
}
else {
print("No Value.")
}
if(SKPaymentQueue.canMakePayments()){
print("IAP is enabled, loading...")
let productID:NSSet = NSSet(objects:"IAP id")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
}
else{
print("Please enable IAPS")
}
}
//IAP Ads
#IBAction func removeAds(sender: UIButton) {
for product in list{
let prodID = product.productIdentifier
if (prodID == "IAP id"){
p = product
buyProduct()
break
}
}
}
#IBAction func restorePurchases(sender: UIButton) {
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
//IAP Functions
var list = [SKProduct]()
var p = SKProduct()
func removeAds(){
banner.removeFromSuperview()
bannerGoogle.removeFromSuperview()
outRemoveAds.removeFromSuperview()
outRestorePurchases.removeFromSuperview()
let theValue = 10
NSUserDefaults.standardUserDefaults().setObject(theValue, forKey: "val")
NSUserDefaults.standardUserDefaults().synchronize()
}
func buyProduct(){
print("Buy: "+p.productIdentifier)
let pay = SKPayment (product: p)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
print("Product Request")
let myProduct = response.products
for product in myProduct{
print("Product Added")
print(product.productIdentifier)
print(product.localizedTitle)
print(product.localizedDescription)
print(product.price)
list.append(product as SKProduct)
}
}
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("Add Payment")
for transaction:AnyObject in transactions{
let trans = transaction as! SKPaymentTransaction
print(trans.error)
switch trans.transactionState{
case .Purchased:
print("IAP unlocked")
print(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID{
case "IAP id":
print("Remove Ads")
removeAds()
default:
print("IAP not setup")
}
queue.finishTransaction(trans)
break
case .Failed:
print ("Buy error")
queue.finishTransaction(trans)
break
default:
print("default: Error")
break
}
}
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("Purchases Restored")
_ = []
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID{
case "IAP id":
print("Remove Ads")
removeAds()
default:
print("IAP not setup")
}
}
}
func finishTransaction(trans:SKPaymentTransaction){
print("Finshed Transaction")
}
func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Remove Transaction")
}
}
Put:
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
In viewDidLoad or viewDidAppear.
I am using IAPHelper to implement in-app purchase, an error is occurring when I go to another view controller after pressing the purchase button. For instance, when I press the purchase button then go to another view controller the error is presented after finishing the purchase work.
class selectQuestion_ViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource{
let helper = IAPHelper(productIdentifiers: NSSet(object: "sppid") as Set<NSObject>)
func purchase(sender: AnyObject)
{
startLoading("Purchasing ..")
self.helper.requestProductsWithCompletionHandler({ (success, products) -> Void in
if success {
self.endLoading()
println("wohooooo")
var sdad = self.helper.productsDict["sppid"]
self.helper.buyProduct(sdad!)
} else {
self.endLoading()
let alert = UIAlertController(title: "Error", message: "Cannot retrieve products list right now.", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
})
}
I recommend you do not use such modules. It would be much better for you and your understanding of IAPs that you check this tutorial and implement the code and methods described there: https://www.youtube.com/watch?v=h1gQklbrgjc
Once you have implemented that code, if something doesn't work (you may have forgotten a line somewhere), come back to SO and we will gladly help you out. Already, as quick help, here is an answer that could help you with most errors once you implement your IAP code with something else than IAP helper: My IAP isn't working. Bugs at func Paymentqueue
In fact, you can watch the video and copy paste the code of my question. My IAP code works ;) Will save you time. But watch the video to understand how to implement IAPs !
I hope this answer helped you ;)
Here is the full IAP code :
import UIKit
import StoreKit
class GameViewController: UIViewController, ADBannerViewDelegate, SKProductsRequestDelegate, SKPaymentTransactionObserver, GKGameCenterControllerDelegate,GADBannerViewDelegate{
#IBOutlet var outRemoveAds: UIButton!
#IBOutlet var outRestorePurchases: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if NSUserDefaults.standardUserDefaults().objectForKey("val") != nil {
print("Has a value.")
banner.removeFromSuperview()
bannerGoogle.removeFromSuperview()
outRemoveAds.removeFromSuperview()
outRestorePurchases.removeFromSuperview()
removeInterFrom = 1
}
else {
print("No Value.")
}
if(SKPaymentQueue.canMakePayments()){
print("IAP is enabled, loading...")
let productID:NSSet = NSSet(objects:"IAP id")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
}
else{
print("Please enable IAPS")
}
}
//IAP Ads
#IBAction func removeAds(sender: UIButton) {
for product in list{
let prodID = product.productIdentifier
if (prodID == "IAP id"){
p = product
buyProduct()
break
}
}
}
#IBAction func restorePurchases(sender: UIButton) {
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
//IAP Functions
var list = [SKProduct]()
var p = SKProduct()
func removeAds(){
banner.removeFromSuperview()
bannerGoogle.removeFromSuperview()
outRemoveAds.removeFromSuperview()
outRestorePurchases.removeFromSuperview()
let theValue = 10
NSUserDefaults.standardUserDefaults().setObject(theValue, forKey: "val")
NSUserDefaults.standardUserDefaults().synchronize()
}
func buyProduct(){
print("Buy: "+p.productIdentifier)
let pay = SKPayment (product: p)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
print("Product Request")
let myProduct = response.products
for product in myProduct{
print("Product Added")
print(product.productIdentifier)
print(product.localizedTitle)
print(product.localizedDescription)
print(product.price)
list.append(product as SKProduct)
}
}
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("Add Payment")
for transaction:AnyObject in transactions{
let trans = transaction as! SKPaymentTransaction
print(trans.error)
switch trans.transactionState{
case .Purchased:
print("IAP unlocked")
print(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID{
case "IAP id":
print("Remove Ads")
removeAds()
default:
print("IAP not setup")
}
queue.finishTransaction(trans)
break
case .Failed:
print ("Buy error")
queue.finishTransaction(trans)
break
default:
print("default: Error")
break
}
}
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("Purchases Restored")
_ = []
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID{
case "IAP id":
print("Remove Ads")
removeAds()
default:
print("IAP not setup")
}
}
}
func finishTransaction(trans:SKPaymentTransaction){
print("Finshed Transaction")
}
func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Remove Transaction")
}
}
Put:
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
in viewDidLoad or viewDidAppear