From 101e2db20cb83591ed1cee4e016a529402a83702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20To=CC=88r?= <3296904+yusuftor@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:55:13 +0100 Subject: [PATCH 1/3] Fire paywall_decline when swiping down modal paywalls Set presentationController delegate for modal/drawer styles so presentationControllerDidDismiss fires on swipe-down, triggering the paywall_decline event even when no survey is present. Co-Authored-By: Claude Opus 4.5 --- .../PaywallViewController.swift | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift b/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift index 9201dbafc..d78aa058b 100644 --- a/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift +++ b/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift @@ -1041,6 +1041,15 @@ extension PaywallViewController: UIAdaptivePresentationControllerDelegate { ) } + public func presentationControllerDidDismiss( + _ presentationController: UIPresentationController + ) { + dismiss( + result: .declined, + closeReason: .manualClose + ) + } + /// Marks that a redeem succeeded while the checkout web view is open. /// Cancels any pending transaction abandon tracking. func markRedeemInitiated() { @@ -1294,10 +1303,19 @@ extension PaywallViewController { // Fetch intro offer eligibility tokens for SK2 purchases on iOS 18.2+ fetchIntroOfferTokens() - if willShowSurvey { - didDisableSwipeForSurvey = true + switch presentationStyle { + case .modal, .drawer: presentationController?.delegate = self - isModalInPresentation = true + if willShowSurvey { + didDisableSwipeForSurvey = true + isModalInPresentation = true + } + default: + if willShowSurvey { + didDisableSwipeForSurvey = true + presentationController?.delegate = self + isModalInPresentation = true + } } addShimmerView(onPresent: true) From f67617843bc56e8db2bb194db466ccf6010b802f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20To=CC=88r?= <3296904+yusuftor@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:51:16 +0100 Subject: [PATCH 2/3] Guard against double-dismiss in presentationControllerDidDismiss Prevents paywall_decline from firing twice when didAttemptToDismiss (survey path) already triggered dismiss before didDismiss is called. Co-Authored-By: Claude Opus 4.5 --- .../Paywall/View Controller/PaywallViewController.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift b/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift index d78aa058b..a5b85d2b5 100644 --- a/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift +++ b/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift @@ -1044,6 +1044,11 @@ extension PaywallViewController: UIAdaptivePresentationControllerDelegate { public func presentationControllerDidDismiss( _ presentationController: UIPresentationController ) { + // Guard against double-dismiss: if didAttemptToDismiss already + // triggered our dismiss(), closeReason will already be set. + guard paywall.closeReason == .none else { + return + } dismiss( result: .declined, closeReason: .manualClose From 95901e7e40af0765c385075ba33b8dd356ca729a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20To=CC=88r?= <3296904+yusuftor@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:34:15 +0100 Subject: [PATCH 3/3] Update PaywallViewController.swift --- .../Paywall/View Controller/PaywallViewController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift b/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift index a5b85d2b5..f53e221f0 100644 --- a/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift +++ b/Sources/SuperwallKit/Paywall/View Controller/PaywallViewController.swift @@ -1310,8 +1310,9 @@ extension PaywallViewController { switch presentationStyle { case .modal, .drawer: + let shouldShowSurvey = willShowSurvey presentationController?.delegate = self - if willShowSurvey { + if shouldShowSurvey { didDisableSwipeForSurvey = true isModalInPresentation = true }