// you need apple developer account for this
// Start by watching these CodeWithChris videos and skip Revenue cat. Link: https://youtu.be/Ecxucvej1Dc
// i have to add code for in app subscription later *.
//Log in to your Apple connect account //https://appstoreconnect.apple.com/.
//Access Users and Access:
//In iTunes Connect, navigate to "Users and Access."
//Click on "Sandbox Testers" under the "People" section.
//Add a New Sandbox Tester:
//Click the "+" button to add a new sandbox tester.
//Fill in the tester's details, such as first name, last name, and dummy email address.
import Foundation
import StoreKit
protocol SubscriptionManagerDelegate: AnyObject {
func transactionDidUpdate(state: SKPaymentTransactionState)
}
class SubscriptionManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
var delegate: SubscriptionManagerDelegate?
static let shared = SubscriptionManager()
private var productIdentifier = "com.InAppSubscription.Demo.MonthlySubscription"
private var product: SKProduct?
private var completionBlock: ((SKProduct?) -> Void)?
override init() {
super.init()
SKPaymentQueue.default().add(self)
}
func requestProductInfo1(myProductIentifier : String, completion: @escaping (SKProduct?) -> Void) {
productIdentifier = myProductIentifier
let productIdentifiers: Set<String> = [productIdentifier]
let request = SKProductsRequest(productIdentifiers: productIdentifiers)
request.delegate = self
request.start()
self.completionBlock = completion
}
func purchaseProduct() {
if SKPaymentQueue.canMakePayments() {
if let product = self.product {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
} else {
print("Product not available")
}
}
}
func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
if let product = response.products.first {
self.product = product
completionBlock?(product)
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
delegate?.transactionDidUpdate(state: .purchased)
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
delegate?.transactionDidUpdate(state: .failed)
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
delegate?.transactionDidUpdate(state: .restored)
SKPaymentQueue.default().finishTransaction(transaction)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
if let date = transaction.transactionDate {
let formattedDate = dateFormatter.string(from: date)
print("Status Check: Restored: \(transaction.payment.productIdentifier), \(formattedDate)")
}
default:
break
}
}
}
}
import UIKit
import StoreKit
class SubscriptionViewController: UIViewController, SubscriptionManagerDelegate {
let productInfoLabel = UILabel()
let transactionStatusLabel = UILabel()
var product: SKProduct?
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
SubscriptionManager.shared.delegate = self
requestProductInfo()
}
func setupUI() {
// Product Info Label
productInfoLabel.text = "Product Information:\nLine 1 of product info\nLine 2 of product info"
productInfoLabel.numberOfLines = 0 // 0 means unlimited lines
productInfoLabel.frame = CGRect(x: 20, y: 100, width: 300, height: 100)
view.addSubview(productInfoLabel)
// Transaction Status Label
transactionStatusLabel.text = "Transaction Status: "
transactionStatusLabel.frame = CGRect(x: 20, y: 210, width: 300, height: 30)
view.addSubview(transactionStatusLabel)
// Request Product Button
let requestProductButton = UIButton(type: .system)
requestProductButton.setTitle("Request Product Info", for: .normal)
requestProductButton.contentHorizontalAlignment = .left
requestProductButton.frame = CGRect(x: 20, y: 280, width: 200, height: 30)
requestProductButton.addTarget(self, action: #selector(requestProductInfo), for: .touchUpInside)
view.addSubview(requestProductButton)
// Purchase Button
let purchaseButton = UIButton(type: .system)
purchaseButton.setTitle("Purchase", for: .normal)
purchaseButton.contentHorizontalAlignment = .left
purchaseButton.frame = CGRect(x: 20, y: 320, width: 200, height: 30)
purchaseButton.addTarget(self, action: #selector(purchaseProduct), for: .touchUpInside)
view.addSubview(purchaseButton)
// Restore Purchases Button
let restorePurchasesButton = UIButton(type: .system)
restorePurchasesButton.setTitle("Restore Purchases", for: .normal)
restorePurchasesButton.contentHorizontalAlignment = .left
restorePurchasesButton.frame = CGRect(x: 20, y: 350, width: 200, height: 30)
restorePurchasesButton.addTarget(self, action: #selector(restorePurchases), for: .touchUpInside)
view.addSubview(restorePurchasesButton)
}
@objc func requestProductInfo() {
SubscriptionManager.shared.requestProductInfo1(myProductIentifier: "com.InAppSubscription.Demo.MonthlySubscription") { [weak self] product in
if let product = product {
self?.setValue("Product Information: \(product.localizedTitle), Price: \(product.price)")
} else {
self?.productInfoLabel.text = "Product Information not available."
}
}
}
@objc func purchaseProduct() {
SubscriptionManager.shared.purchaseProduct()
}
@objc func restorePurchases() {
SubscriptionManager.shared.restorePurchases()
}
@IBAction func showStatus(_ sender: Any) {
SubscriptionManager.shared.restorePurchases()
}
func setValue(_ value : String) {
DispatchQueue.main.async {
self.productInfoLabel.text = value
}
}
func transactionDidUpdate(state: SKPaymentTransactionState) {
DispatchQueue.main.async {
switch state {
case .purchased:
self.transactionStatusLabel.text = "Transaction Status: Purchased"
case .failed:
self.transactionStatusLabel.text = "Transaction Status: Failed"
case .restored:
self.transactionStatusLabel.text = "Transaction Status: Restored"
default:
print("Transaction state Empty")
break
}
}
}
}