Files
purchases-ios/Sources/Purchasing/StoreKit1/StoreKitWrapper.swift
T
Juanpe CatalánandNachoSoto 3a4093d228 Prepare the codebase to listen to the Storefront changes (1/4) (#1557)
* Listen changes via `paymentQueueDidChangeStorefront` method
* Create `StoreKit2StorefrontListener.swift` to listen to Storefront changes for SK2
* Update `PurchasesOrchestrator` to listen to the Storefront changes
* Include `StoreKit2StorefrontListenerTests` in StoreKitTests
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>

* Remove force unwrap

Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>

* Add required spaces

Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>

* Remove useless cancel method when the listener is released
* Notify Storefront changes in the main thread
* Add cancel task when the Listener is released

Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
2022-05-16 15:51:04 -07:00

182 lines
6.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// RCStoreKitWrapper.swift
//
// Created by RevenueCat.
//
import StoreKit
protocol StoreKitWrapperDelegate: AnyObject {
func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, updatedTransaction transaction: SKPaymentTransaction)
func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper, removedTransaction transaction: SKPaymentTransaction)
func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper,
shouldAddStorePayment payment: SKPayment,
for product: SK1Product) -> Bool
func storeKitWrapper(_ storeKitWrapper: StoreKitWrapper,
didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String])
#if os(iOS) || targetEnvironment(macCatalyst)
@available(iOS 13.4, macCatalyst 13.4, *)
var storeKitWrapperShouldShowPriceConsent: Bool { get }
#endif
func storeKitWrapperDidChangeStorefront(_ storeKitWrapper: StoreKitWrapper)
}
class StoreKitWrapper: NSObject, SKPaymentTransactionObserver {
@available(iOS 8.0, macOS 10.14, watchOS 6.2, macCatalyst 13.0, *)
static var simulatesAskToBuyInSandbox = false
var currentStorefront: StorefrontType? {
guard #available(iOS 13.0, tvOS 13.0, macOS 10.15, watchOS 6.2, *) else {
return nil
}
return self.paymentQueue.storefront.map(SK1Storefront.init)
}
weak var delegate: StoreKitWrapperDelegate? {
didSet {
if delegate != nil {
paymentQueue.add(self)
} else {
paymentQueue.remove(self)
}
}
}
private var paymentQueue: SKPaymentQueue
init(paymentQueue: SKPaymentQueue) {
self.paymentQueue = paymentQueue
}
override convenience init() {
self.init(paymentQueue: .default())
}
deinit {
paymentQueue.remove(self)
}
func add(_ payment: SKPayment) {
paymentQueue.add(payment)
}
func finishTransaction(_ transaction: SKPaymentTransaction) {
Logger.purchase(Strings.purchase.finishing_transaction(transaction: transaction))
paymentQueue.finishTransaction(transaction)
}
static func canMakePayments() -> Bool {
return SKPaymentQueue.canMakePayments()
}
@available(iOS 14.0, *)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
@available(macCatalyst, unavailable)
func presentCodeRedemptionSheet() {
// Even though the docs in `SKPaymentQueue.presentCodeRedemptionSheet`
// say that it's available on Catalyst 14.0, there is a note:
// This function doesn’t affect Mac apps built with Mac Catalyst.
// It crashes when called both from Catalyst and also when running as "Designed for iPad".
if paymentQueue.responds(to: #selector(SKPaymentQueue.presentCodeRedemptionSheet)) {
Logger.debug(Strings.purchase.presenting_code_redemption_sheet)
paymentQueue.presentCodeRedemptionSheet()
} else {
Logger.appleError(Strings.purchase.unable_to_present_redemption_sheet)
}
}
func payment(withProduct product: SK1Product) -> SKMutablePayment {
let payment = SKMutablePayment(product: product)
if #available(iOS 8.0, macOS 10.14, watchOS 6.2, macCatalyst 13.0, *) {
payment.simulatesAskToBuyInSandbox = Self.simulatesAskToBuyInSandbox
}
return payment
}
@available(iOS 12.2, macOS 10.14.4, watchOS 6.2, macCatalyst 13.0, tvOS 12.2, *)
func payment(withProduct product: SK1Product, discount: SKPaymentDiscount) -> SKMutablePayment {
let payment = self.payment(withProduct: product)
payment.paymentDiscount = discount
return payment
}
#if os(iOS) || targetEnvironment(macCatalyst)
@available(iOS 13.4, macCatalyst 13.4, *)
func showPriceConsentIfNeeded() {
paymentQueue.showPriceConsentIfNeeded()
}
#endif
}
extension StoreKitWrapper: SKPaymentQueueDelegate {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
Logger.debug(Strings.purchase.paymentqueue_updatedtransaction(transaction: transaction))
delegate?.storeKitWrapper(self, updatedTransaction: transaction)
}
}
// Sent when transactions are removed from the queue (via finishTransaction:).
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
Logger.debug(Strings.purchase.paymentqueue_removedtransaction(transaction: transaction))
delegate?.storeKitWrapper(self, removedTransaction: transaction)
}
}
// Sent when a user initiated an in-app purchase from the App Store.
@available(watchOS, unavailable)
func paymentQueue(_ queue: SKPaymentQueue,
shouldAddStorePayment payment: SKPayment,
for product: SK1Product) -> Bool {
return delegate?.storeKitWrapper(self, shouldAddStorePayment: payment, for: product) ?? false
}
// Sent when access to a family shared subscription is revoked from a family member or canceled the subscription.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
func paymentQueue(_ queue: SKPaymentQueue,
didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) {
Logger.debug(
Strings.purchase.paymentqueue_revoked_entitlements_for_product_identifiers(
productIdentifiers: productIdentifiers
)
)
delegate?.storeKitWrapper(self, didRevokeEntitlementsForProductIdentifiers: productIdentifiers)
}
#if os(iOS) || targetEnvironment(macCatalyst)
@available(iOS 13.4, macCatalyst 13.4, *)
func paymentQueueShouldShowPriceConsent(_ paymentQueue: SKPaymentQueue) -> Bool {
return delegate?.storeKitWrapperShouldShowPriceConsent ?? true
}
#endif
// Sent when the storefront for the payment queue has changed.
func paymentQueueDidChangeStorefront(_ queue: SKPaymentQueue) {
delegate?.storeKitWrapperDidChangeStorefront(self)
}
}