Search code examples
iosswiftsprite-kitstorekit

Type "GameScene" does not conform to protocol "SKPaymentTransactionObserver


Ok so I was following a tutorial (http://stefansdevplayground.blogspot.com/2015/04/how-to-implement-in-app-purchase-for.html) and when I finished following it, I had the error:

Type "GameScene" does not conform to protocol "SKPaymentTransactionObserver".

I'm a beginner and haven't used storekit before but some help would be nice.

(By the way, I haven't updated the NSUserDefaults and names he made in the tutorial yet to my game)

class RemoveAdsScene: SKScene, SKPaymentTransactionObserver, SKProductsRequestDelegate {

private var request : SKProductsRequest!
private var products : [SKProduct] = [] // List of available purchases
private var greenShipPurchased = false // Used to enable/disable the 'green ship'




let settingsTitle = SKLabelNode(text: "[REMOVE|ADS]")
let infoLabel = SKLabelNode(text: "Pay What You Want")
let infoLabel2 = SKLabelNode(text: "To Remove Ads!")
let button199 = SKSpriteNode(imageNamed: "199Button")

  let backButton = SKSpriteNode(imageNamed: "BackButton")
 var audioPlayer: AVAudioPlayer!

override func didMoveToView(view: SKView) {



    infoLabel.position = CGPointMake(self.frame.size.width/2, self.frame.size.height*0.825)
    infoLabel.zPosition = 500
    infoLabel.fontSize = 25
    infoLabel.fontName = "Montserrat-Bold"
    infoLabel.fontColor = SKColor.blackColor()
    self.addChild(infoLabel)

    infoLabel2.position = CGPointMake(self.frame.size.width/2, self.frame.size.height*0.775)
    infoLabel2.zPosition = 500
    infoLabel2.fontSize = 25
    infoLabel2.fontName = "Montserrat-Bold"
    infoLabel2.fontColor = SKColor.blackColor()
    self.addChild(infoLabel2)



    settingsTitle.fontColor = UIColor.init(red: 0.902, green: 0.251, blue: 0.282, alpha: 1)
    settingsTitle.fontSize = 85
    settingsTitle.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height*0.9)
    settingsTitle.fontName = "KGDefyingGravityBounce"
    self.addChild(settingsTitle)

    button199.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height*0.7)
    button199.zPosition = 15
    self.addChild(button199)




    backButton.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height * 0.3)
    backButton.zPosition = 15
    self.addChild(backButton)










    if NSUserDefaults.standardUserDefaults().objectForKey("timeOfTheDay") as! String  == "morning" {
        backgroundColor = GlobalData.dayColor

        infoLabel.fontColor = SKColor.blackColor()
        infoLabel2.fontColor = SKColor.blackColor()
    }
    else {

        backgroundColor = GlobalData.nightColor

        infoLabel.fontColor = SKColor.whiteColor()
        infoLabel2.fontColor = SKColor.whiteColor()


    }





}






func inAppPurchase() {
    let alert = UIAlertController(title: "In App Purchases", message: "", preferredStyle: UIAlertControllerStyle.Alert)

    // Add an alert action for each available product
    for (var i = 0; i < products.count; i += 1) {
        let currentProduct = products[i]
        if !(currentProduct.productIdentifier == "MySecondGameGreenShip" && greenShipPurchased) {
            // Get the localized price
            let numberFormatter = NSNumberFormatter()
            numberFormatter.numberStyle = .CurrencyStyle
            numberFormatter.locale = currentProduct.priceLocale
            // Add the alert action
            alert.addAction(UIAlertAction(title: currentProduct.localizedTitle + " " + numberFormatter.stringFromNumber(currentProduct.price)!, style: UIAlertActionStyle.Default)  { _ in
                // Perform the purchase
                self.buyProduct(currentProduct)

                })
        }
    }
    // Offer the restore option only if purchase info is not available
    if(greenShipPurchased == false) {
        alert.addAction(UIAlertAction(title: "Restore", style: UIAlertActionStyle.Default)  { _ in
            self.restorePurchasedProducts()

            })
    }
    alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default) { _ in

        })
    // Show the alert
    self.view?.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}
// Initialize the App Purchases
func initInAppPurchases() {
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)
    // Get the list of possible purchases
    if self.request == nil {
        self.request = SKProductsRequest(productIdentifiers: Set(["MySecondGameGreenShip","MySecondGameDonate"]))
        self.request.delegate = self
        self.request.start()
    }
}





func buyProduct(product: SKProduct) {
    let payment = SKPayment(product: product)
    SKPaymentQueue.defaultQueue().addPayment(payment)
}

func restorePurchasedProducts() {
    SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}

func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
    self.products = response.products 
    self.request = nil
}

func request(request: SKRequest, didFailWithError error: NSError) {
    print(error)
    self.request = nil
}

func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
    for transaction in transactions as! [SKPaymentTransaction] {
        switch (transaction.transactionState) {
        case .Purchased:
            if transaction.payment.productIdentifier == "MySecondGameGreenShip" {
                handleGreenShipPurchased()
            }
            queue.finishTransaction(transaction)
        case .Restored:
            if transaction.payment.productIdentifier == "MySecondGameGreenShip" {
                handleGreenShipPurchased()
            }
            queue.finishTransaction(transaction)
        case .Failed:
            print("Payment Error: %@", transaction.error)
            queue.finishTransaction(transaction)
        default:
            print("Transaction State: %@", transaction.transactionState)
        }
    }
}



func handleGreenShipPurchased() {
    greenShipPurchased = true
    checkAndActivateGreenShip()
    // persist the purchase locally
    NSUserDefaults.standardUserDefaults().setBool(true, forKey: "MySecondGameGreenShip")
}

func checkAndActivateGreenShip() {
    if NSUserDefaults.standardUserDefaults().boolForKey("MySecondGameGreenShip") {
        greenShipPurchased = true

    }
}

Solution

  • Whenever you feel like you conform to a protocol but Xcode tells you you don't it is a wise idea to look at the protocol documentation to see where you might be wrong. In this case the protocol SKPaymentTransactionObserver has one required method:

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
    

    you try to implement that method but fail since your code does not match the function definition. You wrote

    func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
    

    To fix it simply take the correct function head and slightly alter your following code:

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch (transaction.transactionState) {
        ...
    }