前言

  • Swift作为当前在github上成长最快的语言之一,本人在学习iOS未曾学习过OC,因此在做iOS项目过程中全部采用了Swift,下面详细介绍下Swift的内购的实现。github地址:https://github.com/RyanLeeLY/...

起步

  • 首先我就不介绍如何在iTunesConnect上添加内购商品了,总之添加完商品后,我们需要用到的是productIdentifiers即你填写的商品ID,这个ID是商品在商店中的唯一标识。

  • 导入StoreKit,创建一个类,SKPaymentTransactionObserver协议:当交易在队列中有更新或者移出队列时这个观察者会被调用;SKProductsRequestDelegate实现该协议的代理会受到商品请求返回的信息。

public class LYIAPManager: NSObject,SKPaymentTransactionObserver,SKProductsRequestDelegate
  • 自定义协议,LYIAPRequestDelegate处理请求成功后的代理,LYIAPPaymentDelegate交易时的代理,
    LYIAPDelegate继承上面两个协议,方便使用。

@objc public protocol LYIAPDelegate: LYIAPRequestDelegate,LYIAPPaymentDelegate{
    
}

@objc public protocol LYIAPRequestDelegate: NSObjectProtocol{
    @objc optional func requestFinished()

}

@objc public protocol LYIAPPaymentDelegate: NSObjectProtocol{
    func transactionPurchased(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction)
    @objc optional func transactionFailed(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction)
    @objc optional func transactionRestore(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction)
    @objc optional func transactionDeferred(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction)
    @objc optional func transactionPurchasing(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction)
    @objc optional func transactionRestoreFailedWithError(_ error:NSError)
    @objc optional func transactionRestoreFinished(_ isSuccess:Bool)
    
}

全部实现

//
//  LYIAPManager.swift
//  crater
//
//  Created by 李尧 on 2016/10/11.
//  copyright © 2016年 secstudio. All rights reserved.
//

import UIKit
import StoreKit

private func printLog<T>(_ message:T,file:String = #file,method:String = #function,line:Int = #line){
    #if DEBUG
        print("\((file as Nsstring).lastPathComponent)[\(line)],\(method): \(message)")
    #endif
}

@objc public protocol LYIAPDelegate: LYIAPRequestDelegate,transaction: SKPaymentTransaction)
    @objc optional func transactionRestoreFailedWithError(_ error:Error)
    @objc optional func transactionRestoreFinished(_ isSuccess:Bool)
    
}

public let LYIAP = LYIAPManager.LYIAPInstance

public class LYIAPManager: NSObject,SKProductsRequestDelegate {
    fileprivate let VERIFY_RECEIPT_URL = "https://buy.itunes.apple.com/verifyReceipt"
    fileprivate let ITMS_SANDBox_VERIFY_RECEIPT_URL = "https://sandBox.itunes.apple.com/verifyReceipt"
    fileprivate var restoreSuccess = false
    
    fileprivate var productDict:NSMutableDictionary?
    
    static let LYIAPInstance = LYIAPManager()
    
    var request:SKProductsRequest?
    var observer:SKPaymentTransactionObserver?
    
    weak var delegate:LYIAPDelegate?
    weak var requestDelegate:LYIAPRequestDelegate?
    weak var paymentDelegate:LYIAPPaymentDelegate?
    
    fileprivate override init() {
        
    }
    /**
     Example: let productsIds = NSSet(array: ["com.xxx.xxx.abc"])
     */
    func setRequestWithProducts(_ productsIds: NSSet,delegate: LYIAPDelegate?) {
        request = SKProductsRequest(productIdentifiers: productsIds as! Set<String>)
        request?.delegate = self
        SKPaymentQueue.default().add(self)
        
        if(delegate != nil){
            self.delegate = delegate!
            paymentDelegate = delegate!
            requestDelegate = delegate!
        }
    }
    
    func setPaymentTransactionsDelegate(_ delegate: LYIAPPaymentDelegate){
        paymentDelegate = delegate
    }
    
    func setProductsRequestDelegate(_ delegate: LYIAPRequestDelegate){
        requestDelegate = delegate
    }
    
    func removeRequestDelegate(){
        requestDelegate = nil
    }
    
    func removeProductsDelegate(){
        paymentDelegate = nil
    }
    
    func startRequest(){
        testIsNil()
        request?.start()
    }
    
    func cancelRequest(){
        testIsNil()
        request?.cancel()
    }
    
    func startPaymentWithProductId(_ productId: String){
        //if loaded
        if(SKPaymentQueue.canMakePayments()){
            guard productDict != nil else{
                printLog("products haven't been loaded")
                return
            }
            requestPaymentWithProduct(productDict![productId] as! SKProduct)
        }else{
            printLog("IAP is not supported!")
        }
    }
    
    func restorePayment(){
        restoreSuccess = false
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
    
    public func productsRequest(_ request: SKProductsRequest,didReceive response: SKProductsResponse) {
        if (productDict == nil) {
            printLog("first load products")
            productDict = NSMutableDictionary(capacity: response.products.count)
        }
        for product in response.products  {
            printLog("product \(product.productIdentifier) loaded")
            productDict!.setobject(product,forKey: product.productIdentifier as NScopying)
        }
        requestDelegate?.requestFinished?()
    }
    
    public func paymentQueue(_ queue: SKPaymentQueue,updatedTransactions transactions: [SKPaymentTransaction]) {
        print("updatedTransactions")
        for transaction in transactions {
            switch transaction.transactionState{
            case .purchased:
                printLog("purchased")
                paymentDelegate?.transactionPurchased(queue,transaction: transaction)
                SKPaymentQueue.default().finishTransaction(transaction)
                break
            case .Failed:
                printLog("Failed")
                paymentDelegate?.transactionFailed?(queue,transaction: transaction)
                SKPaymentQueue.default().finishTransaction(transaction)
                break
            case .restored:
                printLog("restore")
                restoreSuccess = true
                paymentDelegate?.transactionRestore?(queue,transaction: transaction)
                SKPaymentQueue.default().finishTransaction(transaction)
                break
            case .purchasing:
                paymentDelegate?.transactionPurchasing?(queue,transaction: transaction)
                printLog("purchasing")
                break
            case .deferred:
                paymentDelegate?.transactionDeferred?(queue,transaction: transaction)
                printLog("deferred")
                break
            }
        }
    }
    
    public func paymentQueue(_ queue: SKPaymentQueue,restoreCompletedTransactionsFailedWithError error: Error) {
        printLog("retore Failed with error:\(error)")
        paymentDelegate?.transactionRestoreFailedWithError?(error)
        
    }
    public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        printLog("finished restore")
        paymentDelegate?.transactionRestoreFinished?(restoreSuccess)
    }
    
    private func requestPaymentWithProduct(_ product: SKProduct){
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
    }
    
    
    
    private func testIsNil(){
        if(request == nil){
            printLog("request hasn't been init")
        }else if(request?.delegate == nil){
            printLog("request delegate hasn't been set")
        }
    }
    
    func verifyPruchase(completion:@escaping (NSDictionary?,NSError?) -> Void) {
        // 验证凭据,获取到苹果返回的交易凭据
        let receiptURL = Bundle.main.appStoreReceiptURL
        // 从沙盒中获取到购买凭据
        let receiptData = NSData(contentsOf: receiptURL!)
        #if DEBUG
            let url = NSURL(string: ITMS_SANDBox_VERIFY_RECEIPT_URL)
        #else
            let url = NSURL(string: VERIFY_RECEIPT_URL)
        #endif
        let request = NSMutableuRLRequest(url: url! as URL,cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy,timeoutInterval: 10.0)
        request.httpMethod = "POST"
        let encodeStr = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithLineFeed)
        let payload = Nsstring(string: "{\"receipt-data\" : \"" + encodeStr! + "\"}")
        let payloadData = payload.data(using: String.Encoding.utf8.rawValue)
        request.httpBody = payloadData;
        
        let session = URLSession.shared
        let semaphore = dispatchSemaphore(value: -1)
        
        let dataTask = session.dataTask(with: request as URLRequest,completionHandler: {(data,response,error) -> Void in
                if error != nil{
                    print("error1")
                    completion(nil,error as NSError?)
                }else{
                    if (data==nil) {
                        print("error2")
                        completion(nil,error as NSError?)
                    }
                    do{
                        let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: data!,options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
                        if (jsonResult.count != 0) {
                            // 比对字典中以下信息基本上可以保证数据安全
                            // bundle_id&application_version&product_id&transaction_id
                            // 验证成功
                            let receipt = jsonResult["receipt"] as! NSDictionary
                            completion(receipt,nil)
                        }
                        print(jsonResult)
                    }catch{
                        print("error3")
                        completion(nil,nil)
                    }
                }
                
                semaphore.signal()
        }) as URLSessionTask
        dataTask.resume()
        semaphore.wait()
    }
}

使用方法

  • 新建一个ViewController,实现协议LYIAPDelegate

import UIKit
import StoreKit

class ViewController: UIViewController,LYIAPDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        let productsIds = NSSet(array: ["com.xxx.xxx.abc"])
        LYIAP.setRequestWithProducts(productsIds,delegate: self)
        LYIAP.startRequest()
        // Do any additional setup after loading the view,typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // dispose of any resources that can be recreated.
    }
    
    func requestFinished() {
        // Do something when products have been loaded.
    }
    
    func transactionPurchased(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) {
        // Identifier of the product that has been purchased
        debugPrint(transaction.payment.productIdentifier)
        
        LYIAP.verifyPruchase(completion: {(receipt,error) in
            // You can verify the transaction. In this callback,you will get the receipt if the transaction is verified by the APPLE. You can compare some tranction infomation with the receipt.
            debugPrint(receipt)
        })
    }
    
    func transactionRestore(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) {
        // Identifier of the product that has been restored
        // You must add restore function to your app accroding to APPLE's provisions
        debugPrint(transaction.payment.productIdentifier)
    }
    
    func transactionRestoreFinished(_ isSuccess: Bool) {
        // It is called when restore is finished. isSuccess will be true when some products have been restored successfully.
    }

}

部分代码参考来源:SaiWu博客园

Swift实现iOS内购的更多相关文章

  1. ios – 属性类型’id’与从’UIToolbar’警告继承的类型’id’不兼容

    我刚刚安装了xcode5,现在我收到了这个警告.我以前从未见过它.我的应用运行良好,但我讨厌看到一个警告.有谁知道这是什么以及如何解决它?

  2. ios – 无法识别的选择器发送到实例NSTimer Swift

    解决方法让updateTime成为一个类方法.如果它是在一个纯粹的Swift类中,你需要在@objc前面说明该方法的声明,如:

  3. ios – 使用Swift 3.0附近的蓝牙设备

    )我只是使用函数内置的参数来提及它确实是一个IOBluetoothDevice对象,而且只是要求打印这些信息.最后的说明我希望在Swift中使用IOBluetooth时,我创建的这个Q/A可以帮助其他有需要的人.缺乏任何教程和大量过时的Objective-C代码使得查找此信息非常具有挑战性.我要感谢@RobNapier在一开始就试图找到这个谜题的答案的支持.我还要感谢NotMyName在AppleDeveloper论坛上对我的post的回复.我将在iOS设备中更早地探索这种用法!

  4. ios – 类型推断(自动类型检测)如何在swift中工作?

    LLVM如何检测变量是一个字符串?

  5. ios – Swift可选项:语言问题,还是做错了什么?

    应该有可选的类型;type是但是,如果我这样做,它的工作原理:它似乎是基本的替代,但我可能会遗漏一些语言的细微差别.谁能对此有所了解?之后就像暧昧一样,更多,这是我的解决方案:这适用于所有非对象Swift对象,包括Swift字符串,数字等.感谢Viktor提醒我String不是Swift中的对象.如果您知道值的类型,您可以替换任何?使用适当的可选类型,如String?

  6. ios – 覆盖Swift中的超类委托

    我正在开发一个包含两个UIViews的Swift(v1.2)项目.MyView和MyViewSubclass.MyView有一个委托,我想在MyViewSubclass中覆盖它作为一个子协议,类似于UITableViews有一个UITableViewDelegate,它也符合超级uiscrollviewdelegate.我的第一个想法是覆盖超类属性,但这会导致编译器错误,因为子类不能覆盖具有不同类

  7. ios – 我可以在swift中将字符串转换为代码块吗?

    有没有办法将字符串转换为代码块?

  8. ios – Swift:方法重载只在返回类型上有所不同

    我一直在看Swift类,其中定义了两种方法,它们的返回类型不同.我不习惯使用允许这种语言的语言,所以我去寻找描述它如何在Swift中工作的文档.我在任何地方都找不到任何东西.我本来期望在Swift书中有关于它的整个部分.这记录在哪里?

  9. ios – 字符串资源Xcode swift

    我是iOS开发和Swift语言的新功能.而且我尝试制作简单的iOS应用程序,我需要在应用程序中使用一些字符串资源.当然,我可以将这个字符串放在我的*.swift文件中作为常量,但我认为这是一个坏的方法.我该怎么做?

  10. ios – 如何使用新的Apple Swift语言发布JSON

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部