diff --git a/Sources/Purchasing/Offering.swift b/Sources/Purchasing/Offering.swift index 1dea65975f..e0788e766f 100644 --- a/Sources/Purchasing/Offering.swift +++ b/Sources/Purchasing/Offering.swift @@ -26,7 +26,7 @@ import Foundation * - ``Offerings`` * - ``Package`` */ -@objc(RCOffering) public class Offering: NSObject { +@objc(RCOffering) public final class Offering: NSObject { /** Unique identifier defined in RevenueCat dashboard. @@ -46,37 +46,37 @@ import Foundation /** Lifetime ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var lifetime: Package? + @objc public let lifetime: Package? /** Annual ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var annual: Package? + @objc public let annual: Package? /** Six month ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var sixMonth: Package? + @objc public let sixMonth: Package? /** Three month ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var threeMonth: Package? + @objc public let threeMonth: Package? /** Two month ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var twoMonth: Package? + @objc public let twoMonth: Package? /** Monthly ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var monthly: Package? + @objc public let monthly: Package? /** Weekly ``Package`` type configured in the RevenueCat dashboard, if available. */ - @objc private(set) public var weekly: Package? + @objc public let weekly: Package? public override var description: String { return """ @@ -114,55 +114,50 @@ import Foundation self.serverDescription = serverDescription self.availablePackages = availablePackages + var foundPackages: [PackageType: Package] = [:] + + var lifetime: Package? + var annual: Package? + var sixMonth: Package? + var threeMonth: Package? + var twoMonth: Package? + var monthly: Package? + var weekly: Package? + for package in availablePackages { + Self.checkForNilAndLogReplacement(previousPackages: foundPackages, newPackage: package) + switch package.packageType { - case .lifetime: - Self.checkForNilAndLogReplacement(package: self.lifetime, newPackage: package) - self.lifetime = package - case .annual: - Self.checkForNilAndLogReplacement(package: self.annual, newPackage: package) - self.annual = package - case .sixMonth: - Self.checkForNilAndLogReplacement(package: self.sixMonth, newPackage: package) - self.sixMonth = package - case .threeMonth: - Self.checkForNilAndLogReplacement(package: self.threeMonth, newPackage: package) - self.threeMonth = package - case .twoMonth: - Self.checkForNilAndLogReplacement(package: self.twoMonth, newPackage: package) - self.twoMonth = package - case .monthly: - Self.checkForNilAndLogReplacement(package: self.monthly, newPackage: package) - self.monthly = package - case .weekly: - Self.checkForNilAndLogReplacement(package: self.weekly, newPackage: package) - self.weekly = package + case .lifetime: lifetime = package + case .annual: annual = package + case .sixMonth: sixMonth = package + case .threeMonth: threeMonth = package + case .twoMonth: twoMonth = package + case .monthly: monthly = package + case .weekly: weekly = package case .custom where package.storeProduct.productCategory == .nonSubscription: // Non-subscription product, ignoring - break + continue case .unknown, .custom: Logger.warn( "Unknown subscription length for package '\(package.offeringIdentifier)': " + "\(package.packageType). Ignoring." ) + continue } - } - } - private static func checkForNilAndLogReplacement(package: Package?, newPackage: Package) { - guard let package = package else { - return + foundPackages[package.packageType] = package } - Logger.warn("Package: \(package.identifier) already exists, overwriting with:\(newPackage.identifier)") - } + self.lifetime = lifetime + self.annual = annual + self.sixMonth = sixMonth + self.threeMonth = threeMonth + self.twoMonth = twoMonth + self.monthly = monthly + self.weekly = weekly - private func valueOrEmpty(_ value: T?) -> String { - if let value = value { - return value.description - } else { - return "" - } + super.init() } } @@ -173,3 +168,21 @@ extension Offering: Identifiable { public var id: String { return self.identifier } } + +extension Offering: Sendable {} + +// MARK: - Private + +private extension Offering { + + static func checkForNilAndLogReplacement(previousPackages: [PackageType: Package], newPackage: Package) { + if let package = previousPackages[newPackage.packageType] { + Logger.warn("Package: \(package.identifier) already exists, overwriting with: \(newPackage.identifier)") + } + } + +} + +private func valueOrEmpty(_ value: T?) -> String { + return value?.description ?? "" +} diff --git a/Sources/Purchasing/Package.swift b/Sources/Purchasing/Package.swift index c1ee7fc5f7..746b54953e 100644 --- a/Sources/Purchasing/Package.swift +++ b/Sources/Purchasing/Package.swift @@ -23,7 +23,7 @@ import Foundation /// - ``Offering`` /// - ``Offerings`` /// -@objc(RCPackage) public class Package: NSObject { +@objc(RCPackage) public final class Package: NSObject { /// The identifier for this Package. @objc public let identifier: String @@ -105,3 +105,5 @@ extension Package: Identifiable { public var id: String { return self.identifier } } + +extension Package: Sendable {} diff --git a/Sources/Purchasing/PackageType.swift b/Sources/Purchasing/PackageType.swift index fa6f71ebf7..23f7d33afb 100644 --- a/Sources/Purchasing/PackageType.swift +++ b/Sources/Purchasing/PackageType.swift @@ -44,6 +44,8 @@ import Foundation extension PackageType: CaseIterable {} +extension PackageType: Sendable {} + extension PackageType: CustomDebugStringConvertible { /// A textual description of the type suitable for debugging. diff --git a/Sources/Purchasing/StoreKitAbstractions/SK1StoreProduct.swift b/Sources/Purchasing/StoreKitAbstractions/SK1StoreProduct.swift index 8bd0ec7168..2cc566cd00 100644 --- a/Sources/Purchasing/StoreKitAbstractions/SK1StoreProduct.swift +++ b/Sources/Purchasing/StoreKitAbstractions/SK1StoreProduct.swift @@ -96,3 +96,8 @@ extension SK1StoreProduct: Hashable { } } + +#if swift(<5.7) +// `SK1Product` isn't `Sendable` until iOS 16.0 / Swift 5.7 +extension SK1StoreProduct: @unchecked Sendable {} +#endif diff --git a/Sources/Purchasing/StoreKitAbstractions/SK1StoreProductDiscount.swift b/Sources/Purchasing/StoreKitAbstractions/SK1StoreProductDiscount.swift index ee281df92a..5508495e83 100644 --- a/Sources/Purchasing/StoreKitAbstractions/SK1StoreProductDiscount.swift +++ b/Sources/Purchasing/StoreKitAbstractions/SK1StoreProductDiscount.swift @@ -61,6 +61,14 @@ internal struct SK1StoreProductDiscount: StoreProductDiscountType { } +#if swift(<5.7) +// `SK1ProductDiscount` isn't `Sendable` until iOS 16.0 / Swift 5.7 +@available(iOS 11.2, macOS 10.13.2, tvOS 11.2, watchOS 6.2, *) +extension SK1StoreProductDiscount: @unchecked Sendable {} +#endif + +// MARK: - Private + private extension StoreProductDiscount.PaymentMode { @available(iOS 11.2, macOS 10.13.2, tvOS 11.2, watchOS 6.2, *) diff --git a/Sources/Purchasing/StoreKitAbstractions/SK2StoreProduct.swift b/Sources/Purchasing/StoreKitAbstractions/SK2StoreProduct.swift index 1d7ff75fb3..31775ca68d 100644 --- a/Sources/Purchasing/StoreKitAbstractions/SK2StoreProduct.swift +++ b/Sources/Purchasing/StoreKitAbstractions/SK2StoreProduct.swift @@ -116,3 +116,9 @@ extension SK2StoreProduct: Hashable { } } + +#if swift(<5.7) +// `SK2Product` isn't `Sendable` until iOS 16.0 / Swift 5.7 +@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) +extension SK2StoreProduct: @unchecked Sendable {} +#endif diff --git a/Sources/Purchasing/StoreKitAbstractions/SK2StoreProductDiscount.swift b/Sources/Purchasing/StoreKitAbstractions/SK2StoreProductDiscount.swift index 65132d302e..ad47401472 100644 --- a/Sources/Purchasing/StoreKitAbstractions/SK2StoreProductDiscount.swift +++ b/Sources/Purchasing/StoreKitAbstractions/SK2StoreProductDiscount.swift @@ -46,6 +46,14 @@ internal struct SK2StoreProductDiscount: StoreProductDiscountType { var localizedPriceString: String { underlyingSK2Discount.displayPrice } } +#if swift(<5.7) +// `SK2ProductDiscount` isn't `Sendable` until iOS 16.0 / Swift 5.7 +@available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) +extension SK2StoreProductDiscount: @unchecked Sendable {} +#endif + +// MARK: - Private + private extension StoreProductDiscount.PaymentMode { @available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *) diff --git a/Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift b/Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift index 02c193c839..d4251f1d1b 100644 --- a/Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift +++ b/Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift @@ -99,7 +99,7 @@ public typealias SK2Product = StoreKit.Product } /// Type that provides access to all of `StoreKit`'s product type's properties. -internal protocol StoreProductType { +internal protocol StoreProductType: Sendable { /// The category of this product, whether a subscription or a one-time purchase. diff --git a/Sources/Purchasing/StoreKitAbstractions/StoreProductDiscount.swift b/Sources/Purchasing/StoreKitAbstractions/StoreProductDiscount.swift index 043ddcfa9c..8870c7b23b 100644 --- a/Sources/Purchasing/StoreKitAbstractions/StoreProductDiscount.swift +++ b/Sources/Purchasing/StoreKitAbstractions/StoreProductDiscount.swift @@ -98,6 +98,10 @@ public final class StoreProductDiscount: NSObject, StoreProductDiscountType { } +extension StoreProductDiscount: Sendable {} +extension StoreProductDiscount.PaymentMode: Sendable {} +extension StoreProductDiscount.DiscountType: Sendable {} + public extension StoreProductDiscount { /// The discount price of the product in the local currency. @@ -136,7 +140,7 @@ extension StoreProductDiscount { } /// The details of an introductory offer or a promotional offer for an auto-renewable subscription. -internal protocol StoreProductDiscountType { +internal protocol StoreProductDiscountType: Sendable { // Note: this is only `nil` for SK1 products before iOS 12.2. // It can become `String` once it's not longer supported. diff --git a/Sources/Purchasing/StoreKitAbstractions/SubscriptionPeriod.swift b/Sources/Purchasing/StoreKitAbstractions/SubscriptionPeriod.swift index ea80478b10..ef4f60b0a9 100644 --- a/Sources/Purchasing/StoreKitAbstractions/SubscriptionPeriod.swift +++ b/Sources/Purchasing/StoreKitAbstractions/SubscriptionPeriod.swift @@ -18,7 +18,7 @@ import StoreKit /// Use the value and the unit together to determine the subscription period. /// For example, if the unit is `.month`, and the value is `3`, the subscription period is three months. @objc(RCSubscriptionPeriod) -public class SubscriptionPeriod: NSObject { +public final class SubscriptionPeriod: NSObject { /// The number of period units. @objc public let value: Int @@ -98,6 +98,9 @@ public extension SubscriptionPeriod { } +extension SubscriptionPeriod.Unit: Sendable {} +extension SubscriptionPeriod: Sendable {} + extension SubscriptionPeriod { func pricePerMonth(withTotalPrice price: Decimal) -> Decimal { let periodsPerMonth: Decimal = {