Skip to content

Conversation

@yusuftor
Copy link
Collaborator

@yusuftor yusuftor commented Jan 27, 2026

Changes in this pull request

  • TestModeManager: detects test mode users from config, fetches products from /v1/products endpoint, caches as StoreProduct
  • StoreKitManager: in test mode, uses cached test products instead of fetching from StoreKit
  • TransactionManager: intercepts purchase and restore flows in test mode — shows test mode drawer/entitlement picker instead of real StoreKit transactions
  • DeviceHelper: forces isSandbox = "true" when test mode active so all events route to sandbox
  • DependencyContainer: wires TestModeManager to StoreKitManager and DeviceHelper after initialization
  • ConfigManager: triggers test product fetching after config load when test mode detected

Checklist

  • All unit tests pass.
  • All UI tests pass.
  • Demo project builds and runs on iOS.
  • Demo project builds and runs on Mac Catalyst.
  • Demo project builds and runs on visionOS.
  • I added/updated tests or detailed why my change isn't tested.
  • I added an entry to the CHANGELOG.md for any breaking changes, enhancements, or bug fixes.
  • I have run swiftlint in the main directory and fixed any issues.
  • I have updated the SDK documentation as well as the online docs.
  • I have reviewed the contributing guide

🤖 Generated with Claude Code

Greptile Overview

Greptile Summary

This PR implements a comprehensive test mode system that allows Superwall team members to test purchase flows without real transactions. The system detects test users via config (testStoreUsers), fetches mock products from the /v1/products API endpoint, intercepts purchase/restore flows to show test dialogs, and forces sandbox mode for event routing.

Key Changes

  • Test Mode Detection: TestModeManager matches current user against config's testStoreUsers by aliasId, userId, or vendorId
  • Product Fetching: In test mode, ConfigManager.fetchTestModeProducts() fetches products from API and populates StoreKitManager.productsById with TestStoreProduct instances
  • Transaction Interception: TransactionManager checks test mode before purchases/restores and delegates to TestModeTransactionHandler which shows UI dialogs for simulating outcomes
  • Sandbox Override: DeviceHelper.isSandbox returns "true" when test mode active to route events to sandbox environment
  • Dependency Wiring: DependencyContainer properly wires TestModeManager to StoreKitManager and DeviceHelper after initialization

Architecture

The implementation follows the existing dependency injection patterns and integrates cleanly at key decision points (TransactionManager purchase/restore, StoreKitManager product fetching, DeviceHelper sandbox detection). Entitlement IDs are correctly converted from Int (API format) to String (internal Entitlement format) in both TestModeManager.fakePurchase() and TestModeTransactionHandler.

Confidence Score: 4/5

  • Safe to merge with minor considerations - well-architected test mode implementation with proper type conversions and clean integration points
  • The implementation is solid with proper architecture, dependency injection, and type handling. Previous review threads noted the Int-to-String entitlement ID conversion which is correctly implemented. The code follows existing patterns and integrates cleanly. Minor points to verify: ensure test mode can be disabled in production, validate the cold launch alert UX, and confirm the /v1/products endpoint is properly authenticated and scoped to the correct application.
  • Verify TestModeColdLaunchAlert.swift and TestModePurchaseDrawer.swift UI implementations for user experience quality

Important Files Changed

Filename Overview
Sources/SuperwallKit/TestMode/TestModeManager.swift implements test mode detection based on config, manages test products and entitlements with String storage for entitlement IDs
Sources/SuperwallKit/TestMode/TestModeTransactionHandler.swift handles test mode purchase/restore flows with UI dialogs, correctly converts Int entitlement IDs from API to String for Entitlement objects
Sources/SuperwallKit/StoreKit/Transactions/TransactionManager.swift integrates test mode intercepts for purchase and restore flows at lines 294-310 and 451-459, correctly handles entitlement ID conversion
Sources/SuperwallKit/StoreKit/StoreKitManager.swift adds test mode branch in getProducts (lines 71-99) to return cached test products instead of StoreKit products
Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift adds test mode manager reference and forces isSandbox = "true" when test mode active (lines 180-188)
Sources/SuperwallKit/Config/ConfigManager.swift evaluates test mode and fetches test products from API via fetchTestModeProducts when test mode active (lines 399-420, 641-669)

Sequence Diagram

sequenceDiagram
    participant User
    participant ConfigManager
    participant TestModeManager
    participant Network
    participant StoreKitManager
    participant TransactionManager
    participant TestModeTransactionHandler
    participant DeviceHelper
    
    Note over ConfigManager,TestModeManager: Config Load & Test Mode Detection
    ConfigManager->>Network: getConfig()
    Network-->>ConfigManager: Config (with testStoreUsers)
    ConfigManager->>TestModeManager: evaluateTestMode(config)
    TestModeManager->>TestModeManager: Check aliasId/userId/vendorId match
    TestModeManager-->>ConfigManager: isTestMode = true/false
    
    alt Test Mode Active
        ConfigManager->>Network: getSuperwallProducts()
        Network-->>ConfigManager: SuperwallProductsResponse
        ConfigManager->>TestModeManager: setProducts(products)
        ConfigManager->>StoreKitManager: setProduct(TestStoreProduct)
        StoreKitManager->>StoreKitManager: Cache test products in productsById
        ConfigManager->>DeviceHelper: testModeManager reference
        DeviceHelper->>DeviceHelper: isSandbox = "true" (forced)
    end
    
    Note over User,TransactionManager: Purchase Flow in Test Mode
    User->>TransactionManager: purchase(productId)
    TransactionManager->>TestModeManager: Check isTestMode
    
    alt Test Mode
        TransactionManager->>TestModeTransactionHandler: handlePurchase()
        TestModeTransactionHandler->>User: Present drawer (Purchase/Abandon/Fail)
        User-->>TestModeTransactionHandler: Select outcome
        TestModeTransactionHandler->>TestModeManager: fakePurchase(entitlementIds)
        TestModeManager->>TestModeManager: Convert Int to String IDs
        TestModeTransactionHandler-->>TransactionManager: PurchaseResult
    else Normal Mode
        TransactionManager->>TransactionManager: Call StoreKit
    end
    
    Note over User,TransactionManager: Restore Flow in Test Mode
    User->>TransactionManager: tryToRestore()
    TransactionManager->>TestModeManager: Check isTestMode
    
    alt Test Mode
        TransactionManager->>TestModeTransactionHandler: handleRestore()
        TestModeTransactionHandler->>User: Present alert (Active/No Subscription/Cancel)
        User-->>TestModeTransactionHandler: Select status
        TestModeTransactionHandler->>TestModeManager: fakePurchase() or resetEntitlements()
        TestModeTransactionHandler-->>TransactionManager: RestorationResult
    else Normal Mode
        TransactionManager->>TransactionManager: Call StoreKit restore
    end
Loading

Context used:

  • Context from dashboard - CLAUDE.md (source)

yusuftor and others added 5 commits January 27, 2026 13:52
Add CodingKeys for snake_case decoding since Web2App decoder
doesn't auto-convert from snake case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Maps V2Product API response data into a StoreProductType for use
as a test store product backed by the Superwall API.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TestModeManager for detecting test mode users via config
- Add TestStoreUser model parsed from config testStoreUsers
- Add TestModeColdLaunchAlert for showing test mode status on launch
- Add TestModePurchaseDrawer for simulating purchases (purchase/abandon/fail)
- Add TestModeTransactionHandler to intercept purchase and restore flows
- Override StoreKitManager.getProducts to use cached test products
- Override TransactionManager.purchase to show test mode drawer
- Override TransactionManager.tryToRestore to show entitlement picker
- Override DeviceHelper.isSandbox to return true in test mode
- Rename V2Product to SuperwallProduct across codebase
- Fetch products from /v1/products endpoint on config load when test mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tsById

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

yusuftor and others added 5 commits January 27, 2026 17:15
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add bundleId to config endpoint application query
- Include bundle_id_config in config JSON response
- Decode bundleIdConfig in Swift Config model
- Check bundle ID mismatch in evaluateTestMode — activates test mode
  when app bundle ID doesn't match config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants