Copyright © 2026 Unity Technologies
*Description*
.NET marshals a bool return value as a 4-byte Win32 BOOL by default. Objective-C BOOL and Swift Bool are one byte, so the remaining three bytes are whatever the register held, and UnmanagedType.Bool treats any non-zero value as true. A native false can therefore surface in managed code as true. Fix is return: MarshalAs(UnmanagedType.I1) on each affected DllImport.
Affects public API: IAppleExtensions.canMakePayments and simulateAskToBuy.
Four bool-returning P/Invokes per binding file, in both Runtime/Apple/iOSStoreBindings.cs and Runtime/AppleMacos/OSXStoreBindings.cs. All native signatures verified:
│ P/Invoke │ Native │ Fix
│ getUnityPurchasingCanMakePayments │ BOOL — UnityPurchasing.m:966 │ MarshalAs(I1)
│ getSimulateAskToBuy │ BOOL — UnityPurchasing.m:975 │ MarshalAs(I1)
│ unityPurchasing_CanMakePayments │ -> Bool — UnityStoreKitPlugin.swift:205 │ MarshalAs(I1)
│ unityPurchasing_checkEntitlement │ void — UnityStoreKitPlugin.swift:248 │ extern void
*Initial setup*
develop; iOS and macOS; any Unity version.
*Reproduction steps*
⚠️ Not reproduced — found by inspection while reviewing a related internal pull request. No customer report exists. Whether the unused bytes bite depends on codegen and the arm64 ABI (Swift zero-extends i1 to i8; the rest of x0 is undefined). Treat as a latent correctness defect, not a known-wrong result — and please don't describe it in release notes as a fixed crash.
Nothing in CI crosses a real P/Invoke boundary (Build MacOS bundle builds the bundle, not an app), so it's verifiable only on device or by inspection.
*Expected behaviour*
a native false reads as false in managed code.
*Actual behavior*
the managed side reads four bytes where the callee wrote one; a non-zero upper byte yields true.
Fix: 8 sites, swept. Also see notes: unityPurchasing_CanMakePayments sits on the StoreKit 2 arm of canMakePayments, which a recent internal fix left unfixed while fixing the StoreKit 1 arm four lines above.
Follow-up (separate ticket): INativeStore.CheckEntitlement is declared bool and documented as "whether the product is entitled", but no implementation can honour that (Apple and Google are both async/callback-based) and no caller reads it. Should be void. Touches AppleStub, AppleMacosStub, FakeStore, XboxStoreImpl, PaymentProviderImpl — mechanical, zero behaviour change.
Issues you vote on will appear here