// 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 } } } }