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.
Related
I am trying to incorporate an in-app purchase. For some reason I am getting a fatal error that states 'fatal error: Index out of range' with an array that holds the SKProduct products. I have one nonconsumable in app purchase, but may decide to add more later. I am trying to access the PREMIUM_PRODUCT_ID product so I can make a purchase. The error occurs when the purchaseMyProduct function is called. Any ideas in why the iapProducts array is out of bounds? Thanks for your help!
The fatal error occurs on this line purchaseMyProduct(product: iapProducts[0])
import UIKit
import StoreKit
class Settings: UIViewController, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
let PREMIUM_PRODUCT_ID = "---------------"
var productID = ""
var productsRequest = SKProductsRequest()
var iapProducts = [SKProduct]()
var nonConsumablePurchaseMade = UserDefaults.standard.bool(forKey: "nonConsumablePurchaseMade")
#IBOutlet weak var adsBtn: UIButton!
#IBAction func restorePurchase(_ sender: Any) {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
// Check your In-App Purchases
print("NON CONSUMABLE PURCHASE MADE: \(nonConsumablePurchaseMade)")
// Fetch IAP Products available
fetchAvailableProducts()
UIAlertView(title: "IAP Tutorial",
message: "You've successfully restored your purchase!",
delegate: nil, cancelButtonTitle: "OK").show()
}
#IBAction func review(_ sender: Any) {
UIApplication.shared.openURL(NSURL(string: "-----------------")! as URL)
}
#IBAction func removeAds(_ sender: Any) {
//UIApplication.shared.openURL(NSURL(string: "----------------")! as URL)
purchaseMyProduct(product: iapProducts[0])
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
...
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
nonConsumablePurchaseMade = true
UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
/*UIAlertView(title: "IAP Tutorial",
message: "You've successfully restored your purchase!",
delegate: nil, cancelButtonTitle: "OK").show()*/
}
func fetchAvailableProducts() {
// Put here your IAP Products ID's
let productIdentifiers = NSSet(objects:
PREMIUM_PRODUCT_ID
)
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
if (response.products.count > 0) {
iapProducts = response.products
// 1st IAP Product (Consumable) ------------------------------------
let firstProduct = response.products[0] as SKProduct
// Get its price from iTunes Connect
let numberFormatter = NumberFormatter()
numberFormatter.formatterBehavior = .behavior10_4
numberFormatter.numberStyle = .currency
numberFormatter.locale = firstProduct.priceLocale
let price1Str = numberFormatter.string(from: firstProduct.price)
// Show its description
//consumableLabel.text = firstProduct.localizedDescription + "\nfor just \(price1Str!)"
// ------------------------------------------------
// 2nd IAP Product (Non-Consumable) ------------------------------
let secondProd = response.products[0] as SKProduct
// Get its price from iTunes Connect
numberFormatter.locale = secondProd.priceLocale
let price2Str = numberFormatter.string(from: secondProd.price)
// Show its description
//nonConsumableLabel.text = secondProd.localizedDescription + "\nfor just \(price2Str!)"
// ------------------------------------
}
}
func canMakePurchases() -> Bool { return SKPaymentQueue.canMakePayments() }
func purchaseMyProduct(product: SKProduct) {
if self.canMakePurchases() {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
print("PRODUCT TO PURCHASE: \(product.productIdentifier)")
productID = product.productIdentifier
// IAP Purchases dsabled on the Device
} else {
UIAlertView(title: "IAP Tutorial",
message: "Purchases are disabled in your device!",
delegate: nil, cancelButtonTitle: "OK").show()
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
// The Consumable product (10 coins) has been purchased -> gain 10 extra coins!
if productID == PREMIUM_PRODUCT_ID {
// Save your purchase locally (needed only for Non-Consumable IAP)
nonConsumablePurchaseMade = true
UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
//premiumLabel.text = "Premium version PURCHASED!"
UIAlertView(title: "IAP Tutorial",
message: "You've successfully unlocked the Premium version!",
delegate: nil,
cancelButtonTitle: "OK").show()
}
break
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break
default: break
}}}
}
}
Your code is way too long, so I took the liberty of making an educated guess what is relevant:
import UIKit
import StoreKit
class Settings: UIViewController, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
var iapProducts = [SKProduct]()
#IBAction func removeAds(_ sender: Any) {
purchaseMyProduct(product: iapProducts[0])
}
}
When you declare iapProducts, you are creating an empty array that contains no values.
Then, in your removeAds function, you are telling it to do something with the first element.
I am going to take a wild guess that nothing exists, so you are forcing it to access an object that doesn't exist.
Try the following and see if it fixes your issue.
var iapProducts: [SKProduct]?
#IBAction func removeAds(_ sender: Any) {
guard let product = iapProducts?.first else { return }
purchaseMyProduct(product: product)
}
This accomplishes two things:
It makes your [SKProduct] array an optional
The guard statement safely unwraps the first product, so only if one exists, will it try to call your purchaseMyProduct function. Otherwise, it just exits
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.
In my application, the users can buy PRO membership to unlock stuff.
Here is the view controllers code where the user buy PRO membership:
import UIKit
import StoreKit
class UpgradePROViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
#IBOutlet weak var upgradeButton: UIButton!
var ApplicationLoadTime = NSUserDefaults().integerForKey("isPRO")
var product_id: NSString?;
let loadedValue = NSUserDefaults().integerForKey("isPRO")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var ApplicationLoadTimeUserDefault = NSUserDefaults.standardUserDefaults()
if (ApplicationLoadTimeUserDefault.valueForKey("isPRO") != nil){
ApplicationLoadTime = ApplicationLoadTimeUserDefault.valueForKey("isPRO") as! NSInteger!
}
upgradeButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
upgradeButton.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Highlighted)
upgradeButton.layer.cornerRadius = 5
upgradeButton.layer.borderWidth = 1
upgradeButton.layer.borderColor = UIColor(white: 1.0, alpha: 0.35).CGColor
upgradeButton.layer.backgroundColor = UIColor(white: 0.1, alpha: 0.85).CGColor
product_id = "com.myself.MyApp.GetPRO";
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func upgradeButtonAction(sender: AnyObject) {
buyConsumable()
}
#IBAction func restoreButtonAction(sender: AnyObject) {
if (SKPaymentQueue.canMakePayments()){
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
println("Purchased Restored");
}
}
func buyConsumable(){
println("About to fetch the products");
// 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<NSObject>);
productsRequest.delegate = self;
productsRequest.start();
println("Fething Products");
}else{
println("can't make purchases");
}
}
// Helper Methods
func buyProduct(product: SKProduct){
println("Sending the Payment Request to Apple");
var payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}
// Delegate Methods for IAP
func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
println("got the request from Apple")
var count : Int = response.products.count
if (count>0) {
var validProducts = response.products
var validProduct: SKProduct = response.products[0] as! SKProduct
if (validProduct.productIdentifier == self.product_id) {
println(validProduct.localizedTitle)
println(validProduct.localizedDescription)
println(validProduct.price)
buyProduct(validProduct);
} else {
println(validProduct.productIdentifier)
}
} else {
println("nothing")
}
}
func request(request: SKRequest!, didFailWithError error: NSError!) {
println("La vaina fallo");
}
func unlockProFeatures(){
ApplicationLoadTime = 1
NSString(format: "isPRO", "%i", 1)
var ApplicationLoadTimeUserDefault = NSUserDefaults.standardUserDefaults()
ApplicationLoadTimeUserDefault.setValue(ApplicationLoadTime, forKey: "isPRO")
ApplicationLoadTimeUserDefault.synchronize()
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("Received Payment Transaction Response from Apple");
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
println("Product Purchased");
unlockProFeatures()
break;
case .Failed:
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
println("Purchased Failed");
break;
case .Restored:
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
println("Purchased Restored");
break;
default:
break;
}
}
}
}
}
Somehow, it looks like the restore purchase does not work.. I am testing using sandbox users. Does anybody has any suggestions what to do? It works all fine to buy the items, but when I try to restore the purchase with the same user that has purchased the item, it does not work. Anything wrong in my code, or anything you would do different?
I made a game that have iAd interstitial using Xcode 7 beta and SpriteKit in Swift 2, I tried to use a function to remove them but It's not working. I'm using two different files, GameScene.swift and GameViewController.swift.
Code that I used in file, GameScene.swift:
func gameOver() {
isGameOver = true
print("Game Over")
loadAd()
}
//iAd
func close(sender: UIButton) {
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
func loadAd() {
print("load ad")
interAd = ADInterstitialAd()
interAd.delegate = self
closeButton.frame = CGRectMake(15, 15, 22, 22)
closeButton.layer.cornerRadius = 11
closeButton.setTitle("x", forState: .Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
closeButton.backgroundColor = UIColor.whiteColor()
closeButton.layer.borderColor = UIColor.blackColor().CGColor
closeButton.layer.borderWidth = 1
closeButton.addTarget(self, action: "close:", forControlEvents: UIControlEvents.TouchDown)
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
print("ad did load")
interAdView = UIView()
interAdView.frame = self.view!.bounds
view!.addSubview(interAdView)
interAd.presentInView(interAdView)
UIViewController.prepareInterstitialAds()
interAdView.addSubview(closeButton)
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("failed to receive")
print(error.localizedDescription)
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
In GameViewController.swift is In-App Purchase to remove ads (to buy Pro Version):
#IBAction func removeAds(sender: UIButton) {
print("Remove Ads Button pressed")
for product in list {
let prodID = product.productIdentifier
if(prodID == "Squares.RemoveAds") {
p = product
buyProduct()
break;
}
}
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
func removeAds() {
}
override func prefersStatusBarHidden() -> Bool {
return true
}
//Remove Ads Payment
var list = [SKProduct]()
var p = SKProduct()
//Squares.regular.removeAds
//Squares.6Plus.removeAds
func buyProduct() {
print("Buy" + p.productIdentifier)
let pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
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)
}
removeAdsButton.enabled = true
removeAdsIPhone6Plus.enabled = true
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("Transactions Restored")
var purchasedItemIDS = []
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "Squares.RemoveAds":
print("Remove Ads")
removeAds()
case "Squares.RemoveAds":
print("Remove Ads for iPhone 6 Plus")
removeAds()
default:
print("IAP not setup")
}
}
}
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 Squares here")
print(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case "Squares.RemoveAds":
print("Remove Ads")
removeAds()
case "Squares.RemoveAds":
print("Remove Ads for iPhone 6 Plus")
removeAds()
default:
print("IAP not Setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
print("Buy Error")
queue.finishTransaction(trans)
break;
default:
print("Default")
break;
}
}
}
func finishTransaction(trans:SKPaymentTransaction){
print("Finish Transaction")
}
func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Remove Transaction")
}
If You read the code in file GameViewController.swift function removeAds() is empty and is called in some codes in same file, so it will remove ads forever,and what I need to do is to put a code in function removeAds() that will remove ads permanently, the problem is what I don't know how to call it and in which way to remove them, because functions are in different files, I tried many ways but doesn't work.
Can You show me please with more details how to do that ?
Ok so basically a new way with better code.
First make a copy your project, just incase.
Secondly, listen and follow my instructions exactly, please.
So I posted you an answer already with the purchase code. Check that you follower the instructions there exactly as I said. Than delete All the code you have in GameScene about the ads. All of it like NSNotifcationCenter, functions, ADInterstitialAdDelegate(at top of gamescene) etc.
Than you make a new swift file in your project and you enter this code.
This is a slimmed down version of my helper, it now includes inter Ads and banner ads. To set up banner do the following. In app delegate under import UIKit you write
import iAd
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
Still in appDelegate under the class implementation you have to create this property
var bannerAdView = ADBannerView()
The reason this goes into the appDelegate is because we are creating a shared iAdBanner. Not really needed for spritekit with 1 viewController but this is the correct way for apps with multiple viewControllers so you might as well use it.
Than copy this into your project as before.
import iAd
class Ads: NSObject {
// MARK: - Properties
static let sharedInstance = Ads()
var presentingViewController: UIViewController!
var interAd = ADInterstitialAd()
var interAdView = UIView()
var interAdCloseButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
override init() {
super.init()
print("Ads Helper init")
preloadInterAd() // preload first interAd, they will preload automatically afterwards.
}
// MARK: - User Methods
class func showBannerAd() {
Ads.sharedInstance.loadBannerAd()
}
class func showInterAd() {
Ads.sharedInstance.showInterAd()
}
class func removeBannerAds() {
Ads.sharedInstance.removeBannerAds()
}
class func removeAllAds() {
Ads.sharedInstance.removeAllAds()
}
//MARK: - Internal Methods
// loading banner
private func loadBannerAd() {
printDebug("iAd banner loading...")
appDelegate.bannerAdView = ADBannerView(frame: presentingViewController.view.bounds)
appDelegate.bannerAdView.delegate = self
appDelegate.bannerAdView.center = CGPoint(x: CGRectGetMidX(presentingViewController.view.frame), y: CGRectGetMaxY(presentingViewController.view.frame) + (appDelegate.bannerAdView.frame.size.height / 2))
}
// preloading inter
private func preloadInterAd() {
print("iAds Inter preloading")
interAd = ADInterstitialAd()
interAd.delegate = self
interAdCloseButton.frame = CGRectMake(13, 13, 22, 22)
interAdCloseButton.layer.cornerRadius = 12
interAdCloseButton.setTitle("X", forState: .Normal)
interAdCloseButton.setTitleColor(UIColor.grayColor(), forState: .Normal)
interAdCloseButton.backgroundColor = UIColor.whiteColor()
interAdCloseButton.layer.borderColor = UIColor.grayColor().CGColor
interAdCloseButton.layer.borderWidth = 2
interAdCloseButton.addTarget(self, action: "pressedCloseButton:", forControlEvents: UIControlEvents.TouchDown) // function such as this with content in brackets need : for selector. VIP
}
private func showInterAd() {
if interAd.loaded {
print("iAds Inter showing")
presentingViewController.view.addSubview(interAdView)
interAd.presentInView(interAdView)
UIViewController.prepareInterstitialAds()
interAdView.addSubview(interAdCloseButton)
// pause game, music etc here
} else {
print("iAds Inter cannot be shown, reloading")
preloadInterAd()
}
}
// closed inter ad
func pressedCloseButton(sender: UIButton) {
interAdCloseButton.removeFromSuperview()
interAdView.removeFromSuperview()
interAd.delegate = nil
preloadInterAd()
}
// remove banner ads
private func removeBannerAds() {
appDelegate.bannerAdView.delegate = nil
appDelegate.bannerAdView.removeFromSuperview()
}
// remove all ads
private func removeAllAds() {
// banners
appDelegate.bannerAdView.delegate = nil
appDelegate.bannerAdView.removeFromSuperview()
// inter
interAdCloseButton.removeFromSuperview()
interAdView.removeFromSuperview()
interAd.delegate = nil
}
}
// MARK: iAds Banner Delegates
extension Ads: ADBannerViewDelegate {
func bannerViewWillLoadAd(banner: ADBannerView!) {
printDebug("iAds banner will load")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
printDebug("iAds banner did load, showing")
presentingViewController.view.addSubview(appDelegate.bannerAdView)
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(1.5)
appDelegate.bannerAdView.center = CGPoint(x: CGRectGetMidX(presentingViewController.view.frame), y: CGRectGetMaxY(presentingViewController.view.frame) - (appDelegate.bannerAdView.frame.size.height / 2))
UIView.commitAnimations()
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
printDebug("iAds banner clicked")
// pause game , music etc here
return true
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
printDebug("iAds banner closed")
// resume game, music here
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
printDebug("iAds banner error")
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(1.5)
appDelegate.bannerAdView.center = CGPoint(x: CGRectGetMidX(presentingViewController.view.frame), y: CGRectGetMaxY(presentingViewController.view.frame) + (appDelegate.bannerAdView.frame.size.height / 2))
appDelegate.bannerAdView.hidden = true
UIView.commitAnimations()
}
}
// MARK: - iAds Inter Delegates
extension Ads: ADInterstitialAdDelegate {
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
print("iAds Inter did preload")
interAdView = UIView()
interAdView.frame = presentingViewController.view.bounds
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
print("iAds Inter did unload")
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("iAds Inter error")
print(error.localizedDescription)
interAdCloseButton.removeFromSuperview()
interAdView.removeFromSuperview()
interAd.delegate = nil
preloadInterAd()
}
}
Okay. Now all you have to do is write one line of code to init this Helper and preload the first Inter (they will preload automatically afterwards.)
In your gameViewController in ViewDidLoad you call this
// this tells the ad helper that this is the GameViewController that shows ads.
// This step also inits the adHelper and preloads the first inter ad.
Ads.sharedInstance.presentingViewController = self
And thats it. In your GameScene in the func gameOver() or wherever you want you now simply say
Ads.showInterAd()
Ads.showBannerAd()
You can always check in the console if the Ad has preloaded as i made prinln for every step. If an ad has not preloaded it cannot be shown. This helps you understand whats happening.
In you gameViewController in the purchase code you now simply say
Ads.removeAllAds()
and all ads are removed.
If you just want to remove BannerAds, for example during gamePlay, simply say
Ads.removeBannerAds()
than load them again whenever you like with
Ads.showBannerAd()
To save a purchase you do this.
In the function removeAllAds() you say right at the end
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "your product ID")
Anywhere in your code where you will say Ads.showInterAd() or Ads.showBannerAd() like func gameOver() you write this
// You can use your product Id as a key, and you dont actually have to set a var. So its the easiest this way. If it cannot find a bool for your product ID it will automatically set it to false. Very nice.
// A reason I made the structs with the product ID is so its easier to reference it so you dont have to type it out every time.
if NSUserDefaults.standardUserDefaults().boolForKey("your product ID") == false {
Ads.showInterAd()
}
Done
You can use NSNotifcationCenter.
So in your viewController where the removeAds function is you would say
NSNotificationCenter.defaultCenter().addObserver(self, selector: "removeAds", name: "RemoveAdsKey", object: nil)
Than whenever you want to call this function you would say
NSNotificationCenter.defaultCenter().postNotificationName("RemoveAdsKey", object: nil)
In regards to the function removeAds() itself. You would need to write something like this.
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
interAd.delegate = nil
You should add the SKpayment transaction observer as soon as possible and only once, not per purchase.
Remove the observer only when your game gets closed. A lot of tutorials show you the way that's not recommended by Apple.
https://developer.apple.com/library/ios/technotes/tn2387/_index.html
To make your life much easier why dont you check out an iAds and Admob helper I have released on gitHub, it was primarily made for SpriteKit.
https://github.com/crashoverride777/Swift-iAds-and-AdMob-Helper
Even if you dont want to use it this should give you an idea of how to use the delegates etc. There is nothing complicated there, just the basic way to use ads. It also shows you how to preload interAds, so they show much faster.
I updated my first answer with a link to apple. Its not that your way is wrong but Apple specifically gives an example of a good and a bad implementation of this and unfortunately most tutorials do it the bad way.
In your specific case you could do the following.
Add 2 NSNotification Center observers to your viewController
NSNotificationCenter.defaultCenter().addObserver(self, selector: "addTransactionObserver", name: "AddObserverKey", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "removeTransactionObserver", name: "RemoveObserverKey", object: nil)
create 2 functions in your viewController.
func addTransactionObserver() {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
// you could put this in view Did Load but I prefer apples way
}
func removeTransactionObserver() {
SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
}
Than in your appDelegate in the method didFinishLaunching... use
NSNotificationCenter.defaultCenter().postNotificationName("AddObserverKey", object: nil)
Than in your appDelegate in the appWillTerminate... use
NSNotificationCenter.defaultCenter().postNotificationName("RemoveObserverKey", object: nil)
You should really consider putting your purchase code into a helper file to make things much easier. For example this is a good start.
http://masteringios.com/blog/2015/02/17/in-app-purchase-how-to-retrieve-the-list-of-products-from-the-app-store/
https://github.com/Vitaa/IAPurchaseManager/blob/master/IAPManager.swift
With the ads, just check my helper, it will give you an idea of where you went wrong.
You Game View Controller could look like this
class gameViewController: .....
struct ProductID {
static let removeAds = "squares.RemoveAds"
static let removeAds6Plus = "squares.RemoveAds6Plus" // really needed?
}
struct NSNotificationKey {
static let addObserver = "AddObserver"
static let removeObserver = "RemoveObserver"
}
func ViewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "addTransactionObserver", name: NSNotificationKey.addObserver, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "removeTransactionObserver", name: NSNotificationKey.removeObserver, object: nil)
....
}
func addTransactionObserver() {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
// you could put this in view Did Load but I prefer apples way
}
func removeTransactionObserver() {
SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
}
#IBAction func removeAds(sender: UIButton) {
print("Remove Ads Button pressed")
for product in list {
let prodID = product.productIdentifier
if(prodID == ProductID.removeAds) {
p = product
buyProduct()
break
}
}
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
func removeAds() {
interAd.delegate = nil
closeButton.removeFromSuperView()
interAdView.removeFromSuperView()
}
override func prefersStatusBarHidden() -> Bool {
return true
}
//Remove Ads Payment
var list = [SKProduct]()
var p = SKProduct()
//Squares.regular.removeAds
//Squares.6Plus.removeAds
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)
}
removeAdsButton.enabled = true
removeAdsIPhone6Plus.enabled = true
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("Transactions Restored")
var purchasedItemIDS = []
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case ProductID.removeAds:
print("Remove Ads")
removeAds()
case ProductID.removeAds6Plus:
print("Remove Ads for iPhone 6 Plus")
removeAds()
default:
print("IAP not setup")
}
}
}
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 Squares here")
print(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case ProductID.removeAds:
print("Remove Ads")
removeAds()
case ProductID.removeAds6Plus: // really needed?
print("Remove Ads for iPhone 6 Plus")
removeAds()
default:
print("IAP not Setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
print("Buy Error")
queue.finishTransaction(trans)
break;
default:
print("Default")
break;
}
}
}
func finishTransaction(trans:SKPaymentTransaction){
print("Finish Transaction")
}
func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Remove Transaction")
}
Than in your appDelegate.swift in didFinishLaunchingWithOptions you say
NSNotificationCenter.defaultCenter().postNotificationName(GameViewController.NSNotificationKey.addObserver, object: nil)
and in appWillTerminate you say
NSNotificationCenter.defaultCenter().postNotificationName(GameViewController.NSNotificationKey.removeObserver, object: nil)
If it still does not work than you need to check your product IDs. You are using Squares.RemoveAds as product Id but than further down the line you have these comments
//Squares.regular.removeAds
//Squares.6Plus.removeAds
double Check your IDs.
Working in xcode 6 and swift, I am trying to get the in-app purchases to remove ads working. The purchase seems to work, but the restore not so much. I kinda got it to work but it does not update the controllers.
In every viewcontroller I have the following to show ads and check if already purchased to hide:
I have an #IBOutlet for the banner:
#IBOutlet weak var topAdBanner: ADBannerView!
As well as the following code:
override func viewDidLoad() {
super.viewDidLoad()
if NSUserDefaults.standardUserDefaults().boolForKey("purchased") ==
true || purchased == true {
self.topAdBanner.hidden = true
} else {
var topAdBanner: ADBannerView
self.topAdBanner.hidden = true
self.topAdBanner.delegate = self
}
Then on my menuviewcontroler this is the code I have which is where my problem is. The app is localized into a few languages, hence the extra localization code:
import UIKit
import StoreKit
class MenuViewController: UIViewController, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
var product_id: NSString?;
#IBOutlet weak var descriptionText: UILabel!
#IBOutlet weak var purchaseButton: UIButton!
#IBOutlet weak var purchaseLabel: UILabel!
#IBOutlet weak var restoreButton: UIButton!
#IBOutlet weak var restoreLabel: UILabel!
#IBOutlet weak var cancelButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
product_id = "app.removeads";
self.descriptionText.text =
NSLocalizedString("REMOVE_ADS_DESCRIPTION", comment:
"REMOVE_ADS_DESCRIPTION")
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
func buyNonConsumable(){
if (SKPaymentQueue.canMakePayments())
{
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest
(productIdentifiers: productID as Set<NSObject>);
productsRequest.delegate = self;
productsRequest.start();
self.descriptionText.text = NSLocalizedString(
"FIRST_PROCESSING", comment: "first_processing")
}else{
self.descriptionText.text = NSLocalizedString("PAYMENT_DISABELD",
comment: "PAYMENT_DISABELD")
}
}
// Helper Methods
func buyProduct(product: SKProduct){
self.descriptionText.text = NSLocalizedString("SECOND_PROCESSING",
comment: "second_processing")
var payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment)
}
// Delegate Methods for IAP
func productsRequest (request: SKProductsRequest, didReceiveResponse
response: SKProductsResponse) {
self.descriptionText.text = NSLocalizedString("THIRD_PROCESSING",
comment: "third_processing")
var count : Int = response.products.count
if (count>0) {
var validProducts = response.products
var validProduct: SKProduct = response.products[0] as! SKProduct
if (validProduct.productIdentifier == self.product_id) {
println(validProduct.localizedTitle)
println(validProduct.localizedDescription)
println(validProduct.price)
buyProduct(validProduct);
} else {
println(validProduct.productIdentifier + "Fehler")
}
} else {
}
}
#IBAction func purchaseButton(sender: AnyObject) {
buyNonConsumable()
}
#IBAction func restoreButton(sender: AnyObject) {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
self.descriptionText.text = NSLocalizedString("FIRST_PROCESSING",
comment: "first_processing")
}
func paymentQueueRestoreCompletedTransactionsFinished(queue:
SKPaymentQueue!) {
println("Transactions Being Restored")
var purchasedItemIDS = []
for transaction:SKPaymentTransaction in queue.transactions as!
[SKPaymentTransaction] {
if transaction.payment.productIdentifier == self.product_id
{
println("Product Already Purchased")
// Unlock Feature
self.descriptionText.text =
NSLocalizedString("UNLOCKED_LABEL", comment: "unlocked")
purchased = true
NSUserDefaults.standardUserDefaults().setBool(true, forKey:
"purchased")
NSUserDefaults.standardUserDefaults().synchronize()
SKPaymentQueue.defaultQueue().finishTransaction(
transaction)
}
}
var alert = UIAlertView(title: "Thank You", message: "Your purchase
was restored. Go back to main screen", delegate: nil,
cancelButtonTitle: "OK")
alert.show()
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions
transactions: [AnyObject]!) {
for transaction in transactions as! [SKPaymentTransaction] {
switch transaction.transactionState {
case SKPaymentTransactionState.Purchased:
if (transaction.downloads != nil) {
SKPaymentQueue.defaultQueue().startDownloads(
transaction.downloads)
} else {
// Unlock feature or content here before
// finishing transaction
self.descriptionText.text =
NSLocalizedString("UNLOCKED_LABEL", comment:
"unlocked")
purchased = true
NSUserDefaults.standardUserDefaults().setBool(true,
forKey: "purchased")
NSUserDefaults.standardUserDefaults().synchronize()
SKPaymentQueue.defaultQueue().finishTransaction(
transaction)
}
case SKPaymentTransactionState.Restored:
println("Restored")
self.descriptionText.text =
NSLocalizedString("RESTORED_LABEL", comment:
"restored")
purchased = true
NSUserDefaults.standardUserDefaults().setBool(true,
forKey:"purchased")
NSUserDefaults.standardUserDefaults().synchronize()
break
case SKPaymentTransactionState.Failed:
self.descriptionText.text = NSLocalizedString(
"ERROR_HEADER",comment: "error")
SKPaymentQueue.defaultQueue().finishTransaction(
transaction)
default:
break
}
}
}
}
Purchasing the app works. Tested is out and it removes the ads.
Restoring however finds the product and says successfully restored but the adds do not go away.
Another problem i seem to have is that when I load the page that has the option to purchase and restore, it asks you for your itunes app store log in as soon as the page loads without pressing anything. I am not sure if it is related to my code.
I have spend the whole day trying to solve this and looking through the forums and posts but I cannot seem to solve it. Any help will be greatly appreciated.
Regarding your last question - yes, asking the iTunes password is related to your code, because the password dialog could appear when you do addTransactionObserver having transactions in the queue. You do it first time (don't do it multiple times though) in viewDidLoad. Better add the observer only when you're doing some action related to purchases - actually purchasing or restoring purchases.
viewDidLoad on your controllers is executed only the time they're added to the view hierarchy. Thus if they were added to the view hierarchy before your purchase/restore controller then your check wouldn't execute. Either move your checks to viewDidAppear, or do some notification to refresh UI state via NSNotificationCenter.