php++内购续期订阅,iOS内购IAP(十九) —— 自动续订订阅(二)

版本记录

版本号

时间

V1.0

2019.05.07 星期二

前言

开始

首先看下代码组织结构

81523d7536ff?utm_source=desktop

下面看一下sb中的内容

81523d7536ff?utm_source=desktop

下面就是源码了

1. ViewController.swift

import UIKit

import StoreKit

class ViewController: UIViewController {

// MARK: - Instance Properties

var quotesContent: QuotesGroup!

var products: [SKProduct] = []

// MARK: - Outlets

@IBOutlet var quoteLbl: UILabel!

@IBOutlet var purchaseBttn: UIButton!

@IBOutlet var restoreBttn: UIButton!

// MARK: - Vie Lifecycle

override func viewDidLoad() {

super.viewDidLoad()

loadQuotesFromFile()

PoohWisdomProducts.store.requestProducts { [weak self] success, products in

guard let self = self else { return }

guard success else {

let alertController = UIAlertController(title: "Failed to load list of products",

message: "Check logs for details",

preferredStyle: .alert)

alertController.addAction(UIAlertAction(title: "OK", style: .default))

self.present(alertController, animated: true, completion: nil)

return

}

self.products = products!

}

if (PoohWisdomProducts.store.isProductPurchased(PoohWisdomProducts.monthlySub) ||

PoohWisdomProducts.store.isProductPurchased(PoohWisdomProducts.yearlySub)){

displayRandomQuote()

} else {

displayPurchaseQuotes()

}

}

func loadQuotesFromFile() {

guard let filePath = Bundle.main.path(forResource: "poohWisdom", ofType: "json") else {

fatalError("Quotes file path is incorrect!")

}

do {

let data = try Data(contentsOf: URL(fileURLWithPath: filePath))

quotesContent = try JSONDecoder().decode(QuotesGroup.self, from: data)

} catch {

fatalError(error.localizedDescription)

}

}

// MARK: - IBActions

@IBAction func purchaseSubscription(_ sender: Any) {

guard !products.isEmpty else {

print("Cannot purchase subscription because products is empty!")

return

}

let alertController = UIAlertController(title: "Choose your subscription",

message: "Which subscription option works best for you",

preferredStyle: .alert)

alertController.addAction(UIAlertAction(title: "Monthly",

style: .default,

handler: { action in

self.purchaseItemIndex(index: 0)

}))

alertController.addAction(UIAlertAction(title: "Yearly",

style: .default,

handler: { action in

self.purchaseItemIndex(index: 1)

}))

self.present(alertController, animated: true, completion: nil)

}

@IBAction func restorePurchases(_ sender: Any) {

PoohWisdomProducts.store.restorePurchases()

}

// MARK: - Displaying Quotes

private func displayPurchaseQuotes() {

quoteLbl.text = "Wanna get random words of wisdom from Winnie the Pooh?\n\n" +

"Press the 'Purchase' button!\nWhat are you waiting for?!"

}

private func purchaseItemIndex(index: Int) {

PoohWisdomProducts.store.buyProduct(products[index]) { [weak self] success, productId in

guard let self = self else { return }

guard success else {

let alertController = UIAlertController(title: "Failed to purchase product",

message: "Check logs for details",

preferredStyle: .alert)

alertController.addAction(UIAlertAction(title: "OK", style: .default))

self.present(alertController, animated: true, completion: nil)

return

}

self.displayRandomQuote()

}

}

private func displayRandomQuote() {

let randNum = Int.random(in: 0 ..< quotesContent.quotes.count)

quoteLbl.text = quotesContent.quotes[randNum]

restoreBttn.isHidden = true

}

}

2. IAPManager.swift

import StoreKit

public typealias ProductID = String

public typealias ProductsRequestCompletionHandler = (_ success: Bool, _ products: [SKProduct]?) -> Void

public typealias ProductPurchaseCompletionHandler = (_ success: Bool, _ productId: ProductID?) -> Void

// MARK: - IAPManager

public class IAPManager: NSObject {

private let productIDs: Set

private var purchasedProductIDs: Set

private var productsRequest: SKProductsRequest?

private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?

private var productPurchaseCompletionHandler: ProductPurchaseCompletionHandler?

public init(productIDs: Set) {

self.productIDs = productIDs

self.purchasedProductIDs = productIDs.filter { productID in

let purchased = UserDefaults.standard.bool(forKey: productID)

if purchased {

print("Previously purchased: \(productID)")

} else {

print("Not purchased: \(productID)")

}

return purchased

}

super.init()

SKPaymentQueue.default().add(self)

}

}

// MARK: - StoreKit API

extension IAPManager {

public func requestProducts(_ completionHandler: @escaping ProductsRequestCompletionHandler) {

productsRequest?.cancel()

productsRequestCompletionHandler = completionHandler

productsRequest = SKProductsRequest(productIdentifiers: productIDs)

productsRequest!.delegate = self

productsRequest!.start()

}

public func buyProduct(_ product: SKProduct, _ completionHandler: @escaping ProductPurchaseCompletionHandler) {

productPurchaseCompletionHandler = completionHandler

print("Buying \(product.productIdentifier)...")

let payment = SKPayment(product: product)

SKPaymentQueue.default().add(payment)

}

public func isProductPurchased(_ productID: ProductID) -> Bool {

return purchasedProductIDs.contains(productID)

}

public class func canMakePayments() -> Bool {

return SKPaymentQueue.canMakePayments()

}

public func restorePurchases() {

SKPaymentQueue.default().restoreCompletedTransactions()

}

}

// MARK: - SKProductsRequestDelegate

extension IAPManager: SKProductsRequestDelegate {

public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

print("Loaded list of products...")

let products = response.products

guard !products.isEmpty else {

print("Product list is empty...!")

print("Did you configure the project and set up the IAP?")

productsRequestCompletionHandler?(false, nil)

return

}

productsRequestCompletionHandler?(true, products)

clearRequestAndHandler()

for p in products {

print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")

}

}

public func request(_ request: SKRequest, didFailWithError error: Error) {

print("Failed to load list of products.")

print("Error: \(error.localizedDescription)")

productsRequestCompletionHandler?(false, nil)

clearRequestAndHandler()

}

private func clearRequestAndHandler() {

productsRequest = nil

productsRequestCompletionHandler = nil

}

}

// MARK: - SKPaymentTransactionObserver

extension IAPManager: SKPaymentTransactionObserver {

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

for transaction in transactions {

switch (transaction.transactionState) {

case .purchased:

complete(transaction: transaction)

break

case .failed:

fail(transaction: transaction)

break

case .restored:

restore(transaction: transaction)

break

case .deferred:

break

case .purchasing:

break

}

}

}

private func complete(transaction: SKPaymentTransaction) {

print("complete...")

productPurchaseCompleted(identifier: transaction.payment.productIdentifier)

SKPaymentQueue.default().finishTransaction(transaction)

}

private func restore(transaction: SKPaymentTransaction) {

guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }

print("restore... \(productIdentifier)")

productPurchaseCompleted(identifier: productIdentifier)

SKPaymentQueue.default().finishTransaction(transaction)

}

private func fail(transaction: SKPaymentTransaction) {

print("fail...")

if let transactionError = transaction.error as NSError?,

let localizedDescription = transaction.error?.localizedDescription,

transactionError.code != SKError.paymentCancelled.rawValue {

print("Transaction Error: \(localizedDescription)")

}

productPurchaseCompletionHandler?(false, nil)

SKPaymentQueue.default().finishTransaction(transaction)

clearHandler()

}

private func productPurchaseCompleted(identifier: ProductID?) {

guard let identifier = identifier else { return }

purchasedProductIDs.insert(identifier)

UserDefaults.standard.set(true, forKey: identifier)

productPurchaseCompletionHandler?(true, identifier)

clearHandler()

}

private func clearHandler() {

productPurchaseCompletionHandler = nil

}

}

3. PoohWisdomProducts.swift

import Foundation

public struct PoohWisdomProducts {

public static let monthlySub = "com.razeware.poohWisdom.monthlySub"

public static let yearlySub = "com.razeware.poohWisdom.yearlySub"

public static let store = IAPManager(productIDs: PoohWisdomProducts.productIDs)

private static let productIDs: Set = [PoohWisdomProducts.monthlySub, PoohWisdomProducts.yearlySub]

}

public func resourceNameForProductIdentifier(_ productIdentifier: String) -> String? {

return productIdentifier.components(separatedBy: ".").last

}

4. QuotesGroup.swift

public struct QuotesGroup: Decodable {

public let quotes: [String]

}

后记

本篇主要讲述了自动续订订阅,感兴趣的给个赞或者关注~~~

81523d7536ff?utm_source=desktop

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值