diff --git a/Sources/Identity/CustomerInfo.swift b/Sources/Identity/CustomerInfo.swift index 41b12f42ff..277cfcfec5 100644 --- a/Sources/Identity/CustomerInfo.swift +++ b/Sources/Identity/CustomerInfo.swift @@ -24,13 +24,10 @@ import Foundation @objc public let entitlements: EntitlementInfos /// All *subscription* product identifiers with expiration dates in the future. - @objc public var activeSubscriptions: Set { activeKeys(dates: expirationDatesByProductId) } + @objc public var activeSubscriptions: Set { self.activeKeys(dates: expirationDatesByProductId) } /// All product identifiers purchases by the user regardless of expiration. - @objc public private(set) lazy var allPurchasedProductIdentifiers: Set = { - return Set(self.expirationDatesByProductId.keys) - .union(self.nonSubscriptions.map { $0.productIdentifier }) - }() + @objc public let allPurchasedProductIdentifiers: Set /// Returns the latest expiration date of all products, nil if there are none. @objc public var latestExpirationDate: Date? { @@ -192,14 +189,15 @@ import Foundation self.originalPurchaseDate = subscriber.originalPurchaseDate self.originalApplicationVersion = subscriber.originalApplicationVersion self.managementURL = subscriber.managementUrl + + self.expirationDatesByProductId = Self.extractExpirationDates(subscriber) + self.purchaseDatesByProductId = Self.extractPurchaseDates(subscriber) + self.allPurchasedProductIdentifiers = Set(self.expirationDatesByProductId.keys) + .union(self.nonSubscriptions.map { $0.productIdentifier }) } - private lazy var expirationDatesByProductId: [String: Date?] = { - return self.extractExpirationDates() - }() - private lazy var purchaseDatesByProductId: [String: Date?] = { - return self.extractPurchaseDates() - }() + private let expirationDatesByProductId: [String: Date?] + private let purchaseDatesByProductId: [String: Date?] } // MARK: - Internal @@ -233,6 +231,15 @@ extension CustomerInfo: RawDataContainer { } +#if swift(>=5.7) +extension CustomerInfo: Sendable {} +#else +// `@unchecked` because: +// - `Date` is not `Sendable` until Swift 5.7 +// - `URL` is not `Sendable` until Swift 5.7 +extension CustomerInfo: @unchecked Sendable {} +#endif + /// `CustomerInfo`'s `Codable` implementation relies on `Data` extension CustomerInfo: Codable { @@ -317,12 +324,12 @@ private extension CustomerInfo { func isAfterReferenceDate(date: Date) -> Bool { date.timeIntervalSince(self.requestDate) > 0 } - func extractExpirationDates() -> [String: Date?] { - return self.subscriber.subscriptions.mapValues { $0.expiresDate } + static func extractExpirationDates(_ subscriber: CustomerInfoResponse.Subscriber) -> [String: Date?] { + return subscriber.subscriptions.mapValues { $0.expiresDate } } - func extractPurchaseDates() -> [String: Date?] { - return self.subscriber.allTransactionsByProductId.mapValues { $0.purchaseDate } + static func extractPurchaseDates(_ subscriber: CustomerInfoResponse.Subscriber) -> [String: Date?] { + return subscriber.allTransactionsByProductId.mapValues { $0.purchaseDate } } } diff --git a/Sources/Purchasing/EntitlementInfo.swift b/Sources/Purchasing/EntitlementInfo.swift index d217674cc6..9a6af8a150 100644 --- a/Sources/Purchasing/EntitlementInfo.swift +++ b/Sources/Purchasing/EntitlementInfo.swift @@ -43,6 +43,7 @@ import Foundation } extension Store: CaseIterable {} +extension Store: Sendable {} extension Store: DefaultValueProvider { @@ -66,6 +67,7 @@ extension Store: DefaultValueProvider { } extension PeriodType: CaseIterable {} +extension PeriodType: Sendable {} extension PeriodType: DefaultValueProvider { @@ -76,7 +78,7 @@ extension PeriodType: DefaultValueProvider { /** The EntitlementInfo object gives you access to all of the information about the status of a user entitlement. */ -@objc(RCEntitlementInfo) public class EntitlementInfo: NSObject { +@objc(RCEntitlementInfo) public final class EntitlementInfo: NSObject { /** The entitlement identifier configured in the RevenueCat dashboard @@ -243,6 +245,10 @@ extension PeriodType: DefaultValueProvider { extension EntitlementInfo: RawDataContainer {} +// @unchecked because: +// - `rawData` is `[String: Any]` which can't be `Sendable` +extension EntitlementInfo: @unchecked Sendable {} + public extension EntitlementInfo { /// True if the user has access to this entitlement, @@ -321,3 +327,11 @@ private extension EntitlementInfo { } } + +#if swift(>=5.7) +extension EntitlementInfo.Contents: Sendable {} +#else +// `@unchecked` because: +// - `Date` is not `Sendable` until Swift 5.7 +extension EntitlementInfo.Contents: @unchecked Sendable {} +#endif diff --git a/Sources/Purchasing/EntitlementInfos.swift b/Sources/Purchasing/EntitlementInfos.swift index 41ec739e35..88f299441a 100644 --- a/Sources/Purchasing/EntitlementInfos.swift +++ b/Sources/Purchasing/EntitlementInfos.swift @@ -17,7 +17,7 @@ import Foundation /** This class contains all the entitlements associated to the user. */ -@objc(RCEntitlementInfos) public class EntitlementInfos: NSObject { +@objc(RCEntitlementInfos) public final class EntitlementInfos: NSObject { /** Dictionary of all EntitlementInfo (``EntitlementInfo``) objects (active and inactive) keyed by entitlement identifier. This dictionary can also be accessed by using an index subscript on ``EntitlementInfos``, e.g. @@ -124,3 +124,5 @@ extension EntitlementInfos { } } + +extension EntitlementInfos: Sendable {} diff --git a/Sources/Purchasing/NonSubscriptionTransaction.swift b/Sources/Purchasing/NonSubscriptionTransaction.swift index bede4c4771..803bcb137d 100644 --- a/Sources/Purchasing/NonSubscriptionTransaction.swift +++ b/Sources/Purchasing/NonSubscriptionTransaction.swift @@ -40,3 +40,11 @@ public final class NonSubscriptionTransaction: NSObject { } } + +#if swift(>=5.7) +extension NonSubscriptionTransaction: Sendable {} +#else +// `@unchecked` because: +// - `Date` is not `Sendable` until Swift 5.7 +extension NonSubscriptionTransaction: @unchecked Sendable {} +#endif diff --git a/Sources/Purchasing/PurchaseOwnershipType.swift b/Sources/Purchasing/PurchaseOwnershipType.swift index e49fec39e1..21a841b3a0 100644 --- a/Sources/Purchasing/PurchaseOwnershipType.swift +++ b/Sources/Purchasing/PurchaseOwnershipType.swift @@ -35,6 +35,7 @@ import Foundation } extension PurchaseOwnershipType: CaseIterable {} +extension PurchaseOwnershipType: Sendable {} extension PurchaseOwnershipType: DefaultValueProvider {