Files
purchases-ios/Sources/Purchasing/StoreKitAbstractions/StoreTransaction.swift
T
NachoSoto 29c034dfc8 Converted CustomerInfo and related types to use Codable (#1496)
### 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
2022-05-24 14:22:11 -07:00

118 lines
4.1 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 }
// 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 }
}