I'm trying to implement In App Purchase in iOS using Swift, but the Restore Purchase functionality is giving problem. Whenever I click on the restore purchase button it is working fine but it keeps adding it to the queue. Please Help.
Below is my code and output.
In the output "IAP not setup method3" keeps on increasing by one whenever I click on Restore Purchase button.
import UIKit
import StoreKit
class PremiumUpgradeVC: UIViewController , SKProductsRequestDelegate, SKPaymentTransactionObserver{
#IBOutlet weak var uiScrollVw: UIScrollView!
#IBOutlet weak var btnPremiumPurchase: UIButton!
#IBOutlet weak var btnPrice: UIButton!
#IBOutlet weak var btnRestorePur: UIButton!
var removeAds : Bool = false
var enableCompilations : Bool = false
var premiumPurchasedKey : String = "isPremiumPurchased"
var list = [SKProduct]()
var p = SKProduct()
var IAPId = "com.creativecub.testme"
override func viewDidLoad() {
super.viewDidLoad()
if(SKPaymentQueue.canMakePayments()){
print("IAP is enabled, loading")
var productID:NSSet = NSSet(objects: IAPId)
var request :SKProductsRequest = SKProductsRequest(productIdentifiers : productID as! Set<String>)
request.delegate = self
request.start()
//loadingAlert.
}else{
print("please enable IAP")
}
}
#IBAction func actionGoPremium(sender: UIButton) {
print("on action button")
for product in list{
var prodID = product.productIdentifier
if(prodID == IAPId){
p = product
buyProduct()
break;
}
}
}
func goPremi(){
print("in method : gopremium")
// removeAds = true
// enableCompilations = true
HelperClass.setValSharedPerf(premiumPurchasedKey, value: "true")
print("ads removed and compilations enables .. Enjoy")
}
#IBAction func actionRestorePurchase(sender: UIButton) {
print("in method : 1")
SKPaymentQueue.defaultQueue().addTransactionObserver(self) SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
func buyProduct() {
print("buy product -" + p.productIdentifier)
let pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
print("in method : 2")
}
//restored transaction function
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("in method : 3")
//print("transaction retsored")
var purchasedItemIDs = []
for transaction in queue.transactions{
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case IAPId:
print("premium Upgrade method 3")
goPremi()
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
default:
print("IAP not setup method3") // this keeps increasing
}
}
}
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
print("in method : 4")
print("product request")
var myProducts = response.products
for product in myProducts {
print("product added - details :" + product.productIdentifier + product.localizedTitle)
let price = priceStringForProduct(product)
btnPrice.setTitle("Buy for: "+price!, forState: UIControlState.Normal)
list.append(product)
}
}
//restore purchase
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
// print("added payment")
print("in method : 5")
for transaction:AnyObject in transactions{
let trans = transaction as! SKPaymentTransaction
//print(trans.error)
switch trans.transactionState {
case .Purchased: //switch1 case 1
print("buy ok, unlock IAP here")
let prodID = p.productIdentifier as String
switch prodID {
case IAPId:
print("premium Upgrade .. purchase restored")
goPremi()
queue.finishTransaction(trans)
default:
print("IAP not setup method5")
}
queue.finishTransaction(trans)
break;
case .Failed: //switch1 case 2
print("error in buy")
queue.finishTransaction(trans)
break;
default: //switch1 case 3
print("default case method 5")
break;
}
}
}
func finishTransaction(trans:SKPaymentTransaction){
print("finsh trans")
SKPaymentQueue.defaultQueue().finishTransaction(trans)
print("in method : 6")
}
func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("remove trnas")
SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
print("in method : 7")
}
func priceStringForProduct(item: SKProduct) -> String? {
let numberFormatter = NSNumberFormatter()
let price = item.price
let locale = item.priceLocale
numberFormatter.numberStyle = .CurrencyStyle
numberFormatter.locale = locale
return numberFormatter.stringFromNumber(price)
}
func goBack()
{
self.dismissViewControllerAnimated(true, completion: nil);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(animated: Bool) {
SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
print("disapeard")
}
}
This is the output:
in method : 1
in method : 5
default case method 5
default case method 5
in method : 3
IAP not setup method3 //this statement keep increasing on button click
IAP not setup method3
IAP not setup method3
IAP not setup method3
IAP not setup method3
premium Upgrade method 3
in method : gopremium
ads removed and compilations enables .. Enjoy
remove trnas
in method : 7
You should not do anything in paymentQueueRestoreCompletedTransactionsFinished aside from updating your UI if required. This delegate method is called to indicate that the restoration process is complete; nothing more.
The restored transactions will be delivered to your updated transactions method with a state of restored. You should process them the same as you do with the state of purchased
Related
I'm trying to do IAP however for some reason, my SKProductsRequest returns 0 products.
-Test products have been added to iTunes connect properly
-Banking and Taxing information is filled
-The product's bundle id matches the app bundle id
-I've waited up to two days for it to get processed through the servers
I used this youtube tutorial to build the app:
https://www.youtube.com/watch?v=zRrs7O5yjKI
And here is the code:
import UIKit
import StoreKit
class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
#IBOutlet weak var lblAd: UILabel!
#IBOutlet weak var lblCoinAmount: UILabel!
#IBOutlet weak var outRemoveAds: UIButton!
#IBOutlet weak var outAddCoins: UIButton!
#IBOutlet weak var outRestorePurchases: UIButton!
var coins = 50
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
outRemoveAds.isEnabled = false
outAddCoins.isEnabled = false
outRestorePurchases.isEnabled = false
if(SKPaymentQueue.canMakePayments()) {
print("IAP is enabled, loading")
let productID: NSSet = NSSet(objects: "com.IAPTesters.10Dolla", "com.IAPTesters.RemoveAds")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
} else {
print("please enable IAPS")
}
}
#IBAction func btnRemoveAds(_ sender: Any) {
print("rem ads")
for product in list {
let prodID = product.productIdentifier
if(prodID == "com.IAPTesters.RemoveAds") {
p = product
buyProduct()
}
}
}
#IBAction func btnAddCoins(_ sender: Any) {
for product in list {
let prodID = product.productIdentifier
if(prodID == "com.IAPTesters.10Dolla") {
p = product
buyProduct()
}
}
}
#IBAction func btnRestorePurchases(_ sender: Any) {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func buyProduct() {
print("buy " + p.productIdentifier)
let pay = SKPayment(product: p)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(pay as SKPayment)
}
func removeAds() {
lblAd.removeFromSuperview()
}
func addCoins() {
coins += 50
lblCoinAmount.text = "\(coins)"
}
var list = [SKProduct]()
var p = SKProduct()
func productsRequest(_ request: SKProductsRequest, didReceive 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)
}
outRemoveAds.isEnabled = true
outAddCoins.isEnabled = true
outRestorePurchases.isEnabled = true
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("transactions restored")
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "com.IAPTesters.RemoveAds":
print("remove ads")
removeAds()
case "com.IAPTesters.10Dolla":
print("add coins to account")
addCoins()
default:
print("IAP not found")
}
}
}
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("buy ok, unlock IAP HERE")
print(p.productIdentifier)
let prodID = p.productIdentifier
switch prodID {
case "com.IAPTesters.RemoveAds":
print("remove ads")
removeAds()
case "com.IAPTesters.10Dolla":
print("add coins to account")
addCoins()
default:
print("IAP not found")
}
queue.finishTransaction(trans)
case .failed:
print("buy error")
queue.finishTransaction(trans)
break
default:
print("Default")
break
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When you run the app it will print "IAP is enabled, loading" and than "product request" but nothing else.
If I print response.invalidProductIdentifiers in the productsRequest function it will return my products: ["com.IAPTesters.RemoveAds", "com.IAPTesters.10Dolla"]
Thanks in advance for the help
It turns out that my banking and taxing information was filled out incorrectly. I refilled it and than I had to wait about 30 minutes for it to work again. Everything is now working correctly! Thanks for all the help
If you refer to the SKProductsRequest documentation you will see:
Note
Be sure to keep a strong reference to the request object; otherwise, the system might deallocate the request before it can complete.
Your SKProductsRequest instance is a local constant in viewDidLoad. This will be deallocated as soon as viewDidLoad exits and since the product request will complete asynchronously, this will be before the request is completed and you will therefore never get a call back.
You should retain your SKProductsRequest in a property so that it isn't released.
class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
#IBOutlet weak var lblAd: UILabel!
#IBOutlet weak var lblCoinAmount: UILabel!
#IBOutlet weak var outRemoveAds: UIButton!
#IBOutlet weak var outAddCoins: UIButton!
#IBOutlet weak var outRestorePurchases: UIButton!
var productsRequest: SKProductsRequest?
var coins = 50
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
outRemoveAds.isEnabled = false
outAddCoins.isEnabled = false
outRestorePurchases.isEnabled = false
if(SKPaymentQueue.canMakePayments()) {
print("IAP is enabled, loading")
let productID: NSSet = NSSet(objects: "com.IAPTesters.10Dolla", "com.IAPTesters.RemoveAds")
self.productsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request?.delegate = self
request?.start()
} else {
print("please enable IAPS")
}
}
func productsRequest(_ request: SKProductsRequest, didReceive 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)
}
outRemoveAds.isEnabled = true
outAddCoins.isEnabled = true
outRestorePurchases.isEnabled = true
self.productsRequest = nil
}
FYI, your implementation of paymentQueueRestoreCompletedTransactionsFinished is incorrect; you should process the restored transactions in updatedTransactions through the .restored transaction state. The paymentQueueRestoreCompletedTransactionsFinished method should only be used to update your UI or perform any other tasks that are required when the restoration process is complete.
If I 'm understood correctly then the problem with your implementation is you have created your products in iTunes with wrong identifiers. As I know the product identifiers should be like your application bundle identifier followed by your product functionality i.e, COM.COMPANYNAME.MYNEWAPPLICATION.REMOVEADS, COM.COMPANYNAME.MYNEWAPPLICATION.10DOLLA
Sample code be like,
typealias SSIAPHelperCompletion = (_ result: [SKProduct]?, _ error: Error?) ->Void
class SSIAPHelper: NSObject {
fileprivate let productsRequest = SKProductsRequest(productIdentifiers: Set(arrayLiteral: "com.companyname.appname.removeads","com.companyname.appname.10dolla"))
fileprivate var completion: SSIAPHelperCompletion?
static let shared = SSIAPHelper()
func requestProducts(completionHandler: #escaping SSIAPHelperCompletion) {
self.completion = completionHandler
self.productsRequest.delegate = SSIAPHelper.shared
self.productsRequest.start()
}
}
extension SSIAPHelper: SKProductsRequestDelegate{
func requestDidFinish(_ request: SKRequest) {
print(#function)
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print(#function, response.products)
if let lCompletion = self.completion{
lCompletion(response.products, nil)
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print(#function)
}
}
You can get products by simply calling below function from your viewDidLoad()
SSIAPHelper.shared.requestProducts(completionHandler: { (result, error) in
})
I have an iOS app written in Swift where I have written code for the store in it's own class (Store). I then have a view controller (StoreController) which displays the product details and presents buttons so that the user can buy/restore purchases. I am using the delegate pattern.
My question is where should I create the Store object? Currently I'm declaring it as a property of StoreController which I think is wrong. I would prefer to be able to create it when my app loads and keep it until it quits.
The current problem is that if I test it and navigate to the StoreController view (initiating the request via Store) then press 'back' the app freezes. I assume because it's waiting on the response?
I saw one example online where someone created it in the AppDelegate file but this didn't work for me as I wasn't able to reference it from my StoreController.
This is my Store class:
import Foundation
import StoreKit
protocol ClassStoreDelegate: class {
func storeUpdateReceived(store: Store)
}
class Store: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
// Properties
weak var delegate: ClassStoreDelegate?
var list = [SKProduct]()
var p = SKProduct()
var defaults = UserDefaults.standard // Where we save whether the user is pro
var localTitle: String?
var localDescription: String?
var localPrice: String?
// Methods
// Calling the delegate method
func storeUpdate() {
delegate?.storeUpdateReceived(store: self)
}
// Buy the product
func buy() {
for product in list {
let prodID = product.productIdentifier
if(prodID == "com.squidgylabs.pro") {
p = product
buyProduct()
}
}
}
// Restore products
func restore() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func getProducts() {
if(SKPaymentQueue.canMakePayments()) {
let productID: NSSet = NSSet(objects: "com.squidgylabs.pro")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
} else {
delegate?.storeUpdateReceived(store: self)
}
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let myProduct = response.products
for product in myProduct {
list.append(product)
}
// Update labels
localTitle = list[0].localizedTitle
localDescription = list[0].localizedDescription
// Format the price and display
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .currency
if let formattedPrice = formatter.string(from: list[0].price){
localPrice = ("buy \(formattedPrice)")
delegate?.storeUpdateReceived(store: self)
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
let transactionsArray = queue.transactions
if (transactionsArray.isEmpty) {
delegate?.storeUpdateReceived(store: self)
}
else {
for transaction in transactionsArray {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "com.squidgylabs.pro":
defaults.set(true, forKey: "pro")
delegate?.storeUpdateReceived(store: self)
default:
delegate?.storeUpdateReceived(store: self)
}
}
}
}
func buyProduct() {
let pay = SKPayment(product: p)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(pay as SKPayment)
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction: AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
switch trans.transactionState {
case .purchased:
let prodID = p.productIdentifier
switch prodID {
case "com.squidgylabs.pro":
defaults.set(true, forKey: "pro")
delegate?.storeUpdateReceived(store: self)
default:
delegate?.storeUpdateReceived(store: self)
}
queue.finishTransaction(trans)
break
case .failed:
delegate?.storeUpdateReceived(store: self)
queue.finishTransaction(trans)
break
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
queue.finishTransaction(trans)
break
default:
break
}
}
}
}
And this is my StoreController (view controller):
import UIKit
class StoreController: UIViewController, ClassStoreDelegate {
// Properties
let store = Store()
// Outlets
#IBOutlet weak var localTitle: UILabel!
#IBOutlet weak var localDescription: UILabel!
#IBOutlet weak var buy: UIButton!
#IBOutlet weak var restore: UIButton!
// Actions
#IBAction func buy(_ sender: UIButton) {
print("buy pressed")
store.buy()
}
#IBAction func restore(_ sender: UIButton) {
print("restore pressed")
store.restore()
}
// Methods
override func viewDidLoad() {
super.viewDidLoad()
store.delegate = self // bind the delegate like this?
// Get list of products for the store
localTitle.isEnabled = false
localDescription.isEnabled = false
buy.isEnabled = false
restore.isEnabled = false
self.navigationItem.title = "Store"
// update once the list of products is got from the store object
store.getProducts()
}
// Running the delegate update
func storeUpdateReceived(store: Store) {
print("storeUpdateReceived activated")
if ((store.localTitle) != nil) {
localTitle.text = store.localTitle!
localDescription.text = store.localDescription
buy.setTitle(store.localPrice, for: .normal)
localTitle.isEnabled = true
localDescription.isEnabled = true
buy.isEnabled = true
restore.isEnabled = true
}
}
}
You're right - instantiate it in your AppDelegate. You know it will be called once and only once, so it's a good place to initialise things.
You can access the AppDelegate from anywhere in your app
appDelegate = (UIApplication.shared.delegate as! AppDelegate)
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
I have made an app and made an In-App purchase for the premium version. Because I can't test In-App purchases in the Simulator.
So here is my code:
class settingsViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver, ADBannerViewDelegate {
#IBOutlet var adBennerView: ADBannerView!
#IBOutlet var outletRemoveAds: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if(SKPaymentQueue.canMakePayments()) {
println("IAP is enabled, loading")
var productID:NSSet = NSSet(objects: "xxx")
var request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as Set<NSObject>)
request.delegate = self
request.start()
} else {
println("please enable IAPS")
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func removeAds(sender: AnyObject) {
for product in list {
var prodID = product.productIdentifier
if(prodID == "xxx") {
p = product
buyProduct()
break;
}
}
}
#IBAction func restorePurchases(sender: AnyObject) {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
var list = [SKProduct]()
var p = SKProduct()
func buyProduct() {
println("buy " + p.productIdentifier)
var pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
println("product request")
var myProduct = response.products
for product in myProduct {
println("product added")
println(product.productIdentifier)
println(product.localizedTitle)
println(product.localizedDescription)
println(product.price)
list.append(product as! SKProduct)
}
outletRemoveAds.enabled = true
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("add paymnet")
for transaction:AnyObject in transactions {
var trans = transaction as! SKPaymentTransaction
println(trans.error)
switch trans.transactionState {
case .Purchased:
println("buy, ok unlock iap here")
println(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case "xxx":
println("remove ads")
removeAds()
isPremium = true
default:
println("IAP not setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
println("buy error")
queue.finishTransaction(trans)
break;
default:
println("default")
break;
}
}
}
func finishTransaction(trans:SKPaymentTransaction)
{
println("finish trans")
SKPaymentQueue.defaultQueue().finishTransaction(trans)
}
func paymentQueue(queue: SKPaymentQueue!, removedTransactions transactions: [AnyObject]!)
{
println("remove trans");
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue!) {
println("transactions restored")
var purchasedItemIDS = []
for transaction in queue.transactions {
var t: SKPaymentTransaction = transaction as! SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "xxx":
println("remove ads")
removeAds()
isPremium = true
default:
println("IAP not setup")
}
}
}
func removeAds() {
adBennerView!.removeFromSuperview()
}
If the user bought the premium version - isPremium becomes true, so how do I know that every time the user log in isPremium still be true?
That happens because you only storing isPremium in the RAM and not anywhere else. Try using NSUserDefaults for that purpose, just to see how it can be done (actually it is advised not to use NSUserDefaults for that type of information, because it is easy to hack, but you have to understand the basics of storing information about your app state).