Libgdx iOS内购测试崩溃问题求助及Swift类添加咨询
Hey there! Let's tackle your iOS In-App Purchase crash problem and answer your Swift class question clearly.
Crashes during IAP taps usually boil down to a handful of common issues—let's start with the most impactful checks:
Analyze Your Xcode Crash Logs
First, pull the exact crash call stack from Xcode. Look for:
- Which thread is crashing? Does it link to
Store.javamethods likepurchase()orrequestProducts()? - Are there error messages like
invalid product identifierorunrecognized selector sent to instance? These point to misconfigured products or broken native bindings. - Is there a null pointer exception in your
AppStoreResolver.javawhen handling transactions?
Check Your Libgdx IAP Implementation
For your Store.java and AppStoreResolver.java:
- Verify product initialization: Ensure you call
IAP.requestProducts()first, and only trigger purchases after the product list loads successfully (don't callpurchase()before theonProductsReceivedcallback fires). - Validate product IDs: Double-check that the IDs in your code match exactly what's configured in App Store Connect (case-sensitive, no extra spaces).
- Check transaction handling: In
AppStoreResolver.java, make sure you're not accessing null values when processingSKPaymentTransactionobjects, and always callfinishTransaction()for every transaction state (purchased, failed, restored).
You don't strictly need one—Libgdx's default IAP libraries (like libgdx-iap or RoboVM's native bindings) work perfectly well with Java/Objective-C. However, Swift can be helpful if:
- You need to use modern StoreKit 2 APIs (which are more intuitive than the old Objective-C ones)
- You want to customize the IAP flow (e.g., add custom error handling or UI)
- You're hitting edge cases that the default Java bindings don't handle
If you decide to add a Swift class, here's how to set it up:
Step 1: Add a Swift File to Your Xcode Project
- Open your Libgdx-generated Xcode project.
- Right-click the project directory → New File → Select Swift File (name it something like
IAPHelper.swift). - When Xcode asks to create an Objective-C bridging header, click Create—this file lets Swift and Java/RoboVM communicate.
Step 2: Implement Swift IAP Logic
Add this basic IAP helper class to IAPHelper.swift:
import StoreKit class IAPHelper: NSObject, SKPaymentTransactionObserver { static let shared = IAPHelper() // Callback type to send results back to Java typealias ProductRequestCompletion = ([SKProduct]?, Error?) -> Void private var productCompletion: ProductRequestCompletion? private override init() { super.init() SKPaymentQueue.default().add(self) } // Request products from App Store func fetchProducts(productIDs: [String], completion: @escaping ProductRequestCompletion) { productCompletion = completion let request = SKProductsRequest(productIdentifiers: Set(productIDs)) request.delegate = self request.start() } // Initiate purchase func purchaseProduct(product: SKProduct) { guard SKPaymentQueue.canMakePayments() else { productCompletion?(nil, NSError(domain: "IAPError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Payments disabled"])) return } let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) } // MARK: - SKProductsRequestDelegate func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { productCompletion?(response.products, response.invalidProductIdentifiers.isEmpty ? nil : NSError(domain: "IAPError", code: 1, userInfo: ["invalidIDs": response.invalidProductIdentifiers])) productCompletion = nil } // MARK: - SKPaymentTransactionObserver func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .purchased: // Send success notification to Java layer NotificationCenter.default.post(name: NSNotification.Name("IAPPurchaseSuccess"), object: transaction.payment.productIdentifier) queue.finishTransaction(transaction) case .failed: NotificationCenter.default.post(name: NSNotification.Name("IAPPurchaseFailed"), object: transaction.error) queue.finishTransaction(transaction) case .restored: NotificationCenter.default.post(name: NSNotification.Name("IAPPurchaseRestored"), object: transaction.payment.productIdentifier) queue.finishTransaction(transaction) default: break } } } }
Step 3: Bind Swift to Libgdx Java
Create a Java class in your Libgdx project to interface with the Swift helper using RoboVM:
import org.robovm.apple.foundation.NSError; import org.robovm.apple.foundation.NSNotification; import org.robovm.apple.foundation.NSNotificationCenter; import org.robovm.apple.storekit.SKProduct; import org.robovm.objc.annotation.Block; import org.robovm.objc.annotation.Method; import org.robovm.objc.annotation.NativeClass; import org.robovm.objc.annotation.Property; @NativeClass public class IAPHelper extends org.robovm.apple.foundation.NSObject { public static IAPHelper getShared() { return IAPHelper.class.getMethod("shared").invoke(null); } @Method(selector = "fetchProducts:completion:") public native void fetchProducts(String[] productIDs, @Block ProductCallback callback); @Method(selector = "purchaseProduct:") public native void purchaseProduct(SKProduct product); public interface ProductCallback { void invoke(SKProduct[] products, NSError error); } }
Step 4: Listen for Purchase Results in Java
In your AppStoreResolver.java, set up a notification listener to catch Swift's purchase events and pass them back to Libgdx's IAP system:
NSNotificationCenter.getDefaultCenter().addObserver(NSNotification.Name.valueOf("IAPPurchaseSuccess"), notification -> { String productID = (String) notification.getObject(); // Notify Libgdx's Store class of successful purchase purchaseSuccess(productID); }); // Repeat similar listeners for IAPPurchaseFailed and IAPPurchaseRestored
- Ensure your App ID in Apple Developer Portal has In-App Purchase enabled.
- Verify your Xcode project's Bundle ID matches exactly what's in App Store Connect.
- Use a sandbox Apple ID for testing (never use a real account).
- Confirm your App Store Connect products are in the Ready to Submit or Approved state (draft products won't load).
内容的提问来源于stack exchange,提问作者Karan

