Skip to content

Commit

Permalink
Merge pull request #288 from superwall/v4-develop
Browse files Browse the repository at this point in the history
4.0.0-beta.5
  • Loading branch information
yusuftor authored Jan 30, 2025
2 parents 265a982 + 6a3da5e commit b0b0e4e
Show file tree
Hide file tree
Showing 43 changed files with 804 additions and 618 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

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

## 4.0.0-beta.5

### Breaking Changes

- Replaces `entitlements.status` with `subscriptionStatus`.

### Enhancements

- Adds `Superwall.shared.subscriptionStatus.isActive` as a convenience variable.

## 4.0.0-beta.4

### Fixes
Expand Down Expand Up @@ -91,6 +101,18 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup
- Adds StoreKit 2 observer mode. This can be enabled by setting the `SuperwallOptions` `shouldObservePurchases` to `true` and `storeKitVersion` to `.storeKit2` (which is the default value). Note that this is only available with apps running iOS 17.2+.
- Adds `products(for:)` which gets the ``StoreProduct`s for given product identifiers.

## 3.12.4

### Fixes

- Simplifies and corrects logic for choosing paywall variants.

## 3.12.3

### Fixes

- Fixes an issue where trying to purchase a product that was previously purchased may prevent the spinner from disappearing on the paywall.

## 3.12.2

### Fixes
Expand Down
8 changes: 4 additions & 4 deletions Examples/Advanced/Advanced.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
888F48D327DBA662009C74A3 /* Styles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888F48D227DBA662009C74A3 /* Styles.swift */; };
888F48D727DBA6B6009C74A3 /* Rubik-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 888F48D627DBA6B6009C74A3 /* Rubik-Regular.ttf */; };
888F48DB27DBA8A9009C74A3 /* Rubik-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 888F48DA27DBA8A9009C74A3 /* Rubik-Bold.ttf */; };
88A801D028EAE717004244CA /* SuperwallEntitlementsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A801CF28EAE717004244CA /* SuperwallEntitlementsView.swift */; };
88A801D028EAE717004244CA /* SuperwallSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A801CF28EAE717004244CA /* SuperwallSubscriptionView.swift */; };
88EE539627DF6D0A00F1FFFB /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88EE539527DF6D0A00F1FFFB /* WelcomeView.swift */; };
88F9F1E327E21718004FCE83 /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F9F1E227E21718004FCE83 /* InfoView.swift */; };
/* End PBXBuildFile section */
Expand All @@ -44,7 +44,7 @@
888F48D227DBA662009C74A3 /* Styles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styles.swift; sourceTree = "<group>"; };
888F48D627DBA6B6009C74A3 /* Rubik-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Rubik-Regular.ttf"; sourceTree = "<group>"; };
888F48DA27DBA8A9009C74A3 /* Rubik-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Rubik-Bold.ttf"; sourceTree = "<group>"; };
88A801CF28EAE717004244CA /* SuperwallEntitlementsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuperwallEntitlementsView.swift; sourceTree = "<group>"; };
88A801CF28EAE717004244CA /* SuperwallSubscriptionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuperwallSubscriptionView.swift; sourceTree = "<group>"; };
88EE539127DF64BB00F1FFFB /* Superwall_Advanced-Products.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Superwall_Advanced-Products.storekit"; sourceTree = "<group>"; };
88EE539527DF6D0A00F1FFFB /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
88F9F1E227E21718004FCE83 /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -128,7 +128,7 @@
children = (
888F48CA27DBA55E009C74A3 /* BrandedButton.swift */,
88F9F1E227E21718004FCE83 /* InfoView.swift */,
88A801CF28EAE717004244CA /* SuperwallEntitlementsView.swift */,
88A801CF28EAE717004244CA /* SuperwallSubscriptionView.swift */,
);
path = Components;
sourceTree = "<group>";
Expand Down Expand Up @@ -259,7 +259,7 @@
888F48D327DBA662009C74A3 /* Styles.swift in Sources */,
887111582D0B027E00D737A4 /* Colors.swift in Sources */,
888F48CB27DBA55E009C74A3 /* BrandedButton.swift in Sources */,
88A801D028EAE717004244CA /* SuperwallEntitlementsView.swift in Sources */,
88A801D028EAE717004244CA /* SuperwallSubscriptionView.swift in Sources */,
888F48C327DB8584009C74A3 /* HomeView.swift in Sources */,
88EE539627DF6D0A00F1FFFB /* WelcomeView.swift in Sources */,
888F48D127DBA62D009C74A3 /* Font.swift in Sources */,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// SuperwallSubscriptionView.swift
// SuperwallSwiftUIExample
//
// Created by Yusuf Tör on 15/03/2022.
//

import SwiftUI
import SuperwallKit

struct SuperwallSubscriptionView: View {
@StateObject private var superwall = Superwall.shared // ensures subscriptionStatus is auto updating
var text: String {
// These are published properties that auto-update
switch superwall.subscriptionStatus {
case .unknown:
return "Loading subscription status."
case .inactive:
return "You do not have an active subscription so a paywall will always show."
case .active(let entitlements):
return "You currently have an active subscription with the \(entitlements.map { $0.id }.joined(separator: " and ")) entitlement. For the purposes of this app, delete and reinstall the app to clear your transactions."
}
}

var body: some View {
Text(text)
.multilineTextAlignment(.center)
.lineSpacing(5)
.padding()
.padding(.bottom, 20)
}
}

#Preview {
SuperwallSubscriptionView()
}
2 changes: 1 addition & 1 deletion Examples/Advanced/Advanced/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct HomeView: View {
.background(Color.primaryTeal300)
.padding()

SuperwallEntitlementsView()
SuperwallSubscriptionView()

VStack(spacing: 20) {
BrandedButton(title: "Launch Non-Gated Feature") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// swiftlint:disable all
//
// RCPurchaseController.swift
// PurchaseController + Entitlements
// Advanced
//
// Created by Yusuf Tor on 13/12/24.
//
Expand Down Expand Up @@ -31,26 +31,23 @@ enum PurchasingError: LocalizedError {
/// `Superwall.configure(apiKey: "superwall_api_key", purchaseController: purchaseController)`
/// 4. Second, configure RevenueCat.
/// `Purchases.configure(withAPIKey: "revenuecat_api_key")`
/// 5. Third, Keep Superwall's entitlements up-to-date with RevenueCat's.
/// `purchaseController.syncEntitlements()`
/// 5. Third, Keep Superwall's subscription status up-to-date with RevenueCat's.
/// `purchaseController.syncSubscriptionStatus()`
final class RCPurchaseController: PurchaseController {
// MARK: Sync Entitlements
// MARK: Sync Subscription Status
/// Makes sure that Superwall knows the customer's entitlements by
/// changing `Superwall.shared.entitlements`
func syncEntitlements() {
func syncSubscriptionStatus() {
assert(Purchases.isConfigured, "You must configure RevenueCat before calling this method.")
Task {

for await customerInfo in Purchases.shared.customerInfoStream {
// Gets called whenever new CustomerInfo is available
let superwallEntitlements = customerInfo.entitlements.activeInCurrentEnvironment.keys.map {
Entitlement(id: $0)
}
await MainActor.run { [superwallEntitlements] in
if superwallEntitlements.isEmpty {
Superwall.shared.entitlements.status = .inactive
} else {
Superwall.shared.entitlements.status = .active(Set(superwallEntitlements))
}
Superwall.shared.subscriptionStatus = .active(Set(superwallEntitlements))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// PurchaseController.swift
// PurchaseController + Entitlements
// SWPurchaseController.swift
// Advanced
//
// Created by Yusuf Tör on 13/12/2024.
//
Expand All @@ -14,13 +14,13 @@ import SuperwallKit
/// `let purchaseController = SWPurchaseController()`
/// 3. Configure Superwall. Pass in the `purchaseController` you just created.
/// `Superwall.configure(apiKey: "superwall_api_key", purchaseController: purchaseController)`
/// 5. Then, keep Superwall's entitlements up-to-date with StoreKit's entitlements.
/// `purchaseController.syncEntitlements()`
/// 5. Then, keep Superwall's subscription status up-to-date with StoreKit's entitlements.
/// `purchaseController.syncSubscriptionStatus()`
final class SWPurchaseController: PurchaseController {
// MARK: Sync Entitlements
/// Makes sure that Superwall knows the customer's entitlements by
/// changing `Superwall.shared.entitlements.status`
func syncEntitlements() async {
// MARK: Sync Subscription Status
/// Makes sure that Superwall knows the customer's subscription status by
/// changing `Superwall.shared.subscriptionStatus`
func syncSubscriptionStatus() async {
var products: Set<String> = []
for await verificationResult in Transaction.currentEntitlements {
switch verificationResult {
Expand All @@ -35,25 +35,25 @@ final class SWPurchaseController: PurchaseController {
let entitlements = Set(storeProducts.flatMap { $0.entitlements })

await MainActor.run {
Superwall.shared.entitlements.status = .active(entitlements)
Superwall.shared.subscriptionStatus = .active(entitlements)
}
}

// MARK: Handle Purchases
/// Makes a purchase with Superwall and returns its result after syncing entitlements. This gets called when
/// Makes a purchase with Superwall and returns its result after syncing subscription status. This gets called when
/// someone tries to purchase a product on one of your paywalls.
func purchase(product: StoreProduct) async -> PurchaseResult {
let result = await Superwall.shared.purchase(product)
await syncEntitlements()
await syncSubscriptionStatus()
return result
}

// MARK: Handle Restores
/// Makes a restore with Superwall and returns its result after syncing entitlements.
/// Makes a restore with Superwall and returns its result after syncing subscription status.
/// This gets called when someone tries to restore purchases on one of your paywalls.
func restorePurchases() async -> RestorationResult {
let result = await Superwall.shared.restorePurchases()
await syncEntitlements()
await syncSubscriptionStatus()
return result
}
}
12 changes: 6 additions & 6 deletions Examples/Advanced/Advanced/SuperwallAdvancedApp.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// PurchaseController+EntitlementsApp.swift
// SuperwallAdvancedApp.swift
// SuperwallSwiftUIExample
//
// Created by Yusuf Tör on 10/03/2022.
Expand Down Expand Up @@ -33,9 +33,9 @@ struct SuperwallAdvancedApp: App {
purchaseController: purchaseController
)

// Step 3 - Sync Entitlements
// Step 3 - Sync Subscription Status
Task {
await purchaseController.syncEntitlements()
await purchaseController.syncSubscriptionStatus()
}
*/

Expand All @@ -59,9 +59,9 @@ struct SuperwallAdvancedApp: App {
.build()
)

// Step 4 – Sync Entitlements
/// Keep Superwall's entitlements up-to-date with RevenueCat's.
purchaseController.syncEntitlements()
// Step 4 – Sync Subscription Status
/// Keep Superwall's subscription status up-to-date with RevenueCat's.
purchaseController.syncSubscriptionStatus()
*/

isPreviouslyLoggedIn.send(Superwall.shared.isLoggedIn)
Expand Down
10 changes: 5 additions & 5 deletions Examples/Basic/Basic/Components/SuperwallSubscriptionView.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// SuperwallEntitlementsView.swift
// SuperwallSubscriptionView.swift
// SuperwallSwiftUIExample
//
// Created by Yusuf Tör on 15/03/2022.
Expand All @@ -9,14 +9,14 @@ import SwiftUI
import SuperwallKit

struct SuperwallSubscriptionView: View {
@StateObject private var entitlements = Superwall.shared.entitlements // ensures entitlements is auto updating
@StateObject private var superwall = Superwall.shared // ensures subscriptionStatus is auto updating
var text: String {
// These are published properties that auto-update
switch entitlements.status {
switch superwall.subscriptionStatus {
case .unknown:
return "Loading..."
return "Loading subscription status."
case .inactive:
return "You are not subscribed so a paywall will always show."
return "You do not have an active subscription so a paywall will always show."
case .active:
return "You are subscribed so a paywall will not show. For the purposes of this app, delete and reinstall the app to clear your subscriptions."
}
Expand Down
2 changes: 1 addition & 1 deletion Examples/Basic/Basic/SuperwallBasicApp.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// PurchaseController+EntitlementsApp.swift
// SuperwallBasicApp.swift
// SuperwallSwiftUIExample
//
// Created by Yusuf Tör on 10/03/2022.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,9 @@ enum InternalSuperwallPlacement {
}
}

struct EntitlementStatusDidChange: TrackableSuperwallPlacement {
let superwallPlacement: SuperwallPlacement = .entitlementStatusDidChange
let status: EntitlementStatus
struct SubscriptionStatusDidChange: TrackableSuperwallPlacement {
let superwallPlacement: SuperwallPlacement = .subscriptionStatusDidChange
let status: SubscriptionStatus
var audienceFilterParams: [String: Any] = [:]
func getSuperwallParameters() async -> [String: Any] {
var params: [String: Any] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ extension Superwall {
)

do {
try await waitForEntitlementsAndConfig(request, paywallStatePublisher: nil)
try await waitForSubsStatusAndConfig(request, paywallStatePublisher: nil)
} catch {
return logErrors(from: request, error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public enum SuperwallPlacement {
case deviceAttributes(attributes: [String: Any])

/// When the entitlement status did change.
case entitlementStatusDidChange
case subscriptionStatusDidChange

/// Anytime the app leaves the foreground.
case appClose
Expand Down Expand Up @@ -257,8 +257,8 @@ extension SuperwallPlacement {
return .init(objcPlacement: .sessionStart)
case .deviceAttributes:
return .init(objcPlacement: .deviceAttributes)
case .entitlementStatusDidChange:
return .init(objcPlacement: .entitlementStatusDidChange)
case .subscriptionStatusDidChange:
return .init(objcPlacement: .subscriptionStatusDidChange)
case .appClose:
return .init(objcPlacement: .appClose)
case .deepLink:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public enum SuperwallPlacementObjc: Int, CaseIterable {
case subscriptionStart

/// When the entitlement status did change.
case entitlementStatusDidChange
case subscriptionStatusDidChange

/// When the user successfully completes a transaction for a subscription product with an introductory offer.
case freeTrialStart
Expand Down Expand Up @@ -207,8 +207,8 @@ public enum SuperwallPlacementObjc: Int, CaseIterable {
return "session_start"
case .deviceAttributes:
return "device_attributes"
case .entitlementStatusDidChange:
return "entitlementStatus_didChange"
case .subscriptionStatusDidChange:
return "subscriptionStatus_didChange"
case .appClose:
return "app_close"
case .deepLink:
Expand Down
Loading

0 comments on commit b0b0e4e

Please sign in to comment.