Flagship
Revenue Sharing iOS SDK
iOS SDK · StoreKit 2
Passive StoreKit 2 monitoring SDK for reliable revenue attribution and fraud prevention.
- iOS SDK
- StoreKit 2
- Swift
- StoreKit 2
- Swift Package
- Networking

Summary
An iOS SDK for referral revenue attribution. Apps integrate it once, and it monitors StoreKit purchases to report transactions to a backend for verification and commission splitting. I joined to improve reliability, reduce duplicate reporting, and align expectations with real StoreKit behavior. Key highlights:
- Passive StoreKit 2 listener with automatic reporting (no “report purchase” calls from host app)
- Stronger deduplication and bounded retries to prevent backend spam
- Clear documentation of unavoidable platform limits (especially for consumables)
Quick facts
- Role: iOS SDK engineer (hired to harden StoreKit monitoring + reporting)
- Timeframe: Short engagement + follow-up scope planned, then paused by client
- Platform: iOS 15+ (Swift Package), plus a SwiftUI example app
- Status: Paused (client postponed further SDK work)
- Team: Small founder-led team (me + founder)
Problem
- SDK had to detect purchases even if the host app “forgets” to report them. Anti-fraud requirement.
- StoreKit 2 doesn’t provide a true always-on callback for same-device purchases independent of purchase handling.
- Backend was getting spammed from replays and retry loops, especially on app launch.
Solution
I implemented a two-path capture strategy: Transaction.updates for replay/out-of-app cases plus periodic Transaction.currentEntitlements polling for near-real-time detection of subscriptions and non-consumables. I also tightened reporting to be idempotent on-device and bounded on failures, and clarified what must be enforced server-side.
- Added device-side dedupe + max retry attempts per transaction, and recommended backend idempotency
Architecture
- Swift Package entry point (AppRevShareSDK) wrapping APIClient + StoreKitManager
- StoreKitManager runs a single listener, consumes Transaction.updates and currentEntitlements polling
- Verified transactions only (StoreKit VerificationResult gating)
- Local persistence in UserDefaults for devKey, identifiers, processed transaction IDs, attempt counters
- Thread-safe dedupe helpers (locks around processed IDs / attempt counts)
- Network layer with typed request/response models for register, update identifier, report payment
- Opt-in logger with header masking and structured request/response/error logs
- SwiftUI example app to validate integration: init, register, update identifier, purchase trigger
Tech stack
- iOS: Swift, StoreKit 2, SwiftUI (example app)
- Architecture: Swift Package, passive listener, on-device idempotency + retry control
- Backend/Infra: REST API contract (register/update/report), server-side App Store verification (client-owned)
- Tooling: Xcode, XCTest, structured logging utilities
Hard problems solved
- Identified and corrected a key assumption: Transaction.updates is not a universal “every purchase” stream for same-device purchases
- Designed a practical capture strategy that improves reliability without heavy CPU/RAM cost (updates + entitlements polling)
- Reduced backend spam by preventing duplicate re-reporting on replayed transactions and by adding bounded retries
- Implemented safe on-device idempotency while documenting its limits (reinstall wipes storage; backend must be idempotent)
- Worked within a backend constraint where registration could disable the SDK entirely (success=false) and ensured zero calls after disable
- Validated flows despite referral-gated registration and server-side transaction verification rejecting mismatched sandbox data
Impact / Results
- Improved purchase detection reliability for subscriptions and non-consumables while documenting consumable limitations
- Prevented repeated reporting loops and reduced noisy traffic patterns during app boot/replay scenarios
- Delivered changes via a feature branch and proof via recorded demos against a test app