Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
0e048c0
Align iOS pricing calculations with editor behavior
yusuftor Feb 25, 2026
7fe2f37
Fix weekly/daily/monthly price conversions to annualize first
yusuftor Feb 25, 2026
27e37cf
Make sure original price is matched
yusuftor Feb 25, 2026
396f8bb
Merge branch 'fix_editor_pricing_parity' of https://github.com/superw…
yusuftor Feb 25, 2026
4d61ad8
Align weekly yearly conversion with editor
yusuftor Feb 25, 2026
847c746
Merge pull request #441 from superwall/fix_editor_pricing_parity
yusuftor Feb 26, 2026
77138ea
Fix sandbox detection for Stripe and non-StoreKit purchase flows
Feb 26, 2026
cb20c1b
Update changes
yusuftor Feb 26, 2026
6b68830
Merge pull request #442 from superwall/fix/improve-sandbox-detection
yusuftor Feb 26, 2026
522d713
Fix restore alert, add localisations
yusuftor Feb 27, 2026
085a246
Localise purchase error, pending approval, and done alerts
yusuftor Feb 27, 2026
e79022d
Adds to changelog, updates version
yusuftor Feb 27, 2026
76d509f
Merge pull request #443 from superwall/fix/restore-alert
yusuftor Feb 27, 2026
b0edcae
Fix: revoked subscriptions incorrectly reported as active
Mar 2, 2026
b9d3197
Use isActive instead of re-deriving from state in purchase correction
Mar 2, 2026
5650528
Clarify inline comment: distinguish revoked vs expired override
Mar 2, 2026
50ff6c5
Update Package.resolved
yusuftor Mar 2, 2026
10063a1
Fix comment: expired override is not redundant
Mar 2, 2026
ea4e20a
Merge pull request #445 from superwall/fix/revoked-subscription-entit…
yusuftor Mar 2, 2026
47e4ec8
Update CHANGELOG.md
yusuftor Mar 2, 2026
4c5849b
Fix trial eligibility for Stripe products
yusuftor Mar 3, 2026
7ea8f3d
Fix Stripe trial eligibility and refresh on cached paywalls
yusuftor Mar 5, 2026
c8e68dd
Fix test
yusuftor Mar 5, 2026
0ed5361
Update AddPaywallProducts.swift
yusuftor Mar 5, 2026
26a6c25
Fix tests
yusuftor Mar 5, 2026
552a802
Update AddPaywallProducts.swift
yusuftor Mar 5, 2026
bb374f9
Update StripeTrialEligibilityTests.swift
yusuftor Mar 5, 2026
de9d30f
update based on feedback
yusuftor Mar 5, 2026
fad78db
remove unused logic
yusuftor Mar 5, 2026
7e8264b
Update AddPaywallProducts.swift
yusuftor Mar 5, 2026
c66733c
Merge pull request #446 from superwall/fix/stripe-trial-eligibility
yusuftor Mar 5, 2026
20cbd17
Add currentEntitlement fallback for transaction retrieval
yusuftor Mar 10, 2026
c0cfde1
Use currentEntitlements (plural) on iOS 18.4+ and pick most recent match
yusuftor Mar 10, 2026
abc0eb7
Add changelog entry for currentEntitlements fallback
yusuftor Mar 10, 2026
d1821e9
Update CHANGELOG.md
yusuftor Mar 10, 2026
4ddaf76
revert app delegate changes
yusuftor Mar 10, 2026
6b0040a
Update PurchasingCoordinator.swift
yusuftor Mar 10, 2026
e923d68
Merge pull request #450 from superwall/fix/current-entitlement-transa…
yusuftor Mar 10, 2026
61bebd5
Fix WebView process termination to use presentation state instead of …
yusuftor Mar 10, 2026
182d0ef
Add retry cap for active paywalls on process termination
yusuftor Mar 10, 2026
3370291
Move changelog entry to 4.14.1
yusuftor Mar 10, 2026
8809297
Remove counter reset in else branch to enforce hard retry cap
yusuftor Mar 10, 2026
145563e
Fix retry cap: remove didFinish reset, dismiss active paywalls on cap
yusuftor Mar 10, 2026
a45af39
Reset retry counter on fresh load, set didFailToLoad on active dismissal
yusuftor Mar 10, 2026
02bc8fa
Reset retry counter in didFinish to track consecutive unrecovered kills
yusuftor Mar 10, 2026
5354f4d
Update CHANGELOG.md
yusuftor Mar 10, 2026
79be182
Merge pull request #451 from superwall/fix/webview-process-terminatio…
yusuftor Mar 10, 2026
68bfaa7
Add more compiler versions, visionOS fix
yusuftor Mar 10, 2026
2e87213
Track free trial start for Stripe purchases and fix notification sche…
yusuftor Mar 10, 2026
a6950b1
Also check product.trialPeriodDays before tracking free trial start
yusuftor Mar 10, 2026
a013461
Use injected superwall instance instead of Superwall.shared
yusuftor Mar 10, 2026
be98ed2
Update Package.resolved
yusuftor Mar 10, 2026
3ea6c95
Rename outer product binding to avoid shadowing
yusuftor Mar 10, 2026
6d766c1
Use redemptionProduct.identifier for FreeTrialStart product
yusuftor Mar 10, 2026
5a2b474
Add tests for isFreeTrialAvailable + trialPeriodDays combinations
yusuftor Mar 10, 2026
5f7ff07
Update CHANGELOG.md
yusuftor Mar 10, 2026
02acd97
Add freeTrialStart delegate event assertions to tests
yusuftor Mar 10, 2026
1e3383c
Merge pull request #452 from superwall/fix/stripe-free-trial-tracking
yusuftor Mar 10, 2026
03e28e0
Remove empty test stub and deduplicate roundedPrice helper
yusuftor Mar 11, 2026
ec8aea0
Add debug warning when Stripe product has no entitlements for trial c…
yusuftor Mar 11, 2026
c314cb9
Fix LogScope to use .productsManager
yusuftor Mar 11, 2026
d7ae1b1
Update identity because 789 has a web entitlement
yusuftor Mar 11, 2026
6e50fcd
Clean up blank lines and simplify trial eligibility loop
yusuftor Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

The changelog for `SuperwallKit`. Also see the [releases](https://github.com/superwall/Superwall-iOS/releases) on GitHub.

## 4.14.1

### Enhancements

- Localizes all alerts into 41 languages.
- Makes sure to refresh free trial eligibility on every paywall open.

### Fixes

- Makes `device.isSandbox` more reliable.
- Fixes the web restore alert not showing the "Yes" action button and "Cancel" incorrectly triggering the restore action.
- 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 paywalls and tracks `freeTrial_start`.
- 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

### Enhancements
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Examples/Advanced/Advanced/WelcomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"Enter your name",
text: $name,
prompt: Text("Enter your name")
.foregroundStyle(Color.primaryTeal300)

Check warning on line 61 in Examples/Advanced/Advanced/WelcomeView.swift

View workflow job for this annotation

GitHub Actions / Example-Projects-SwiftLint

Indentation Width Violation: Code should be indented using one tab or 2 spaces (indentation_width)
)
.textContentType(.name)
.textInputAutocapitalization(.never)
Expand All @@ -70,7 +70,7 @@
@ViewBuilder
private func logInButton() -> some View {
BrandedButton(title: "Log In") {
Superwall.shared.identify(userId: "789")
Superwall.shared.identify(userId: "7891")
Superwall.shared.setUserAttributes(["firstName": name])
isLoggedIn = true
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/SuperwallKit/Misc/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ let sdkVersion = """
*/

let sdkVersion = """
4.14.0
4.14.1
"""
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,46 @@ extension Transaction {

return nil
}

/// Gets the current entitlement transaction for a given `productId`
/// whose purchase date is within an hour of the given `purchaseDate`.
/// Uses `currentEntitlements(for:)` on iOS 18.4+ (which returns multiple
/// transactions, e.g. direct + Family Sharing), falling back to the
/// deprecated `currentEntitlement(for:)` on older versions.
static func currentEntitlement(
for productId: String,
since purchaseDate: Date
) async -> VerificationResult<Transaction>? {
#if compiler(>=6.1)
if #available(iOS 18.4, visionOS 2.4, *) {
var best: VerificationResult<Transaction>?

for await result in Transaction.currentEntitlements(for: productId) {
let transaction = result.unsafePayloadValue

if !transaction.purchaseDate.isWithinAnHourBefore(purchaseDate) {
continue
}

if let currentBest = best?.unsafePayloadValue,
currentBest.purchaseDate >= transaction.purchaseDate {
continue
}

best = result
}

return best
}
#endif

let verificationResult = await Transaction.currentEntitlement(for: productId)

if let transaction = verificationResult.map({ $0.unsafePayloadValue }),
transaction.purchaseDate.isWithinAnHourBefore(purchaseDate) {
return verificationResult
}

return nil
}
}
10 changes: 10 additions & 0 deletions Sources/SuperwallKit/Models/Product/StripeProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,40 @@ public final class StripeProduct: NSObject, Codable, Sendable {
/// The product identifier.
public let id: String

/// The number of trial days for this product, if any.
public let trialDays: Int?

/// The product's store.
private let store: String

enum CodingKeys: String, CodingKey {
case bundleId
case id = "productIdentifier"
case store
case trialDays
}

init(
id: String,
trialDays: Int? = nil,
store: String = "STRIPE"
) {
self.id = id
self.trialDays = trialDays
self.store = store
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(store, forKey: .store)
try container.encodeIfPresent(trialDays, forKey: .trialDays)
}

public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
trialDays = try container.decodeIfPresent(Int.self, forKey: .trialDays)
store = try container.decode(String.self, forKey: .store)
if store != "STRIPE" {
throw DecodingError.dataCorrupted(
Expand All @@ -57,12 +65,14 @@ public final class StripeProduct: NSObject, Codable, Sendable {
return false
}
return id == other.id
&& trialDays == other.trialDays
&& store == other.store
}

public override var hash: Int {
var hasher = Hasher()
hasher.combine(id)
hasher.combine(trialDays)
hasher.combine(store)
return hasher.finalize()
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ class DeviceHelper {
return "true"
#else

// Prefer AppTransaction.environment (iOS 16+) when available,
// as it reliably detects sandbox for all purchase types including
// Stripe and non-StoreKit flows.
if let isSandbox = ReceiptManager.isSandboxEnvironment {
return "\(isSandbox)"
}

guard let url = Bundle.main.appStoreReceiptURL else {
return "false"
}
Expand Down
10 changes: 8 additions & 2 deletions Sources/SuperwallKit/Network/Device Helper/SwiftVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,16 @@ extension DeviceHelper {
}

func currentCompilerVersion() -> String {
#if compiler(>=6.2)
#if compiler(>=6.3)
return "6.3"
#elseif compiler(>=6.2.4)
return "6.2.4"
#elseif compiler(>=6.2.3)
return "6.2.3"
#elseif compiler(>=6.2)
return "6.2"
#elseif compiler(>=6.1)
return "6.1+"
return "6.1"
#elseif compiler(>=6.0)
return "6.0"
#elseif compiler(>=5.10)
Expand Down
Loading
Loading