Skip to content

Conversation

@ianrumac
Copy link
Contributor

@ianrumac ianrumac commented Jan 22, 2026

Changes in this pull request

  • Adds custom callback support to iOS SDK
  • Adds custom callback handler to PaywallPresentationHandler

Note: need to test, my simulators are not working again

  • Sample:
handler.onCustomCallback { callback async -> CustomCallbackResult in            
     // callback.name - the callback name from the paywall                       
     // callback.variables - optional dictionary of variables                    
                                                                                 
     switch callback.name {                                                      
     case "validate_email":                                                      
         let email = callback.variables?["email"] as? String                     
         let isValid = await validateEmail(email)                                
         if isValid {                                                            
             return .success(data: ["validated": true])                          
         } else {                                                                
             return .failure(data: ["error": "Invalid email"])                   
         }                                                                       
     default:                                                                    
         return .failure()                                                       
     }                                                                           
 }

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

Greptile Overview

Greptile Summary

Adds custom callback support to the iOS SDK, enabling paywalls to invoke developer-defined async handlers for custom validation and business logic. The implementation follows the existing permission handling pattern with a thread-safe registry that maps paywall identifiers to callback handlers.

  • Added public API types (CustomCallback, CustomCallbackResult, CustomCallbackBehavior) for callback requests and responses
  • Implemented CustomCallbackRegistry for thread-safe handler storage using NSLock
  • Extended PaywallPresentationHandler with onCustomCallback() method for registering handlers
  • Integrated callback lifecycle with paywall presentation (register on present, unregister on dismiss)
  • Added message decoding and handling in PaywallMessageHandler with proper error handling
  • Sends callback results back to webview via JSON serialization

Issues:

  • CHANGELOG.md not updated (required per style guide)
  • No unit tests added for the new functionality (required per checklist)

Confidence Score: 4/5

  • Safe to merge with minor process compliance issues
  • The implementation is technically sound with proper thread safety, error handling, and lifecycle management. However, the PR is missing CHANGELOG.md updates and unit tests, which are required per the repository's contributing guidelines and checklist.
  • No files require special attention - the implementation is clean and follows existing patterns

Important Files Changed

Filename Overview
Sources/SuperwallKit/Paywall/Presentation/CustomCallback.swift New public API types for custom callback support - clean implementation with good documentation
Sources/SuperwallKit/Paywall/Presentation/CustomCallbackRegistry.swift Thread-safe registry implementation using NSLock for handler storage
Sources/SuperwallKit/Paywall/View Controller/Web View/Message Handling/PaywallMessageHandler.swift Implements callback handling with proper error handling and result communication back to webview

Sequence Diagram

sequenceDiagram
    participant App as iOS App
    participant Handler as PaywallPresentationHandler
    participant Registry as CustomCallbackRegistry
    participant Superwall as Superwall SDK
    participant WebView as Paywall WebView
    participant MessageHandler as PaywallMessageHandler

    App->>Handler: onCustomCallback(handler)
    Note over Handler: Stores handler reference
    
    App->>Superwall: register(placement, handler: Handler)
    Superwall->>WebView: Present Paywall
    WebView-->>Superwall: onPresented(paywallInfo)
    Superwall->>Registry: register(paywallIdentifier, handler)
    Note over Registry: Maps identifier -> handler
    
    WebView->>MessageHandler: requestCallback(name, behavior, variables)
    MessageHandler->>Registry: getHandler(paywallIdentifier)
    Registry-->>MessageHandler: Return handler closure
    
    alt Handler exists
        MessageHandler->>Handler: await handler(CustomCallback)
        Handler-->>App: Execute callback logic
        App-->>Handler: Return CustomCallbackResult
        Handler-->>MessageHandler: Result (success/failure + data)
        MessageHandler->>WebView: sendCallbackResult(status, data)
    else No handler
        MessageHandler->>WebView: sendCallbackResult(failure)
    end
    
    WebView-->>Superwall: onDismissed(paywallInfo)
    Superwall->>Registry: unregister(paywallIdentifier)
    Note over Registry: Remove handler mapping
Loading

Context used:

  • Context from dashboard - CLAUDE.md (source)

@ianrumac ianrumac requested a review from yusuftor January 22, 2026 11:31
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.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +117 to +126

/// Sets the handler that will be called when the paywall requests a custom callback.
///
/// Use this to handle custom callbacks from your paywall. The callback receives a ``CustomCallback``
/// object containing the callback name and any variables, and should return a ``CustomCallbackResult``
/// indicating success or failure.
///
/// - Parameter handler: An async block that accepts a ``CustomCallback`` and returns a ``CustomCallbackResult``.
public func onCustomCallback(_ handler: @escaping (CustomCallback) async -> CustomCallbackResult) {
self.onCustomCallbackHandler = handler
Copy link

Choose a reason for hiding this comment

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

CHANGELOG.md needs updating for this new API addition per the style guide (CLAUDE.md:97-98)

The guide states: "Update CHANGELOG.md for customer-facing changes: Include new API additions... Focus on what the change does for developers."

Suggest adding an entry like:

### Enhancements
- Added `onCustomCallback()` method to `PaywallPresentationHandler` for handling custom callback requests from paywalls

Context Used: Context from dashboard - CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: Sources/SuperwallKit/Paywall/Presentation/PaywallPresentationHandler.swift
Line: 117:126

Comment:
CHANGELOG.md needs updating for this new API addition per the style guide (`CLAUDE.md:97-98`)

The guide states: "Update CHANGELOG.md for customer-facing changes: Include new API additions... Focus on what the change does for developers."

Suggest adding an entry like:
```markdown
### Enhancements
- Added `onCustomCallback()` method to `PaywallPresentationHandler` for handling custom callback requests from paywalls
```

**Context Used:** Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=ae0afcf7-0b55-482b-9764-29f361e46714))

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

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