Skip to content

Commit

Permalink
Merge pull request #187 from superwall-me/internal-purchase-controlle…
Browse files Browse the repository at this point in the history
…r-only

Internal purchase controller (only) refactor
  • Loading branch information
yusuftor committed Dec 6, 2023
2 parents f1c17ba + e8aa3d4 commit 0942f49
Show file tree
Hide file tree
Showing 23 changed files with 256 additions and 277 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ extension TriggerSession.Transaction {
type: introductoryPrice.type
)

self.introductoryRedeemable = await Superwall.shared.dependencyContainer.storeKitManager.isFreeTrialAvailable(for: product)
self.introductoryRedeemable = await Superwall.shared.dependencyContainer.receiptManager.isFreeTrialAvailable(for: product)
self.hasIntroductoryOffer = true
} else {
self.hasIntroductoryOffer = false
Expand Down
6 changes: 3 additions & 3 deletions Sources/SuperwallKit/Config/ConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ class ConfigManager {
/// A task that is non-`nil` when preloading all paywalls.
private var currentPreloadingTask: Task<Void, Never>?

private let factory: RequestFactory & RuleAttributesFactory
private let factory: RequestFactory & RuleAttributesFactory & ReceiptFactory

init(
options: SuperwallOptions?,
storeKitManager: StoreKitManager,
storage: Storage,
network: Network,
paywallManager: PaywallManager,
factory: RequestFactory & RuleAttributesFactory
factory: RequestFactory & RuleAttributesFactory & ReceiptFactory
) {
if let options = options {
self.options = options
Expand All @@ -68,7 +68,7 @@ class ConfigManager {

func fetchConfiguration() async {
do {
await storeKitManager.loadPurchasedProducts()
_ = await factory.loadPurchasedProducts()

let config = try await network.getConfig { [weak self] in
self?.configState.send(.retrying)
Expand Down
66 changes: 44 additions & 22 deletions Sources/SuperwallKit/Dependencies/DependencyContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,23 @@ final class DependencyContainer {
var api: Api!
var transactionManager: TransactionManager!
var delegateAdapter: SuperwallDelegateAdapter!
var productPurchaser: ProductPurchaserSK1!
var receiptManager: ReceiptManager!
var purchaseController: PurchaseController!
// swiftlint:enable implicitly_unwrapped_optional
let productsFetcher = ProductsFetcherSK1()

init(
swiftPurchaseController: PurchaseController? = nil,
objcPurchaseController: PurchaseControllerObjc? = nil,
purchaseController controller: PurchaseController? = nil,
options: SuperwallOptions? = nil
) {
let purchaseController = InternalPurchaseController(
factory: self,
swiftPurchaseController: swiftPurchaseController,
objcPurchaseController: objcPurchaseController
purchaseController = controller ?? AutomaticPurchaseController(factory: self)
receiptManager = ReceiptManager(
delegate: productsFetcher,
receiptDelegate: purchaseController as? ReceiptDelegate
)
storeKitManager = StoreKitManager(purchaseController: purchaseController)

storeKitManager = StoreKitManager(productsFetcher: productsFetcher)
delegateAdapter = SuperwallDelegateAdapter()
storage = Storage(factory: self)
network = Network(factory: self)
Expand Down Expand Up @@ -117,12 +121,18 @@ final class DependencyContainer {

transactionManager = TransactionManager(
storeKitManager: storeKitManager,
receiptManager: receiptManager,
purchaseController: purchaseController,
sessionEventsManager: sessionEventsManager,
factory: self
)

// Initialise the product purchaser so that it can immediately start listening to transactions.
_ = storeKitManager.purchaseController.productPurchaser
productPurchaser = ProductPurchaserSK1(
storeKitManager: storeKitManager,
receiptManager: receiptManager,
sessionEventsManager: sessionEventsManager,
factory: self
)
}
}

Expand Down Expand Up @@ -423,17 +433,6 @@ extension DependencyContainer: StoreTransactionFactory {
}
}

// MARK: - Product Purchaser Factory
extension DependencyContainer: ProductPurchaserFactory {
func makeSK1ProductPurchaser() -> ProductPurchaserSK1 {
return ProductPurchaserSK1(
storeKitManager: storeKitManager,
sessionEventsManager: sessionEventsManager,
factory: self
)
}
}

// MARK: - Options Factory
extension DependencyContainer: OptionsFactory {
func makeSuperwallOptions() -> SuperwallOptions {
Expand All @@ -451,7 +450,7 @@ extension DependencyContainer: TriggerFactory {
// MARK: - Purchase Controller Factory
extension DependencyContainer: HasExternalPurchaseControllerFactory {
func makeHasExternalPurchaseController() -> Bool {
return storeKitManager.purchaseController.hasExternalPurchaseController
return purchaseController.isInternal == false
}
}

Expand All @@ -472,7 +471,15 @@ extension DependencyContainer: ComputedPropertyRequestsFactory {
// MARK: - Purchased Transactions Factory
extension DependencyContainer: PurchasedTransactionsFactory {
func makePurchasingCoordinator() -> PurchasingCoordinator {
return storeKitManager.purchaseController.productPurchaser.coordinator
return productPurchaser.coordinator
}

func purchase(product: SKProduct) async -> PurchaseResult {
return await productPurchaser.purchase(product: product)
}

func restorePurchases() async -> RestorationResult {
return await productPurchaser.restorePurchases()
}
}

Expand All @@ -485,3 +492,18 @@ extension DependencyContainer: UserAttributesEventFactory {
)
}
}

// MARK: - Receipt factory
extension DependencyContainer: ReceiptFactory {
func loadPurchasedProducts() async -> Set<StoreProduct>? {
return await receiptManager.loadPurchasedProducts()
}

func refreshReceipt() async {
return await receiptManager.refreshReceipt()
}

func isFreeTrialAvailable(for product: StoreProduct) async -> Bool {
return await receiptManager.isFreeTrialAvailable(for: product)
}
}
12 changes: 8 additions & 4 deletions Sources/SuperwallKit/Dependencies/FactoryProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,6 @@ protocol ApiFactory: AnyObject {
) async -> [String: String]
}

protocol ProductPurchaserFactory: AnyObject {
func makeSK1ProductPurchaser() -> ProductPurchaserSK1
}

protocol StoreTransactionFactory: AnyObject {
func makeStoreTransaction(from transaction: SK1Transaction) async -> StoreTransaction

Expand All @@ -137,8 +133,16 @@ protocol TriggerFactory: AnyObject {

protocol PurchasedTransactionsFactory {
func makePurchasingCoordinator() -> PurchasingCoordinator
func purchase(product: SKProduct) async -> PurchaseResult
func restorePurchases() async -> RestorationResult
}

protocol UserAttributesEventFactory {
func makeUserAttributesEvent() -> InternalSuperwallEvent.Attributes
}

protocol ReceiptFactory {
func loadPurchasedProducts() async -> Set<StoreProduct>?
func refreshReceipt() async
func isFreeTrialAvailable(for product: StoreProduct) async -> Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// Created by Yusuf Tör on 12/05/2023.
//
// swiftlint:disable trailing_closure

import Foundation

Expand Down Expand Up @@ -47,7 +48,10 @@ extension PaywallRequestManager {
fromProducts: result.products,
productsById: result.productsById,
isFreeTrialAvailableOverride: request.overrides.isFreeTrial,
isFreeTrialAvailable: storeKitManager.isFreeTrialAvailable(for:)
isFreeTrialAvailable: { [weak self] product in
guard let self = self else { return false }
return await self.factory.isFreeTrialAvailable(for: product)
}
)
paywall.swProducts = outcome.orderedSwProducts
paywall.productVariables = outcome.productVariables
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ actor PaywallRequestManager {
typealias Factory = DeviceHelperFactory
& TriggerSessionManagerFactory
& ConfigManagerFactory
& ReceiptFactory

init(
storeKitManager: StoreKitManager,
Expand Down
112 changes: 0 additions & 112 deletions Sources/SuperwallKit/StoreKit/InternalPurchaseController.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,26 @@
import Foundation
import StoreKit

protocol ReceiptDelegate: AnyObject {
func receiptLoaded(purchases: Set<InAppPurchase>) async
}

actor ReceiptManager: NSObject {
var purchasedSubscriptionGroupIds: Set<String>?
private var purchases: Set<InAppPurchase> = []
private var receiptRefreshCompletion: ((Bool) -> Void)?
private weak var delegate: ProductsFetcherSK1?
private let receiptData: () -> Data?
private let purchaseController: InternalPurchaseController
private weak var delegate: ProductsFetcherSK1?
private weak var receiptDelegate: ReceiptDelegate?

init(
delegate: ProductsFetcherSK1,
purchaseController: InternalPurchaseController,
delegate: ProductsFetcherSK1?,
receiptDelegate: ReceiptDelegate?,
receiptData: @escaping () -> Data? = ReceiptLogic.getReceiptData
) {
self.delegate = delegate
self.receiptDelegate = receiptDelegate
self.receiptData = receiptData
self.purchaseController = purchaseController
}

/// Loads purchased products from the receipt, storing the purchased subscription group identifiers,
Expand All @@ -34,14 +38,14 @@ actor ReceiptManager: NSObject {
let payload = ReceiptLogic.getPayload(using: receiptData),
let delegate = delegate
else {
await purchaseController.syncSubscriptionStatus(withPurchases: [])
await receiptDelegate?.receiptLoaded(purchases: [])
return nil
}

let purchases = payload.purchases
self.purchases = purchases

await purchaseController.syncSubscriptionStatus(withPurchases: purchases)
await receiptDelegate?.receiptLoaded(purchases: purchases)

let purchasedProductIds = Set(purchases.map { $0.productIdentifier })

Expand Down Expand Up @@ -83,7 +87,10 @@ actor ReceiptManager: NSObject {
return !purchasedSubsGroupIds.contains(subsGroupId)
}

/// Refreshes the receipt.
/// This refreshes the device receipt.
///
/// - Warning: This will prompt the user to log in, so only do this on
/// when restoring or after purchasing.
func refreshReceipt() async {
Logger.debug(
logLevel: .debug,
Expand Down

0 comments on commit 0942f49

Please sign in to comment.