I'm completely lost with StoreKit. I just want to add a button that people can click on and pay a small fee and that's it. Nothing else. I've tried looking at the StoreKit sample code here: https://developer.apple.com/documentation/storekit/in-app_purchase/implementing_a_store_in_your_app_using_the_storekit_api
The sample code doesn't explain how to actually take the code apart and implement it in a simple way. How can you have one single product that you can buy by pressing a button?
Here is my code in Store.swift
.
import Foundation
import StoreKit
typealias Transaction = StoreKit.Transaction
public enum StoreError: Error {
case failedVerification
}
class Store: ObservableObject {
@Published var products: [Product] = []
@Published var purchasedProducts: [Product] = []
let productList: [String: String]
init() {
if let path = Bundle.main.path(forResource: "Products", ofType: "plist"),
let plist = FileManager.default.contents(atPath: path) {
productList = (try? PropertyListSerialization.propertyList(from: plist, format: nil) as? [String: String]) ?? [:]
} else {
productList = [:]
}
products = []
Task {
await fetchProducts()
}
}
@MainActor
func fetchProducts() async {
do {
let storeProducts = try await Product.products(for: productList.keys)
var newProducts: [Product] = []
for product in storeProducts {
newProducts.append(product)
}
} catch {
print("Failed product request from the App Store server: \(error)")
}
}
func isPurchased(_ product: Product) async throws -> Bool {
return purchasedProducts.contains(product)
}
func purchase(_ product: Product) async throws -> Bool {
let result = try await product.purchase()
switch result {
case .success:
print("Product has been purchased.")
return true
case .userCancelled, .pending:
return false
default:
return false
}
}
func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
switch result {
case .unverified:
throw StoreError.failedVerification
case .verified(let safe):
return safe
}
}
@MainActor
func updateProductStatus() async {
var purchasedProducts: [Product] = []
for await result in Transaction.currentEntitlements {
do {
let transaction = try checkVerified(result)
if let product = products.first(where: { $0.id == transaction.productID }) {
purchasedProducts.append(product)
}
} catch {
print()
}
}
self.purchasedProducts = purchasedProducts
}
}
This is what I have in my view within my app.
Section(
header: Text("Products")
) {
ForEach(store.products) { product in
Button(
action: {
// Nothing.
},
label: {
Text("Product: " + product.displayName)
Spacer()
Text(product.displayPrice)
}
)
}
}
When I run the app, it builds successfully but it just shows the section heading and then it's blank underneath. What am I doing wrong here which means that my product is not loading?
I have;
- added the in-app purchase capability in my app
- added the product into a plist file in my app
- added the product into my app within App Store Connect
- added my products into the product config file in Xcode
- added the state object reference at the top of my view
- added the StoreKit scheme to my app
I don't think I've missed anything else out here.
Can someone please help me with where I'm going wrong? I've read so many tutorials and watched so many videos and they literally all explain it a different way.