diff --git a/Qonversion/Qonversion/Entities/Transaction.swift b/Qonversion/Qonversion/Entities/Transaction.swift index ae6027e1..d1677d33 100644 --- a/Qonversion/Qonversion/Entities/Transaction.swift +++ b/Qonversion/Qonversion/Entities/Transaction.swift @@ -8,282 +8,286 @@ import Foundation import StoreKit -public enum RevocationReason { - case developerIssue - case other -} +extension Qonversion { + + public enum RevocationReason { + case developerIssue + case other + } -public struct Storefront { - public let countryCode: String + public struct Storefront { + public let countryCode: String - /// A value defined by Apple that uniquely identifies an App Store storefront. - public let id: String? - - init?(countryCode: String?, id: String?) { - guard let countryCode: String = countryCode else { return nil } + /// A value defined by Apple that uniquely identifies an App Store storefront. + public let id: String? - self.countryCode = countryCode - self.id = id + init?(countryCode: String?, id: String?) { + guard let countryCode: String = countryCode else { return nil } + + self.countryCode = countryCode + self.id = id + } } -} -public struct Transaction { - - public enum OwnershipType: String { - case purchased - case familyShared - } - - public struct Currency { + public struct Transaction { - public let identifier: String - public let symbol: String? + public enum OwnershipType: String { + case purchased + case familyShared + } - init?(identifier: String?, symbol: String?) { - guard let identifier = identifier else { return nil } + public struct Currency { + + public let identifier: String + public let symbol: String? + + init?(identifier: String?, symbol: String?) { + guard let identifier = identifier else { return nil } + + self.identifier = identifier + self.symbol = symbol + } - self.identifier = identifier - self.symbol = symbol } - } - - public enum Environment: String { - case production - case sandbox - case xcode - } - - public struct Offer { + public enum Environment: String { + case production + case sandbox + case xcode + } - public enum OfferType: String { - case introductory - case promotional - case code + public struct Offer { - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - static func from(transaction: StoreKit.Transaction?) -> Qonversion.Transaction.Offer.OfferType? { - guard let transaction: StoreKit.Transaction = transaction else { return nil } - - let type: StoreKit.Transaction.OfferType? - if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { - type = transaction.offer?.type - } else { - type = transaction.offerType - } + public enum OfferType: String { + case introductory + case promotional + case code - guard let type: StoreKit.Transaction.OfferType = type else { return nil } + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + static func from(transaction: StoreKit.Transaction?) -> Qonversion.Transaction.Offer.OfferType? { + guard let transaction: StoreKit.Transaction = transaction else { return nil } + + let type: StoreKit.Transaction.OfferType? + if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { + type = transaction.offer?.type + } else { + type = transaction.offerType + } + + guard let type: StoreKit.Transaction.OfferType = type else { return nil } + + switch type { + case .introductory: + return Qonversion.Transaction.Offer.OfferType.introductory + case .promotional: + return Qonversion.Transaction.Offer.OfferType.promotional + case .code: + return Qonversion.Transaction.Offer.OfferType.code + default: + return nil + } + } + } + + public enum PaymentMode: String { + case freeTrial + case payAsYouGo + case payUpFront - switch type { - case .introductory: - return Qonversion.Transaction.Offer.OfferType.introductory - case .promotional: - return Qonversion.Transaction.Offer.OfferType.promotional - case .code: - return Qonversion.Transaction.Offer.OfferType.code - default: - return nil + @available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) + static func from(paymentMode: StoreKit.Transaction.Offer.PaymentMode?) -> Qonversion.Transaction.Offer.PaymentMode? { + guard let paymentMode = paymentMode else { return nil } + switch paymentMode { + case .freeTrial: + return Qonversion.Transaction.Offer.PaymentMode.freeTrial + case .payAsYouGo: + return Qonversion.Transaction.Offer.PaymentMode.payAsYouGo + case .payUpFront: + return Qonversion.Transaction.Offer.PaymentMode.payUpFront + default: + return nil + } } } - } - - public enum PaymentMode: String { - case freeTrial - case payAsYouGo - case payUpFront + + let id: String? + let type: Transaction.Offer.OfferType? @available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) - static func from(paymentMode: StoreKit.Transaction.Offer.PaymentMode?) -> Qonversion.Transaction.Offer.PaymentMode? { - guard let paymentMode = paymentMode else { return nil } - switch paymentMode { - case .freeTrial: - return Qonversion.Transaction.Offer.PaymentMode.freeTrial - case .payAsYouGo: - return Qonversion.Transaction.Offer.PaymentMode.payAsYouGo - case .payUpFront: - return Qonversion.Transaction.Offer.PaymentMode.payUpFront - default: - return nil - } + var paymentMode: Qonversion.Transaction.Offer.PaymentMode? { + guard let offer = _offer as? StoreKit.Transaction.Offer else { return nil } + return Qonversion.Transaction.Offer.PaymentMode.from(paymentMode: offer.paymentMode) } - } - - let id: String? - let type: Qonversion.Transaction.Offer.OfferType? - - @available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) - var paymentMode: Qonversion.Transaction.Offer.PaymentMode? { - guard let offer = _offer as? StoreKit.Transaction.Offer else { return nil } - return Qonversion.Transaction.Offer.PaymentMode.from(paymentMode: offer.paymentMode) - } - - @available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) - var originalOffer: StoreKit.Transaction.Offer? { _offer as? StoreKit.Transaction.Offer } - - // Workaround to make originalOffer variable available for specific OS versions - let _offer: Any? - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - init?(with transaction: StoreKit.Transaction) { - self.type = Qonversion.Transaction.Offer.OfferType.from(transaction: transaction) - if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { - guard let offer = transaction.offer else { return nil } + @available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) + var originalOffer: StoreKit.Transaction.Offer? { _offer as? StoreKit.Transaction.Offer } + + // Workaround to make originalOffer variable available for specific OS versions + let _offer: Any? + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init?(with transaction: StoreKit.Transaction) { + self.type = Qonversion.Transaction.Offer.OfferType.from(transaction: transaction) - self._offer = offer - self.id = offer.id - } else { - self.id = transaction.offerID - self._offer = nil + if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { + guard let offer = transaction.offer else { return nil } + + self._offer = offer + self.id = offer.id + } else { + self.id = transaction.offerID + self._offer = nil + } } + } - } - - public enum Reason: String { - case purchase - case renewal - } - - public var jsonRepresentation: Data? + public enum Reason: String { + case purchase + case renewal + } + + public var jsonRepresentation: Data? - public let id: String? + public let id: String? - public let originalId: String? + public let originalId: String? - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var webOrderLineItemId: String? { storeKitTransaction?.webOrderLineItemID } + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var webOrderLineItemId: String? { storeKitTransaction?.webOrderLineItemID } - public let productId: String + public let productId: String - public let subscriptionGroupId: String? - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var appBundleId: String? { storeKitTransaction?.appBundleID } + public let subscriptionGroupId: String? + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var appBundleId: String? { storeKitTransaction?.appBundleID } - public let purchaseDate: Date? + public let purchaseDate: Date? - public let originalPurchaseDate: Date? + public let originalPurchaseDate: Date? - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var expirationDate: Date? { storeKitTransaction?.expirationDate } + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var expirationDate: Date? { storeKitTransaction?.expirationDate } - public let purchasedQuantity: Int + public let purchasedQuantity: Int - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var isUpgraded: Bool? { storeKitTransaction?.isUpgraded } + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var isUpgraded: Bool? { storeKitTransaction?.isUpgraded } - // Workaround to make offer variable available for specific OS versions - private let _offer: Qonversion.Transaction.Offer? - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var offer: Qonversion.Transaction.Offer? { _offer } - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var revocationDate: Date? { storeKitTransaction?.revocationDate } - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var appAccountToken: UUID? { storeKitTransaction?.appAccountToken } - - @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, visionOS 1.0, *) - public var environment: Qonversion.Transaction.Environment? { Qonversion.Transaction.Environment(rawValue: storeKitTransaction?.environment.rawValue ?? "") } - - @available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) - public var reason: Qonversion.Transaction.Reason { - guard let storeKitTransaction = storeKitTransaction, - let reason = Qonversion.Transaction.Reason(rawValue: storeKitTransaction.reason.rawValue) - else { return Qonversion.Transaction.Reason.purchase } + // Workaround to make offer variable available for specific OS versions + private let _offer: Qonversion.Transaction.Offer? - return reason - } - - public let price: Decimal? - - public let currency: Qonversion.Transaction.Currency? - - public let storefront: Storefront? - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var deviceVerification: Data? { storeKitTransaction?.deviceVerification } - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var deviceVerificationNonce: UUID? { storeKitTransaction?.deviceVerificationNonce } - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var signedDate: Date? { storeKitTransaction?.signedDate } - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var ownershipType: Qonversion.Transaction.OwnershipType { - guard let storeKitTransaction = storeKitTransaction, - let ownershipType = Qonversion.Transaction.OwnershipType(rawValue: storeKitTransaction.ownershipType.rawValue) - else { return Qonversion.Transaction.OwnershipType.purchased } + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var offer: Qonversion.Transaction.Offer? { _offer } - return ownershipType - } - - private let _storeKitTransaction: Any? - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - public var storeKitTransaction: StoreKit.Transaction? { _storeKitTransaction as? StoreKit.Transaction } - - public let skPaymentTransaction: SKPaymentTransaction? - - init(transaction: SKPaymentTransaction, product: SKProduct) { - self.jsonRepresentation = nil - self.id = transaction.transactionIdentifier - self.originalId = transaction.original?.transactionIdentifier - self.productId = transaction.payment.productIdentifier - self.subscriptionGroupId = product.subscriptionGroupIdentifier - self.purchaseDate = transaction.transactionDate - self.originalPurchaseDate = transaction.original?.transactionDate - self.purchasedQuantity = transaction.payment.quantity - self.storefront = Qonversion.Storefront(countryCode: SKPaymentQueue.default().storefront?.countryCode, id: SKPaymentQueue.default().storefront?.identifier) - self.price = product.price as Decimal + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var revocationDate: Date? { storeKitTransaction?.revocationDate } + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var appAccountToken: UUID? { storeKitTransaction?.appAccountToken } - let currencyCode: String? - if #available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) { - currencyCode = product.priceLocale.currency?.identifier - } else { - currencyCode = product.priceLocale.currencyCode + @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, visionOS 1.0, *) + public var environment: Qonversion.Transaction.Environment? { Qonversion.Transaction.Environment(rawValue: storeKitTransaction?.environment.rawValue ?? "") } + + @available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) + public var reason: Qonversion.Transaction.Reason { + guard let storeKitTransaction = storeKitTransaction, + let reason = Qonversion.Transaction.Reason(rawValue: storeKitTransaction.reason.rawValue) + else { return Qonversion.Transaction.Reason.purchase } + + return reason } - self.currency = Qonversion.Transaction.Currency(identifier: currencyCode, symbol: product.priceLocale.currencySymbol) + public let price: Decimal? - self._offer = nil - self._storeKitTransaction = nil - self.skPaymentTransaction = transaction - } - - @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) - init(transaction: StoreKit.Transaction) { - self.jsonRepresentation = transaction.jsonRepresentation - self.id = String(transaction.id) - self.originalId = String(transaction.originalID) - self.productId = transaction.productID - self.subscriptionGroupId = transaction.subscriptionGroupID - self.purchaseDate = transaction.purchaseDate - self.originalPurchaseDate = transaction.originalPurchaseDate - self.purchasedQuantity = transaction.purchasedQuantity - self.price = transaction.price + public let currency: Qonversion.Transaction.Currency? - if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, visionOS 1.0, *) { - self.currency = Qonversion.Transaction.Currency(identifier: transaction.currency?.identifier, symbol: transaction.currency?.currencySymbol()) - } else { - self.currency = Qonversion.Transaction.Currency(identifier: transaction.currencyCode, symbol: transaction.currencyCode?.toCurrencySymbol()) - } + public let storefront: Qonversion.Storefront? + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var deviceVerification: Data? { storeKitTransaction?.deviceVerification } - if #available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, visionOS 1.0, *) { - self.storefront = Qonversion.Storefront(countryCode: transaction.storefront.countryCode, id: transaction.storefront.id) - } else { - self.storefront = Qonversion.Storefront(countryCode: transaction.storefrontCountryCode, id: nil) + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var deviceVerificationNonce: UUID? { storeKitTransaction?.deviceVerificationNonce } + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var signedDate: Date? { storeKitTransaction?.signedDate } + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var ownershipType: Qonversion.Transaction.OwnershipType { + guard let storeKitTransaction = storeKitTransaction, + let ownershipType = Qonversion.Transaction.OwnershipType(rawValue: storeKitTransaction.ownershipType.rawValue) + else { return Qonversion.Transaction.OwnershipType.purchased } + + return ownershipType } - if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { - self._offer = Qonversion.Transaction.Offer(with: transaction) - } else { + private let _storeKitTransaction: Any? + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + public var storeKitTransaction: StoreKit.Transaction? { _storeKitTransaction as? StoreKit.Transaction } + + public let skPaymentTransaction: SKPaymentTransaction? + + init(transaction: SKPaymentTransaction, product: SKProduct) { + self.jsonRepresentation = nil + self.id = transaction.transactionIdentifier + self.originalId = transaction.original?.transactionIdentifier + self.productId = transaction.payment.productIdentifier + self.subscriptionGroupId = product.subscriptionGroupIdentifier + self.purchaseDate = transaction.transactionDate + self.originalPurchaseDate = transaction.original?.transactionDate + self.purchasedQuantity = transaction.payment.quantity + self.storefront = Qonversion.Storefront(countryCode: SKPaymentQueue.default().storefront?.countryCode, id: SKPaymentQueue.default().storefront?.identifier) + self.price = product.price as Decimal + + let currencyCode: String? + if #available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) { + currencyCode = product.priceLocale.currency?.identifier + } else { + currencyCode = product.priceLocale.currencyCode + } + + self.currency = Qonversion.Transaction.Currency(identifier: currencyCode, symbol: product.priceLocale.currencySymbol) + self._offer = nil + self._storeKitTransaction = nil + self.skPaymentTransaction = transaction + } + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(transaction: StoreKit.Transaction) { + self.jsonRepresentation = transaction.jsonRepresentation + self.id = String(transaction.id) + self.originalId = String(transaction.originalID) + self.productId = transaction.productID + self.subscriptionGroupId = transaction.subscriptionGroupID + self.purchaseDate = transaction.purchaseDate + self.originalPurchaseDate = transaction.originalPurchaseDate + self.purchasedQuantity = transaction.purchasedQuantity + self.price = transaction.price + + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, visionOS 1.0, *) { + self.currency = Qonversion.Transaction.Currency(identifier: transaction.currency?.identifier, symbol: transaction.currency?.currencySymbol()) + } else { + self.currency = Qonversion.Transaction.Currency(identifier: transaction.currencyCode, symbol: transaction.currencyCode?.toCurrencySymbol()) + } + + if #available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, visionOS 1.0, *) { + self.storefront = Qonversion.Storefront(countryCode: transaction.storefront.countryCode, id: transaction.storefront.id) + } else { + self.storefront = Qonversion.Storefront(countryCode: transaction.storefrontCountryCode, id: nil) + } + + if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { + self._offer = Qonversion.Transaction.Offer(with: transaction) + } else { + self._offer = nil + } + self._storeKitTransaction = transaction + self.skPaymentTransaction = nil } - self._storeKitTransaction = transaction - self.skPaymentTransaction = nil } + } diff --git a/Qonversion/Qonversion/Qonversion.swift b/Qonversion/Qonversion/Qonversion.swift index 75d393a2..decc793b 100644 --- a/Qonversion/Qonversion/Qonversion.swift +++ b/Qonversion/Qonversion/Qonversion.swift @@ -22,7 +22,7 @@ public final class Qonversion { } public func collectAppleSearchAdsAttribution() { - userPropertiesManager.collectAppleSearchAdsAttribution() + userPropertiesManager?.collectAppleSearchAdsAttribution() } public func collectAdvertisingId() {