mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-10 22:45:55 +05:45
### Depends on: - #1537 - #1540 - #1541 - #1543 - #1546 - #1547 - #1550 - #1551, - #1565 ### Changes - Removed all custom deserialization code thanks to #1537, #1541, and #1543. - The format of the customer info response is now very concise and only in one place (`CustomerInfoResponse`) instead of being passed through dictionaries and magic strings everywhere. - `CustomerInfo` was combining the dictionaries inside of `"subscriptions"` and `"non_subscriptions"`, despite them having different formats. This is now made explicit through type conversions between the two, (see `CustomerInfoResponse.allTransactionsByProductId`). - Removed all custom `CustomerInfo` errors since deserialization is automatic, and the underlying `Error` information will be provided thanks to `ErrorUtils.logDecodingError`. - `CustomerInfo` deserialization always wraps errors into `ErrorCode.customerInfoError`, which is now also covered in a new test. - Added a new snapshot test that covers `CustomerInfo` serialization by storing the result in a JSON file. ### Other changes: - `CustomerInfo` equality is now automatic through the `CustomerInfoResponse` `Equatable` conformance. - Simplified `schemaVersion` handling - All tests still use `CustomerInfo(data: [String: Any])`, but that method is only visible for tests now, and it uses the underlying `Decodable` implementation (see `CustomerInfo+TestExtensions.swift`). - Added extra tests for `CustomerInfoResponse` that use a fixture `CustomerInfo.json`, which is easier to maintain than having JSON in code. - `RawDataContainer` implementation is changed in #1565. ### TODO: - [x] Fix all tests - [x] Update `RawValueContainer` - [x] Consider recovering `CustomerInfoError` - [x] Look at all previously logged errors - [x] Finish `CustomerInfoResponseTests` - [x] Handle all TODOs - [x] Make `RawDataContainer` actually store all the original data: #1565
118 lines
4.1 KiB
Swift
118 lines
4.1 KiB
Swift
//
|
||
// 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 }
|
||
|
||
// 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()
|
||
}
|
||
|
||
}
|
||
|
||
/// Information that represents the customer’s purchase of a product.
|
||
internal protocol StoreTransactionType {
|
||
|
||
/// 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 }
|
||
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
}
|
||
|
||
extension StoreTransaction: Identifiable {
|
||
|
||
/// The stable identity of the entity associated with this instance.
|
||
public var id: String { return self.transactionIdentifier }
|
||
|
||
}
|