UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to listen for transaction updates in my Storekit setup

Forums > SwiftUI

I've got a basic Storekit setup for my app, allowing users to send a tip if they're enjoying the app.

What I'd like help with:

Resolving the warning: Making a purchase without listening for transaction updates risks missing successful purchases. Create a Task to iterate Transaction.updates at launch.

Code

Ok, so here's the code I use in my AboutView.swift (simplified):

import SwiftUI
import StoreKit

struct AboutScreen: View {
    // Binding for our sheet
    @Binding var isPresented: Bool

    // States for Storekit
    @State var products: [Product] = []

    // Our IAPs
    let iapProductIDs = Set([
        "MYAPP.iaps.smallTip",
        "MYAPP.iaps.mediumTip",
        "MYAPP.iaps.largeTip",
    ])

    var body: some View {
        NavigationView {
            VStack {
                // Only display if we have products
                if products.count != 0 {
                    ForEach(products) { product in
                        Button {
                            Task.init {
                                try await purchaseProduct(product)
                            }
                        } label: {
                            HStack {
                                Text(product.displayName)
                                Spacer()
                                Text(product.displayPrice)
                            }
                        }
                    }
                }
                // The rest of the view
                // ...
            }
            .navigationTitle("About")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        isPresented = false
                    } label: {
                        Text("Close")
                    }
                }
            }
        }
        .task {
            await fetchProducts()
        }
    }

    // Fetch our products and save them to `products`
    func fetchProducts() async {
        do {
            self.products = try await Product.products(for: iapProductIDs)
        } catch {
            print("STOREKIT: Unable to fetch products")
        }
    }

    // Buy a product 
    func purchaseProduct(_ product: Product) async throws -> StoreKit.Transaction {
        let result = try await product.purchase()

        switch result {
            case .pending:
                throw PurchaseError.pending

            case .success(let verification):
                switch verification {
                    case .verified(let transaction):
                        print("STOREKIT: Successfully purchased \(product.displayName)")
                        await transaction.finish()
                        return transaction

                    case .unverified:
                        throw PurchaseError.failed
                }
            case .userCancelled:
                throw PurchaseError.cancelled

            @unknown default:
                assertionFailure("Unexpected result")
                throw PurchaseError.failed
        }
    }

    enum PurchaseError: Error {
        case pending, failed, cancelled
    }
}

The purchase runs successfully in the simulator, but throws the following warning:

Making a purchase without listening for transaction updates risks missing successful purchases. Create a Task to iterate Transaction.updates at launch.

I've been looking online for a few hours and I can't reallly understand how to resolve this without using an entirely new storekit setup. The tutorials I've looked at all approach this differently, which is confusing me a lot.

2      

@Obelix I'm already using a ForEach for the buttons (see top code block). I was asking how I could use individual buttons instead, and how to correctly format targetting a specific product's purchase within the button.

Edit - updated my original post for extra clarity around the second question.

Edit - I've removed part of my original post to keep it focussed on a single problem instead of 2.

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.