Releases: SpendOwl/spendowl-ios
v1.2.1
Fixed
- Recover missed consumables. The startup recovery scan now uses
Transaction.allinstead ofTransaction.currentEntitlements, so consumable purchases that the live listeners missed (app killed mid-flow, a failed send) are recovered on the next launch for already-attributed users — bringing consumables to the same reliability as non-consumables and subscriptions. - Durable purchase dedup. Transactions are marked as sent only after a confirmed network send, and the in-memory pending set is seeded from the persisted queue at launch. An event dropped from the bounded retry queue before sending is now recovered on a later launch instead of being lost, and is never enqueued as a duplicate.
Revenue totals are unaffected; this improves the paid/organic attribution split for consumables going forward. No public API changes.
v1.2.0
What's New
Hardened First-Launch Attribution
Reduces attribution loss on first launch when the device or app is in a flaky state. Diagnosed against real production traffic where Apple-reported installs vastly exceeded SpendOwl-recorded attributions in small-volume geos / basic ads — a meaningful slice of the gap is recoverable on the SDK side.
Three reliability layers added to the attribution path:
-
Token retry —
AAAttribution.attributionToken()is retried with exponential backoff (5 attempts at 0 / 0.5 / 1 / 2 / 4s) on the transientAAAttributionErrorDomaincodes 1 (NetworkError) and 2 (InternalError). Code 3 (PlatformNotSupported) is permanent and short-circuits. -
Disk-persisted attribution queue — the attribution payload is written to a single-slot UserDefaults queue before the first send and cleared on success. If the user backgrounds or kills the app before the send completes, the next launch replays the persisted payload before attempting a fresh fetch.
-
Background task assertion — the attribution Task spawned in
SpendOwl.configure()is wrapped inUIApplication.beginBackgroundTask(...)on iOS so a quick backgrounding right after configure doesn't suspend the request. The expiration handler ends the task to avoid OS termination.
Other Changes
- PurchaseTracker —
NSLockinteractions extracted into sync helpers for Swift 6 strict-concurrency compatibility (silences async-context lock warnings). - Tests —
SpendOwlTests.swiftsplit into three feature-axis files (SpendOwlTests,EventQueueTests,AttributionTests); +9 new tests covering token retry success/exhaust/permanent-error, queue save/load/clear/persistence/corruption, positive replay, and failed-send retains-payload semantics.
v1.1.0
What's New
Fully Automatic Purchase Tracking
Purchases are now tracked automatically — no code changes needed beyond SpendOwl.configure(apiKey:).
The SDK uses three complementary listeners to capture every purchase:
- SKPaymentTransactionObserver — catches the first purchase in the same session
- Transaction.updates — catches renewals, cross-device, Ask to Buy, and offer codes
- Transaction.currentEntitlements — startup safety net for missed purchases
Deduplication is handled atomically via transactionId. The SDK is fully read-only — transaction.finish() is never called, so it works alongside RevenueCat, Adapty, or any other StoreKit integration.
Other Changes
- Thread-safe
EventQueuewithNSLock - Improved pre-commit checks in
CLAUDE.md - New tests for
PurchaseEventoptional fields and transaction ID bounds
Full Changelog: v1.0.0...v1.1.0
v1.0.0
SpendOwl iOS SDK v1.0.0
First public release of the SpendOwl iOS SDK.
Features
- Apple Ads Attribution — Backend-driven attribution using AdServices
AAAttribution.attributionToken() - StoreKit 2 Purchase Tracking — Automatic transaction observation and receipt validation
- Privacy Manifest — Bundled
PrivacyInfo.xcprivacyfor App Store compliance - Persistent Event Queue — Automatic retry with exponential backoff for failed API calls
- Crash-Proof API — All public methods are designed to never crash your app
Requirements
- iOS 15.0+
- Swift 5.9+
- Xcode 15.0+
Installation
Swift Package Manager
https://github.com/SpendOwl/spendowl-ios.git
Add the package URL in Xcode via File → Add Package Dependencies, or add to your Package.swift:
dependencies: [
.package(url: "https://github.com/SpendOwl/spendowl-ios.git", from: "1.0.0")
]