Skip to main content

Troubleshooting

This page covers the most common issues developers encounter when integrating the AppCoins iOS SDK — in both Swift and Unity — and how to resolve them. Each entry follows the same structure: symptom, causes, and fix.


isAvailable() always returns false

Symptom: AppcSDK.isAvailable() (Swift) or the Automatic mode selection in AppCoinsIAP.ConfigureStoreAsync (Unity) never activates AppCoins billing, even on a real device.

Causes:

  1. Testing setup is not configured — the app is not recognized as distributed through an alternative marketplace.
  2. The device is running iOS earlier than 17.4.
  3. The app was installed from the Apple App Store or TestFlight.

See How Runtime Detection Works for the full list of install sources and their effect on availability.

Fix:

  1. In Xcode, open your target's Build Settings and search for Marketplaces. Under Deployment, set the value to com.aptoide.ios.store.
  2. In your scheme, go to Run → Options. In the Distribution dropdown, select com.aptoide.ios.store.
  3. Run on a real device running iOS 17.4 or later.

Alternatively, you can force AppCoins mode at runtime without rebuilding by opening Safari on the device and navigating to:

{bundle_id}.iap://wallet.appcoins.io/default/mode?value=appcoins

Replace {bundle_id} with your application's bundle identifier. Use value=apple to test the Apple billing fallback, or value=automatic to restore normal detection. Note that mode overrides have no effect on builds installed from the Apple App Store, to prevent misuse.


notEntitled error on purchase

Symptom: Calling product.purchase() (Swift) or controller.InitiatePurchase (Unity) immediately throws or returns AppCoinsSDKError.notEntitled without presenting a payment sheet.

Causes:

  • The Keychain Sharing capability has not been added to the target.
  • The keychain group is set to the wrong value (Xcode sometimes pre-fills the team identifier or bundle identifier).

Fix:

  1. In Xcode, select your target and open the Signing & Capabilities tab.
  2. Click + and add the Keychain Sharing capability.
  3. In the Keychain Groups list, delete any existing entry and type exactly com.aptoide.appcoins-wallet.
  4. Clean and rebuild.
⚠️
The Keychain Sharing group must be set to exactly com.aptoide.appcoins-wallet — no prefix, no suffix, no bundle ID. Any other value causes notEntitled at runtime.

Products array is empty / productUnavailable error

Symptom: Product.products(for:) (Swift) returns an empty array, or GetProducts (Unity) fires OnProductsFetchFailed. Alternatively, product.purchase() throws productUnavailable.

Causes:

  1. The app has not yet been submitted and approved in Aptoide Connect.
  2. The SKU identifiers in your code do not exactly match the ones defined in Aptoide Connect (identifiers are case-sensitive).

Fix:

  1. Open Aptoide Connect and confirm the app listing exists and has been approved.
  2. Navigate to Products in your app listing and verify each SKU identifier matches character-for-character what you pass to Product.products(for:) (Swift) or your Unity ProductCatalog.
  3. Products become queryable only after the review passes.
⚠️
In-app products can only be queried after the application has been reviewed and approved on Aptoide Connect.

Payment redirect does not complete (purchase hangs)

Symptom: The user is redirected to the payment method but the app never receives the purchase result. The purchase stays permanently pending.

Causes (Swift):

  1. AppcSDK.handle(redirectURL:) is not being called in the URL context handler.
  2. The URL scheme $(PRODUCT_BUNDLE_IDENTIFIER).iap with role Editor is missing from Info → URL Types.

Fix:

  1. Open your target's Info tab in Xcode. Under URL Types, confirm there is an entry with:
    • URL Schemes: $(PRODUCT_BUNDLE_IDENTIFIER).iap
    • Role: Editor
  2. In SceneDelegate.swift, confirm that scene(_:openURLContexts:) calls AppcSDK.handle(redirectURL:). Note: AppcSDK.initialize() must be called at app launch in willConnectTo, not inside URL handlers:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if AppcSDK.handle(redirectURL: URLContexts.first?.url) { return }
// your existing URL handling
}

For Unity, the post-build script adds the URL scheme automatically. If the purchase still hangs, rebuild the Xcode project from Unity and verify the URL Type appears in the Info tab of the Xcode target.


Users missing purchases after app crash or relaunch

Symptom: A user paid but did not receive their item because the app crashed or was force-quit during the purchase flow. After relaunching, the item is still missing.

Cause: The app is not iterating over Transaction.unfinished (Swift) or calling FetchPurchases (Unity) on startup to recover paid-but-unconsumed transactions.

Fix (Swift):

Add an unfinished-transaction pass in your app's initialization sequence, after the isAvailable() check:

func processUnfinishedTransactions() async {
guard await AppcSDK.isAvailable() else { return }

for await verificationResult in Transaction.unfinished {
switch verificationResult {
case .verified(let transaction):
giveItemToUser(productID: transaction.productID)
await transaction.finish()
case .unverified(_, let error):
print("Unverified unfinished transaction: \(error.description)")
}
}
}

Fix (Unity):

Set ProcessPendingOrdersOnPurchasesFetched(true) before Connect(), then call FetchPurchases() after. Unfinished purchases re-arrive through OnPurchasePending — the same handler used for new purchases.

async void Start()
{
await AppCoinsIAP.ConfigureStoreAsync(AppCoinsStoreMode.Automatic);

controller = UnityIAPServices.StoreController();
controller.OnPurchasePending += OnPurchasePending;

// Must be set before Connect so FetchPurchases results come through OnPurchasePending
controller.ProcessPendingOrdersOnPurchasesFetched(true);

await controller.Connect();
controller.FetchPurchases(); // recovers unfinished transactions
}
⚠️
Unfinished transactions are automatically refunded after 24 hours. Recover them on every app launch so users receive items they paid for.

transaction.id type mismatch (Swift)

Symptom: A compiler error or unexpected behavior when comparing or storing transaction.id — code that worked with StoreKit fails to compile or produces wrong results with the AppCoins SDK.

Cause: StoreKit's Transaction.id is UInt64. The AppCoins SDK's Transaction.id is String. Code copied from a StoreKit integration will break.

Fix:

Update all comparisons and storage to treat transaction.id as a String:

// Wrong (StoreKit pattern)
let id: UInt64 = transaction.id

// Correct (AppCoins SDK)
let id: String = transaction.id

If you are bridging both StoreKit and AppCoins billing in the same codebase, convert the StoreKit transaction ID when needed:

let appCoinsCompatibleID = String(storeKitTransaction.id)

Unity: ConfigureStoreAsync was not awaited before Connect

Symptom: Unity IAP connects but uses the wrong billing provider, or OnProductsFetched never fires. AppCoins billing is not active even on a device where it should be.

Cause: controller.Connect() was called before ConfigureStoreAsync finished. The AppCoins store provider was not yet registered when Unity IAP initialized.

Fix:

Always await ConfigureStoreAsync before calling Connect:

async void Start()
{
var selectedStore = await AppCoinsIAP.ConfigureStoreAsync(AppCoinsStoreMode.Automatic);
Debug.Log("Selected store: " + selectedStore);
await controller.Connect();
}
⚠️
ConfigureStoreAsync must be awaited before calling Connect. If it is not, the AppCoins store may not be registered in time and Unity IAP will fall back to Apple or fail silently.

Unity: AppCoins not activating on device (Automatic mode)

Symptom: Running on a real iOS 17.4+ device with Automatic mode, but AppCoinsIAP.SelectedStore is always "AppleAppStore".

Cause: The testing setup is missing — same root cause as isAvailable() returning false in Swift.

Fix:

Apply the same Xcode and scheme settings:

  1. In Build Settings, set Marketplaces to com.aptoide.ios.store.
  2. In the scheme's Run → Options → Distribution, select com.aptoide.ios.store.

Or force AppCoins mode using the deep link from Safari on the device:

{bundle_id}.iap://wallet.appcoins.io/default/mode?value=appcoins