1

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 ?

Emm
  • 1,963
  • 2
  • 20
  • 51

4 Answers4

2

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

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
  • I will clean comments now, glad its working for you now. One more thing in the future you might want to consider saving your data in Keychain instead of NSUserDefaults, especially for things like in app purchases. Otherwise its very easy to unlock stuff if you have a jailbroken device. This is the helper I use, its beyond easy. https://github.com/jrendel/SwiftKeychainWrapper – crashoverride777 Sep 14 '15 at 14:39
  • Here is the code that I used for Game Center, I had some problems too. http://stackoverflow.com/questions/32145090/how-to-make-high-score-of-game-to-be-saved-on-leaderboard-with-swift Check answer too ! And If You want to add me on facebook here is it: https://www.facebook.com/Emin.Rma.Emini.Im.Madridista.A.JoCcKeR.Rma – Emm Sep 18 '15 at 17:09
  • 2
    Awesome. I think you should really just get my full helper since you are already using half of it. I would also say about 20-30% of my downloads is from countries that dont support iAds. Also iAds do fail sometimes so you always have googles as a backUp. Its well worth it especially for indie devs like us. It will maybe take you 30 min max to set up your google account etc. I made step by step instructions so there should be no issues. https://github.com/crashoverride777/Swift-iAds-and-AdMob-Helper – crashoverride777 Sep 19 '15 at 13:27
  • My friend, I can't import GoogleMobileAds in my code, this is happening after last updates of Xcode. Did You heard any new why is this not working ? I put framework of GoogleMobileAds but when I import it in code it finds error and it says: "No such module 'GoogleMobileAds'", I searched everywhere why this is not working but I din't find anything. – Emm Oct 25 '15 at 18:06
  • u need to go to Xcode and delete the framework reference in the left panel where all your .swift files etc are . Than you need to go to targets-buildSettings and look for search paths. In search paths delete framework search paths (back button), which should be the link to the folder location of the google sdk. Than download the latest SDK from googles website (they updated them recently) and copy it from your download folder to your project folder on your computer. Than go back to Xcode and add the framework again by going to targets-general-linked frameworks. – crashoverride777 Oct 25 '15 at 21:40
  • You need to do this every time a new Google SDK update is released, especially deleting the framework search path. – crashoverride777 Oct 25 '15 at 21:41
  • Ya, I'm doing, But I think I had problem with Xcode, Now I'm reinstalling it. Thanks bro. – Emm Oct 25 '15 at 21:42
  • no problem. That frameworks search path stuff caused me problems in the beginning because I didnt know I had to delete it. If those steps dont fix it than yeah try a reinstall, although that properly won't help because the reference is saved in your project, not Xcode – crashoverride777 Oct 25 '15 at 21:44
  • I am having no problems with my ads, especially the latest google SKD 7.5.2 seemed to have fixed some Xcode 7 bugs – crashoverride777 Oct 25 '15 at 21:45
1

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.

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
1

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.

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
1

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.

crashoverride777
  • 10,581
  • 2
  • 32
  • 56