Skip to content

Conversation

@yusuftor
Copy link
Contributor

@yusuftor yusuftor commented Jul 2, 2025

Problem

Deep links, especially on cold start, were not consistently triggering paywalls in the app. The issue was identified as a race condition where:

  1. Timing Issue: Deep link listening was set up AFTER SDK configuration in the example app
  2. Missing Cold Start Handling: No mechanism to handle the initial deep link that launches the app (cold start scenario)
  3. SDK Not Ready: Deep links received before SDK configuration completion were lost

Root Cause

  • _handleIncomingLinks() was called after Superwall.configure() in the example app
  • No handling of appLinks.getInitialLink() for cold start scenarios
  • No caching mechanism for deep links received before SDK is fully configured

Solution

1. Deep Link Caching in SDK

  • Added _pendingDeepLink static variable to cache links received before configuration
  • Added _isConfigured flag to track SDK configuration state
  • Modified handleDeepLink() to cache links when SDK not ready
  • Added _processPendingDeepLink() to process cached links after configuration

2. Enhanced Configuration Flow

  • Wrapped the completion callback to automatically process pending deep links
  • Set _isConfigured = true when configuration completes
  • Ensures cached deep links are processed immediately after SDK is ready

3. Cold Start Support

  • Added handleInitialDeepLink() static helper method
  • Updated example app to call appLinks.getInitialLink() for cold start scenarios
  • Moved deep link setup before SDK configuration to prevent race conditions

4. Improved Example App

  • Deep link handling now set up in initState() before configureSuperwall()
  • Added _handleInitialLink() method to handle cold start deep links
  • Enhanced logging with more descriptive messages
  • Removed redundant _handleIncomingLinks() call from configureSuperwall()

Testing

The fix addresses both warm start (app already running) and cold start (app launched by deep link) scenarios:

  • Warm Start: Deep links continue to work as before through uriLinkStream
  • Cold Start: Initial deep links are now captured via getInitialLink() and processed after SDK configuration

Files Changed

  • lib/src/public/Superwall.dart: Added caching mechanism and configuration flow improvements
  • example/lib/main.dart: Updated deep link handling order and added cold start support

Backward Compatibility

This change is fully backward compatible. Existing apps will continue to work without modifications, but can optionally adopt the new handleInitialDeepLink() pattern for better cold start support.

Next Steps

For apps experiencing cold start deep link issues:

  1. Move deep link setup before SDK configuration
  2. Add appLinks.getInitialLink() handling for cold start scenarios
  3. Consider using the new Superwall.handleInitialDeepLink() helper method

Fixes the cold start deep link issue identified by Daniil, Duncan, and Yusuf.

yusuftor and others added 2 commits July 2, 2025 20:18
- Add deep link caching in Superwall class for links received before SDK configuration
- Implement _processPendingDeepLink() to handle cached links after configuration completes
- Add handleInitialDeepLink() helper method for cold start scenarios
- Update example app to handle initial deep links using appLinks.getInitialLink()
- Move deep link setup before SDK configuration to prevent race conditions
- Improve logging with more descriptive messages for debugging

Fixes issue where deep links on cold start were not consistently triggering paywalls due to timing issues between app initialization and SDK configuration.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove custom caching mechanism in favor of native iOS SDK queuing
- iOS SDK already has static Superwall.handleDeepLink() that queues links before configure
- Android uses instance method but keeps existing behavior
- Keep existing instance method API for backward compatibility
- Add documentation about cross-platform behavior differences

The iOS native SDK already handles queuing deep links received before configuration via the static handleDeepLink method and DeepLinkRouter.storeDeepLink(). This is much cleaner than implementing our own caching layer.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@yusuftor yusuftor changed the base branch from main to develop July 2, 2025 18:33
yusuftor and others added 4 commits July 2, 2025 20:44
- Keep existing Superwall.shared.handleDeepLink() API for consistency
- iOS implementation now uses static Superwall.handleDeepLink() that safely queues links before configure
- Android keeps existing instance method behavior
- Example app uses getInitialLink() for cold start and handles before configure
- Move deep link setup before SDK configuration to prevent timing issues

The key insight is that iOS already has proper queuing via the static method, so we just need to ensure the Flutter wrapper uses it correctly. Much cleaner than adding new APIs.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Convert handleDeepLink to static method that bypasses shared instance
- Safe to call before configure() as it directly uses hostApi without instance dependency
- Update example app to use static Superwall.handleDeepLink() consistently
- iOS: Uses static Superwall.handleDeepLink() that queues links before configuration
- Android: Attempts immediate processing, gracefully handles if SDK not ready

This provides a clean API where Superwall.handleDeepLink() can be safely called
before configure() without worrying about shared instance initialization.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add handleDeepLink() instance method back for backward compatibility
- Add handleDeepLinkStatic() static method safe to call before configure()
- Update example app to use static method for cold start scenarios
- Move deep link setup before SDK configuration in example
- Add getInitialLink() handling for cold start deep links

Key changes:
- iOS: Both methods use static Superwall.handleDeepLink() that safely queues links
- Android: Both methods use instance method, gracefully handle if not configured
- hostApi available immediately as it just creates method channel communication
- Provides both instance (shared.handleDeepLink) and static (handleDeepLinkStatic) APIs

This solves the cold start deep link issue where links weren't consistently
triggering paywalls due to timing between app launch and SDK configuration.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ce method

- Keep only the original shared.handleDeepLink() instance method for simplicity
- Handle initial deep links after configure() completes in the completion callback
- iOS: Uses static Superwall.handleDeepLink() native method that safely queues links
- Android: Uses instance method after configuration
- Move _handleInitialLink() call to configure completion to ensure SDK is ready

This is the cleanest approach that leverages the native iOS queuing without
adding complexity or breaking changes to the Flutter API.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@yusuftor yusuftor force-pushed the fix/cold-start-deep-links branch from 548f437 to a2a184e Compare July 2, 2025 19:02
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