swift
150 lines · 1 tab
Sofia Martinez
Jan 2026
1 tab
import StoreKit
class StoreKitManager: NSObject, ObservableObject {
static let shared = StoreKitManager()
@Published var products: [SKProduct] = []
@Published var isPurchasing = false
private var productIDs: Set<String> = [
"com.myapp.premium.monthly",
"com.myapp.premium.yearly",
"com.myapp.coin.pack.small"
]
private var completionHandlers: [String: (Result<SKPaymentTransaction, Error>) -> Void] = [:]
override private init() {
super.init()
SKPaymentQueue.default().add(self)
}
deinit {
SKPaymentQueue.default().remove(self)
}
// MARK: - Fetch Products
func fetchProducts() {
let request = SKProductsRequest(productIdentifiers: productIDs)
request.delegate = self
request.start()
}
// MARK: - Purchase Product
func purchase(_ product: SKProduct, completion: @escaping (Result<SKPaymentTransaction, Error>) -> Void) {
guard SKPaymentQueue.canMakePayments() else {
completion(.failure(StoreError.cannotMakePayments))
return
}
completionHandlers[product.productIdentifier] = completion
isPurchasing = true
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
// MARK: - Restore Purchases
func restorePurchases(completion: @escaping (Result<Void, Error>) -> Void) {
SKPaymentQueue.default().restoreCompletedTransactions()
}
// MARK: - Check Subscription Status
func hasActiveSubscription() -> Bool {
// Check receipt for active subscription
guard let receiptURL = Bundle.main.appStoreReceiptURL,
let receiptData = try? Data(contentsOf: receiptURL) else {
return false
}
// In production, validate receipt with your server
// This is simplified for example
return receiptData.count > 0
}
}
// MARK: - SKProductsRequestDelegate
extension StoreKitManager: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
DispatchQueue.main.async {
self.products = response.products
// Log invalid product IDs
for invalidID in response.invalidProductIdentifiers {
print("Invalid product ID: \(invalidID)")
}
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Product request failed: \(error)")
}
}
// MARK: - SKPaymentTransactionObserver
extension StoreKitManager: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
handlePurchased(transaction)
case .failed:
handleFailed(transaction)
case .restored:
handleRestored(transaction)
case .deferred, .purchasing:
break
@unknown default:
break
}
}
}
private func handlePurchased(_ transaction: SKPaymentTransaction) {
// Deliver content
deliverPurchase(for: transaction.payment.productIdentifier)
// Complete transaction
SKPaymentQueue.default().finishTransaction(transaction)
// Call completion handler
if let handler = completionHandlers[transaction.payment.productIdentifier] {
handler(.success(transaction))
completionHandlers.removeValue(forKey: transaction.payment.productIdentifier)
}
isPurchasing = false
}
private func handleFailed(_ transaction: SKPaymentTransaction) {
if let error = transaction.error as? SKError {
if error.code != .paymentCancelled {
print("Purchase failed: \(error.localizedDescription)")
}
if let handler = completionHandlers[transaction.payment.productIdentifier] {
handler(.failure(error))
completionHandlers.removeValue(forKey: transaction.payment.productIdentifier)
}
}
SKPaymentQueue.default().finishTransaction(transaction)
isPurchasing = false
}
private func handleRestored(_ transaction: SKPaymentTransaction) {
deliverPurchase(for: transaction.payment.productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
private func deliverPurchase(for productID: String) {
// Unlock content based on product ID
UserDefaults.standard.set(true, forKey: productID)
print("Content delivered for: \(productID)")
}
}
enum StoreError: Error {
case cannotMakePayments
case noProductsFound
}
1 file · swift
Explain with highlit
StoreKit enables selling digital goods and subscriptions within iOS apps. I request product info from App Store Connect with product identifiers, then display prices in the user's currency. Purchase flows use SKPaymentQueue to add transactions, which get validated on Apple's servers. Transaction observers handle purchase success, failure, and restoration. For subscriptions, I check receipt validation to verify active status. StoreKit 2 simplifies with async/await APIs and automatic receipt validation. App Store Server API handles server-side validation. I test purchases with sandbox accounts before production. Proper error handling addresses network issues, cancelled purchases, and pending transactions.
Related snips
swift
import Combine
import Foundation
class SearchViewModel: ObservableObject {
@Published var searchQuery = ""
@Published var results: [SearchResult] = []
Combine operators for data transformation
swift
combine
reactive
by Sofia Martinez
1 tab
swift
import SwiftUI
struct ContentView: View {
@State private var username = ""
@State private var isLoggedIn = false
@StateObject private var viewModel = LoginViewModel()
SwiftUI declarative UI with state management
swift
swiftui
ios
by Sofia Martinez
2 tabs
swift
import SwiftUI
struct CardModifier: ViewModifier {
var backgroundColor: Color = .white
var cornerRadius: CGFloat = 12
var shadowRadius: CGFloat = 5
Custom SwiftUI view modifiers for reusability
swift
swiftui
ios
by Sofia Martinez
2 tabs
swift
import Foundation
import UIKit
class ImageProcessor {
// Background processing with main thread updates
func processImage(_ image: UIImage, completion: @escaping (UIImage?) -> Void) {
Grand Central Dispatch for concurrency
swift
gcd
concurrency
by Sofia Martinez
1 tab
swift
import SwiftUI
enum Route: Hashable {
case postDetail(id: Int)
case userProfile(id: Int)
case settings
Navigation patterns with NavigationStack
swift
swiftui
navigation
by Sofia Martinez
2 tabs
swift
import UIKit
import WebKit
class WebViewController: UIViewController {
private var webView: WKWebView!
private var progressView: UIProgressView!
WKWebView for web content display
swift
wkwebview
webkit
by Sofia Martinez
1 tab
Share this code
Here's the card — post it anywhere.