Skip to content

Commit

Permalink
Offering: Sendable conformance
Browse files Browse the repository at this point in the history
The type is now immutable, so the compiler ensures it's thread-safe.

For [CSDK-379].
Extracted from #1795 to make it easier to review this in isolation.
  • Loading branch information
NachoSoto committed Aug 16, 2022
1 parent 7316c91 commit 4bf7df9
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 47 deletions.
99 changes: 56 additions & 43 deletions Sources/Purchasing/Offering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 """
Expand Down Expand Up @@ -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<T: CustomStringConvertible>(_ value: T?) -> String {
if let value = value {
return value.description
} else {
return ""
}
super.init()
}

}
Expand All @@ -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<T: CustomStringConvertible>(_ value: T?) -> String {
return value?.description ?? ""
}
4 changes: 3 additions & 1 deletion Sources/Purchasing/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -105,3 +105,5 @@ extension Package: Identifiable {
public var id: String { return self.identifier }

}

extension Package: Sendable {}
2 changes: 2 additions & 0 deletions Sources/Purchasing/PackageType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import Foundation

extension PackageType: CaseIterable {}

extension PackageType: Sendable {}

extension PackageType: CustomDebugStringConvertible {

/// A textual description of the type suitable for debugging.
Expand Down
5 changes: 5 additions & 0 deletions Sources/Purchasing/StoreKitAbstractions/SK1StoreProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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, *)
Expand Down
6 changes: 6 additions & 0 deletions Sources/Purchasing/StoreKitAbstractions/SK2StoreProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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, *)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Purchasing/StoreKitAbstractions/StoreProduct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand Down

0 comments on commit 4bf7df9

Please sign in to comment.