Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup
- Fixes a rare issue where a user's subscription could remain active after a refund, preventing paywalls from being shown.
- Fixes trial eligibility for Stripe products.
- Fixes an issue where `transaction_complete` could be missing transaction information when a crossgrade occurred while using a purchase controller.
- Fixes terminated webviews refreshing in a loop on low RAM devices.

## 4.14.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import WebKit

protocol SWWebViewDelegate: AnyObject {
var info: PaywallInfo { get }
var isActive: Bool { get }
func webViewDidFail()
}

Expand Down Expand Up @@ -44,14 +45,8 @@ class SWWebView: WKWebView {
private let isOnDeviceCacheEnabled: Bool
private var completion: ((Error?) -> Void)?
private let enableIframeNavigation: Bool

/// Tracks the number of times the WebView process has terminated and been reloaded.
/// Used to prevent infinite reload loops on memory-constrained devices.
private var processTerminationRetryCount = 0

/// Maximum number of automatic reloads after process termination.
/// After this limit, the WebView will be reloaded when presented instead.
private let maxProcessTerminationRetries = 1
private var activeProcessTerminationRetryCount = 0
private let maxActiveProcessTerminationRetries = 3

init(
isMac: Bool,
Expand Down Expand Up @@ -153,6 +148,7 @@ class SWWebView: WKWebView {
}

func loadURL(from paywall: Paywall) async {
activeProcessTerminationRetryCount = 0
let didLoad = await loadingHandler.loadURL(
paywallUrlConfig: paywall.urlConfig,
paywallUrl: paywall.url
Expand Down Expand Up @@ -242,8 +238,7 @@ extension SWWebView: WKNavigationDelegate {
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Reset retry count on successful load
processTerminationRetryCount = 0
activeProcessTerminationRetryCount = 0
completion?(nil)
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

Expand All @@ -264,15 +259,14 @@ extension SWWebView: WKNavigationDelegate {
}

func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
// Only reload if we haven't exceeded the retry limit.
// This prevents infinite reload loops on memory-constrained devices
// where iOS keeps terminating the WebView process.
if processTerminationRetryCount < maxProcessTerminationRetries {
processTerminationRetryCount += 1
if delegate?.isActive == true,
activeProcessTerminationRetryCount < maxActiveProcessTerminationRetries {
activeProcessTerminationRetryCount += 1
webView.reload()
} else if delegate?.isActive == true {
loadingHandler.didFailToLoad = true
delegate?.webViewDidFail()
} else {
// Mark as failed so the WebView will be reloaded when presented again
// via PaywallViewController.viewWillAppear checking didFailToLoad.
loadingHandler.didFailToLoad = true
}
Comment thread
yusuftor marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.

Expand Down