Files
purchases-ios/Sources/Purchasing/StoreKitAbstractions/StoreTransaction.swift
T
NachoSoto 01ed2cb2cf StoreTransaction: read Storefront from StoreKit.Transaction (#2611)
This was added in iOS 17, and it's potentially more accurate than
reading `Storefront.currentStorefront` later.
2023-06-12 09:35:04 -07:00

154 lines
5.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
//
// StoreTransaction.swift
//
// Created by Andrés Boedo on 2/12/21.
import Foundation
import StoreKit
/// TypeAlias to StoreKit 1's Transaction type, called `StoreKit.SKPaymentTransaction`
public typealias SK1Transaction = SKPaymentTransaction
/// TypeAlias to StoreKit 2's Transaction type, called `StoreKit.Transaction`
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
public typealias SK2Transaction = StoreKit.Transaction
/// Abstract class that provides access to properties of a transaction.
/// ``StoreTransaction``s can represent transactions from StoreKit 1, StoreKit 2 or
/// transactions made from other places, like Stripe, Google Play or Amazon Store.
@objc(RCStoreTransaction) public final class StoreTransaction: NSObject, StoreTransactionType {
private let transaction: StoreTransactionType
init(_ transaction: StoreTransactionType) {
self.transaction = transaction
super.init()
}
// Note: docs are inherited through `StoreTransactionType`
// swiftlint:disable missing_docs
@objc public var productIdentifier: String { self.transaction.productIdentifier }
@objc public var purchaseDate: Date { self.transaction.purchaseDate }
@objc public var transactionIdentifier: String { self.transaction.transactionIdentifier }
@objc public var quantity: Int { self.transaction.quantity }
@objc public var storefront: Storefront? { self.transaction.storefront }
func finish(_ wrapper: PaymentQueueWrapperType, completion: @escaping @Sendable () -> Void) {
self.transaction.finish(wrapper, completion: completion)
}
// swiftlint:enable missing_docs
/// Creates an instance from any `StoreTransactionType`.
/// If `transaction` is already a wrapped `StoreTransaction` then this returns it instead.
static func from(transaction: StoreTransactionType) -> StoreTransaction {
return transaction as? StoreTransaction
?? StoreTransaction(transaction)
}
public override func isEqual(_ object: Any?) -> Bool {
self.transactionIdentifier == (object as? StoreTransactionType)?.transactionIdentifier
}
public override var hash: Int {
var hasher = Hasher()
hasher.combine(self.transactionIdentifier)
return hasher.finalize()
}
public override var description: String {
return """
<\(String(describing: StoreTransaction.self)):
identifier="\(self.transactionIdentifier)"
product="\(self.productIdentifier)"
date="\(self.purchaseDate)"
quantity=\(self.quantity)
>
"""
}
}
/// Information that represents the customer’s purchase of a product.
internal protocol StoreTransactionType: Sendable {
/// The product identifier.
var productIdentifier: String { get }
/// The date that App Store charged the user’s account for a purchased or restored product,
/// or for a subscription purchase or renewal after a lapse.
var purchaseDate: Date { get }
/// The unique identifier for the transaction.
var transactionIdentifier: String { get }
/// The number of consumable products purchased.
/// - Note: multi-quantity purchases aren't currently supported.
var quantity: Int { get }
/// The App Store storefront associated with the transaction.
/// - Note: this is only available for StoreKit 2 transactions starting with iOS 17.
var storefront: Storefront? { get }
/// Indicates to the App Store that the app delivered the purchased content
/// or enabled the service to finish the transaction.
func finish(_ wrapper: PaymentQueueWrapperType, completion: @escaping @Sendable () -> Void)
}
// MARK: - Wrapper constructors / getters
extension StoreTransaction {
internal convenience init(sk1Transaction: SK1Transaction) {
self.init(SK1StoreTransaction(sk1Transaction: sk1Transaction))
}
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
internal convenience init(sk2Transaction: SK2Transaction) {
self.init(SK2StoreTransaction(sk2Transaction: sk2Transaction))
}
/// Returns the `SKPaymentTransaction` if this `StoreTransaction` represents a `SKPaymentTransaction`.
@objc public var sk1Transaction: SK1Transaction? {
return (self.transaction as? SK1StoreTransaction)?.underlyingSK1Transaction
}
/// Returns the `StoreKit.Transaction` if this `StoreTransaction` represents a `StoreKit.Transaction`.
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *)
public var sk2Transaction: SK2Transaction? {
return (self.transaction as? SK2StoreTransaction)?.underlyingSK2Transaction
}
}
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.2, *)
extension StoreTransactionType {
/// - Returns: the `Storefront` associated to this transaction, or `Storefront.currentStorefront` if not available.
var storefrontOrCurrent: Storefront? {
get async {
return await self.storefront ??? (await Storefront.currentStorefront)
}
}
}
extension StoreTransaction: Identifiable {
/// The stable identity of the entity associated with this instance.
public var id: String { return self.transactionIdentifier }
}