Skip to content

Commit

Permalink
Merge pull request #166 from superwall-me/feature/add-internal-purcha…
Browse files Browse the repository at this point in the history
…se-controller

Feature/add internal purchase controller
  • Loading branch information
yusuftor committed Aug 31, 2023
2 parents c81e977 + 244aca0 commit 8119196
Show file tree
Hide file tree
Showing 39 changed files with 599 additions and 913 deletions.
4 changes: 2 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
## Changes in this pull request

Issue fixed: #
-

### Checklist

- [ ] All unit and UI tests pass. Demo project builds and runs.
- [ ] I added tests, an experiment, or detailed why my change isn't tested.
- [ ] I added/updated tests or detailed why my change isn't tested.
- [ ] I added an entry to the `CHANGELOG.md` for any breaking changes, enhancements, or bug fixes.
- [ ] I have run `swiftlint` in the main directory and fixed any issues.
- [ ] I have updated the SDK documentation as well as the online docs.
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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

## 3.3.3

### Fixes

- Fixes issue where verification was happening after the finishing of transactions when not using a `PurchaseController`.

## 3.3.2

### Fixes
Expand All @@ -27,7 +33,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup
### Enhancements

- Adds the ability to add a paywall exit survey. Surveys are configured via the dashboard and added to paywalls. When added to a paywall, it will attempt to display when the user taps the close button. If the paywall has the `modalPresentationStyle` of `pageSheet`, `formSheet`, or `popover`, the survey will also attempt to display when the user tries to drag to dismiss the paywall. The probability of the survey showing is determined by the survey's configuration in the dashboard. A user will only ever see the survey once unless you reset responses via the dashboard. The survey will always show on exit of the paywall in the debugger.
- Adds the ability to add `survey_close` as a trigger and use the selected option title in rules.
- Adds the ability to add `survey_response` as a trigger and use the selected option title in rules.
- Adds new `PaywallCloseReason` `.manualClose`.

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ enum InternalSuperwallEvent {
struct AppInstall: TrackableSuperwallEvent {
let superwallEvent: SuperwallEvent = .appInstall
let appInstalledAtString: String
let hasPurchaseController: Bool
let hasExternalPurchaseController: Bool
var customParameters: [String: Any] = [:]
func getSuperwallParameters() async -> [String: Any] {
return [
"application_installed_at": appInstalledAtString,
"using_purchase_controller": hasPurchaseController
"using_purchase_controller": hasExternalPurchaseController
]
}
}
Expand Down
78 changes: 1 addition & 77 deletions Sources/SuperwallKit/Delegate/SuperwallDelegateAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,12 @@

import Foundation
import Combine
import StoreKit

/// An adapter between the internal SDK and the public swift/objective c delegate.
final class SuperwallDelegateAdapter {
var hasPurchaseController: Bool {
return swiftPurchaseController != nil || objcPurchaseController != nil
}

var swiftDelegate: SuperwallDelegate?
var objcDelegate: SuperwallDelegateObjc?
var swiftPurchaseController: PurchaseController?
var objcPurchaseController: PurchaseControllerObjc?

/// Called on init of the Superwall instance via
/// ``Superwall/configure(apiKey:purchaseController:options:completion:)-52tke``.
init(
swiftPurchaseController: PurchaseController?,
objcPurchaseController: PurchaseControllerObjc?
) {
self.swiftPurchaseController = swiftPurchaseController
self.objcPurchaseController = objcPurchaseController
}

@MainActor
func handleCustomPaywallAction(withName name: String) {
Expand Down Expand Up @@ -136,64 +121,3 @@ final class SuperwallDelegateAdapter {
}
}
}

// MARK: - Product Purchaser
extension SuperwallDelegateAdapter: ProductPurchaser {
@MainActor
func purchase(
product: StoreProduct
) async -> PurchaseResult {
if let purchaseController = swiftPurchaseController {
guard let sk1Product = product.sk1Product else {
return .failed(PurchaseError.productUnavailable)
}
return await purchaseController.purchase(product: sk1Product)
} else if let purchaseController = objcPurchaseController {
guard let sk1Product = product.sk1Product else {
return .failed(PurchaseError.productUnavailable)
}
return await withCheckedContinuation { continuation in
purchaseController.purchase(product: sk1Product) { result, error in
if let error = error {
continuation.resume(returning: .failed(error))
} else {
switch result {
case .purchased:
continuation.resume(returning: .purchased)
case .pending:
continuation.resume(returning: .pending)
case .cancelled:
continuation.resume(returning: .cancelled)
case .failed:
break
}
}
}
}
}
return .cancelled
}
}

// MARK: - TransactionRestorer
extension SuperwallDelegateAdapter: TransactionRestorer {
@MainActor
func restorePurchases() async -> RestorationResult {
var result: RestorationResult = .failed(nil)
if let purchaseController = swiftPurchaseController {
result = await purchaseController.restorePurchases()
} else if let purchaseController = objcPurchaseController {
result = await withCheckedContinuation { continuation in
purchaseController.restorePurchases { result, error in
switch result {
case .restored:
continuation.resume(returning: .restored)
case .failed:
continuation.resume(returning: .failed(error))
}
}
}
}
return result
}
}
37 changes: 10 additions & 27 deletions Sources/SuperwallKit/Dependencies/DependencyContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ final class DependencyContainer {
objcPurchaseController: PurchaseControllerObjc? = nil,
options: SuperwallOptions? = nil
) {
storeKitManager = StoreKitManager(factory: self)
delegateAdapter = SuperwallDelegateAdapter(
let purchaseController = InternalPurchaseController(
factory: self,
swiftPurchaseController: swiftPurchaseController,
objcPurchaseController: objcPurchaseController
)
storeKitManager = StoreKitManager(purchaseController: purchaseController)
delegateAdapter = SuperwallDelegateAdapter()
storage = Storage(factory: self)
network = Network(factory: self)

Expand Down Expand Up @@ -115,6 +117,9 @@ final class DependencyContainer {
sessionEventsManager: sessionEventsManager,
factory: self
)

// Initialise the product purchaser so that it can immediately start listening to transactions.
_ = storeKitManager.purchaseController.productPurchaser
}
}

Expand Down Expand Up @@ -376,17 +381,6 @@ extension DependencyContainer: ConfigManagerFactory {
}
}

// MARK: - StoreKitCoordinatorFactory
extension DependencyContainer: StoreKitCoordinatorFactory {
func makeStoreKitCoordinator() -> StoreKitCoordinator {
return StoreKitCoordinator(
delegateAdapter: delegateAdapter,
storeKitManager: storeKitManager,
factory: self
)
}
}

// MARK: - StoreTransactionFactory
extension DependencyContainer: StoreTransactionFactory {
func makeStoreTransaction(from transaction: SK1Transaction) async -> StoreTransaction {
Expand Down Expand Up @@ -417,22 +411,11 @@ extension DependencyContainer: ProductPurchaserFactory {
return ProductPurchaserSK1(
storeKitManager: storeKitManager,
sessionEventsManager: sessionEventsManager,
delegateAdapter: delegateAdapter,
factory: self
)
}
}

// MARK: - Purchase Manager Factory
extension DependencyContainer: PurchaseManagerFactory {
func makePurchaseManager() -> PurchaseManager {
return PurchaseManager(
storeKitManager: storeKitManager,
hasPurchaseController: delegateAdapter.hasPurchaseController
)
}
}

// MARK: - Options Factory
extension DependencyContainer: OptionsFactory {
func makeSuperwallOptions() -> SuperwallOptions {
Expand All @@ -448,9 +431,9 @@ extension DependencyContainer: TriggerFactory {
}

// MARK: - Purchase Controller Factory
extension DependencyContainer: HasPurchaseControllerFactory {
func makeHasPurchaseController() -> Bool {
return delegateAdapter.hasPurchaseController
extension DependencyContainer: HasExternalPurchaseControllerFactory {
func makeHasExternalPurchaseController() -> Bool {
return storeKitManager.purchaseController.isDeveloperProvided
}
}

Expand Down
12 changes: 2 additions & 10 deletions Sources/SuperwallKit/Dependencies/FactoryProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@ protocol ConfigManagerFactory: AnyObject {
) -> Paywall?
}

protocol StoreKitCoordinatorFactory: AnyObject {
func makeStoreKitCoordinator() -> StoreKitCoordinator
}

protocol IdentityInfoFactory: AnyObject {
func makeIdentityInfo() async -> IdentityInfo
}
Expand All @@ -97,8 +93,8 @@ protocol DeviceHelperFactory: AnyObject {
func makeIsSandbox() -> Bool
}

protocol HasPurchaseControllerFactory: AnyObject {
func makeHasPurchaseController() -> Bool
protocol HasExternalPurchaseControllerFactory: AnyObject {
func makeHasExternalPurchaseController() -> Bool
}

protocol ApiFactory: AnyObject {
Expand Down Expand Up @@ -129,10 +125,6 @@ protocol StoreTransactionFactory: AnyObject {
func makeStoreTransaction(from transaction: SK2Transaction) async -> StoreTransaction
}

protocol PurchaseManagerFactory: AnyObject {
func makePurchaseManager() -> PurchaseManager
}

protocol OptionsFactory: AnyObject {
func makeSuperwallOptions() -> SuperwallOptions
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/SuperwallKit/Storage/Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ class Storage {
/// The disk cache.
private let cache: Cache

private unowned let factory: DeviceHelperFactory & HasPurchaseControllerFactory
private unowned let factory: DeviceHelperFactory & HasExternalPurchaseControllerFactory

// MARK: - Configuration

init(
factory: DeviceHelperFactory & HasPurchaseControllerFactory,
factory: DeviceHelperFactory & HasExternalPurchaseControllerFactory,
cache: Cache = Cache(),
coreDataManager: CoreDataManager = CoreDataManager()
) {
Expand Down Expand Up @@ -172,13 +172,13 @@ class Storage {
return
}

let hasPurchaseController = factory.makeHasPurchaseController()
let hasExternalPurchaseController = factory.makeHasExternalPurchaseController()
let deviceInfo = factory.makeDeviceInfo()

Task {
let event = InternalSuperwallEvent.AppInstall(
appInstalledAtString: deviceInfo.appInstalledAtString,
hasPurchaseController: hasPurchaseController
hasExternalPurchaseController: hasExternalPurchaseController
)
_ = await trackEvent(event)
}
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 8119196

Please sign in to comment.