Skip to content

Commit 8af94b9

Browse files
authored
Merge branch 'main' into cursor/appsflyer-attribution-integration-e2df
2 parents 69b1dd4 + 9ebd514 commit 8af94b9

File tree

24 files changed

+252
-121
lines changed

24 files changed

+252
-121
lines changed

RevenueCat.xcodeproj/project.pbxproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
030890842D2B77E70069677B /* VariableHandlerV2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030890832D2B77E20069677B /* VariableHandlerV2Tests.swift */; };
1212
030F918A2D55C1D20085103F /* LocaleFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F91892D55C1AB0085103F /* LocaleFinder.swift */; };
1313
030F918C2D55C9DC0085103F /* LocaleFinderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F918B2D55C9D80085103F /* LocaleFinderTests.swift */; };
14-
030F918E2D5664410085103F /* LocaleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F918D2D5664410085103F /* LocaleExtensions.swift */; };
1514
030F93B32D5ED90B0085103F /* ProgressViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F93B22D5ED90B0085103F /* ProgressViewModifier.swift */; };
1615
0313FD41268A506400168386 /* DateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0313FD40268A506400168386 /* DateProvider.swift */; };
1716
032B13C82E0263750099E1ED /* SubscriptionHistoryTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032B13C72E0263750099E1ED /* SubscriptionHistoryTracker.swift */; };
@@ -1499,7 +1498,6 @@
14991498
030890832D2B77E20069677B /* VariableHandlerV2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariableHandlerV2Tests.swift; sourceTree = "<group>"; };
15001499
030F91892D55C1AB0085103F /* LocaleFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleFinder.swift; sourceTree = "<group>"; };
15011500
030F918B2D55C9D80085103F /* LocaleFinderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleFinderTests.swift; sourceTree = "<group>"; };
1502-
030F918D2D5664410085103F /* LocaleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleExtensions.swift; sourceTree = "<group>"; };
15031501
030F93B22D5ED90B0085103F /* ProgressViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressViewModifier.swift; sourceTree = "<group>"; };
15041502
0313FD40268A506400168386 /* DateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateProvider.swift; sourceTree = "<group>"; };
15051503
032B13C72E0263750099E1ED /* SubscriptionHistoryTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionHistoryTracker.swift; sourceTree = "<group>"; };
@@ -2973,7 +2971,6 @@
29732971
isa = PBXGroup;
29742972
children = (
29752973
03F0B6502DD5700D00E82199 /* LocalizationDictionaryExtensions.swift */,
2976-
030F918D2D5664410085103F /* LocaleExtensions.swift */,
29772974
030F91892D55C1AB0085103F /* LocaleFinder.swift */,
29782975
);
29792976
path = Localizations;
@@ -7696,7 +7693,6 @@
76967693
77089F9E2CD39EC100848CD5 /* ShadowModifier.swift in Sources */,
76977694
0354AA462D4029C300F9E330 /* TabControlButtonComponentViewModel.swift in Sources */,
76987695
0387D3C82EC51F09008E4A6B /* FileImageLoader.swift in Sources */,
7699-
030F918E2D5664410085103F /* LocaleExtensions.swift in Sources */,
77007696
0354AA472D4029C300F9E330 /* TabControlComponentViewModel.swift in Sources */,
77017697
0354AA482D4029C300F9E330 /* TabsComponentView.swift in Sources */,
77027698
0354AA492D4029C300F9E330 /* TabControlButtonComponentView.swift in Sources */,

RevenueCatUI/Data/TestData.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//
77

88
import Foundation
9-
import RevenueCat
9+
@_spi(Internal) import RevenueCat
1010
import SwiftUI
1111

1212
// swiftlint:disable type_body_length file_length force_unwrapping
@@ -45,6 +45,7 @@ enum TestData {
4545
static let weeklyProduct = TestStoreProduct(
4646
localizedTitle: "Weekly",
4747
price: 1.99,
48+
currencyCode: "USD",
4849
localizedPriceString: "$1.99",
4950
productIdentifier: "com.revenuecat.product_1",
5051
productType: .autoRenewableSubscription,
@@ -56,6 +57,7 @@ enum TestData {
5657
static let monthlyProduct = TestStoreProduct(
5758
localizedTitle: "Monthly",
5859
price: 6.99,
60+
currencyCode: "USD",
5961
localizedPriceString: "$6.99",
6062
productIdentifier: "com.revenuecat.product_2",
6163
productType: .autoRenewableSubscription,
@@ -68,6 +70,7 @@ enum TestData {
6870
static let threeMonthProduct = TestStoreProduct(
6971
localizedTitle: "3 months",
7072
price: 4.99,
73+
currencyCode: "USD",
7174
localizedPriceString: "$4.99",
7275
productIdentifier: "com.revenuecat.product_5",
7376
productType: .autoRenewableSubscription,
@@ -80,6 +83,7 @@ enum TestData {
8083
static let threeMonthProductThailand = TestStoreProduct(
8184
localizedTitle: "3 months",
8285
price: 5.00,
86+
currencyCode: Locale.thailand.rc_languageCode!,
8387
localizedPriceString: "฿5.00",
8488
productIdentifier: "com.revenuecat.product_5",
8589
productType: .autoRenewableSubscription,
@@ -92,6 +96,7 @@ enum TestData {
9296
static let sixMonthProduct = TestStoreProduct(
9397
localizedTitle: "6 months",
9498
price: 7.99,
99+
currencyCode: "USD",
95100
localizedPriceString: "$7.99",
96101
productIdentifier: "com.revenuecat.product_5",
97102
productType: .autoRenewableSubscription,
@@ -104,6 +109,7 @@ enum TestData {
104109
static let annualProduct = TestStoreProduct(
105110
localizedTitle: "Annual",
106111
price: 53.99,
112+
currencyCode: "USD",
107113
localizedPriceString: "$53.99",
108114
productIdentifier: "com.revenuecat.product_3",
109115
productType: .autoRenewableSubscription,
@@ -116,6 +122,7 @@ enum TestData {
116122
static let annualProduct60 = TestStoreProduct(
117123
localizedTitle: "Annual",
118124
price: 60.00,
125+
currencyCode: "USD",
119126
localizedPriceString: "$60.00",
120127
productIdentifier: "com.revenuecat.product_3",
121128
productType: .autoRenewableSubscription,
@@ -128,18 +135,20 @@ enum TestData {
128135
static let annualProduct60Taiwan = TestStoreProduct(
129136
localizedTitle: "Annual",
130137
price: 60.00,
131-
localizedPriceString: "$60.00",
138+
currencyCode: "USD",
139+
localizedPriceString: "US$60.00",
132140
productIdentifier: "com.revenuecat.product_3",
133141
productType: .autoRenewableSubscription,
134142
localizedDescription: "PRO annual",
135143
subscriptionGroupIdentifier: "group",
136144
subscriptionPeriod: .init(value: 1, unit: .year),
137-
introductoryDiscount: Self.intro(14, .day, priceString: "$2.99"),
145+
introductoryDiscount: Self.intro(14, .day, priceString: "US$2.99"),
138146
locale: Locale.taiwan
139147
)
140148
static let lifetimeProduct = TestStoreProduct(
141149
localizedTitle: "Lifetime",
142150
price: 119.49,
151+
currencyCode: "USD",
143152
localizedPriceString: "$119.49",
144153
productIdentifier: "com.revenuecat.product_lifetime",
145154
productType: .nonConsumable,
@@ -217,6 +226,7 @@ enum TestData {
217226
static let productWithPromoOfferPayUpFront = TestStoreProduct(
218227
localizedTitle: "PRO monthly",
219228
price: 3.99,
229+
currencyCode: "USD",
220230
localizedPriceString: "$3.99",
221231
productIdentifier: "com.revenuecat.promo.product_2",
222232
productType: .autoRenewableSubscription,
@@ -239,6 +249,7 @@ enum TestData {
239249
static let productWithIntroOffer = TestStoreProduct(
240250
localizedTitle: "PRO monthly",
241251
price: 3.99,
252+
currencyCode: "USD",
242253
localizedPriceString: "$3.99",
243254
productIdentifier: "com.revenuecat.product_4",
244255
productType: .autoRenewableSubscription,
@@ -260,6 +271,7 @@ enum TestData {
260271
static let productWithIntroOfferPayUpFront = TestStoreProduct(
261272
localizedTitle: "PRO monthly",
262273
price: 3.99,
274+
currencyCode: "USD",
263275
localizedPriceString: "$3.99",
264276
productIdentifier: "com.revenuecat.product_5",
265277
productType: .autoRenewableSubscription,
@@ -281,6 +293,7 @@ enum TestData {
281293
static let productWithNoIntroOffer = TestStoreProduct(
282294
localizedTitle: "PRO annual",
283295
price: 34.99,
296+
currencyCode: "USD",
284297
localizedPriceString: "$34.99",
285298
productIdentifier: "com.revenuecat.product_3",
286299
productType: .autoRenewableSubscription,

RevenueCatUI/Templates/V2/Localizations/LocaleExtensions.swift

Lines changed: 0 additions & 41 deletions
This file was deleted.

RevenueCatUI/Templates/V2/Localizations/LocaleFinder.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// Created by Josh Holtz on 2/6/25.
1313

1414
import Foundation
15+
@_spi(Internal) import RevenueCat
1516

1617
extension Dictionary where Key == String {
1718

RevenueCatUI/Views/LoadingPaywallView.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,32 +118,38 @@ private extension LoadingPaywallView {
118118
static let weeklyProduct = TestStoreProduct(
119119
localizedTitle: "Weekly",
120120
price: 1.99,
121+
currencyCode: "USD",
121122
localizedPriceString: "$1.99",
122123
productIdentifier: "com.revenuecat.product_1",
123124
productType: .autoRenewableSubscription,
124125
localizedDescription: "PRO weekly",
125126
subscriptionGroupIdentifier: "group",
126-
subscriptionPeriod: .init(value: 1, unit: .week)
127+
subscriptionPeriod: .init(value: 1, unit: .week),
128+
locale: Locale(identifier: "en_US")
127129
)
128130
static let monthlyProduct = TestStoreProduct(
129131
localizedTitle: "Monthly",
130132
price: 12.99,
133+
currencyCode: "USD",
131134
localizedPriceString: "$12.99",
132135
productIdentifier: "com.revenuecat.product_2",
133136
productType: .autoRenewableSubscription,
134137
localizedDescription: "PRO monthly",
135138
subscriptionGroupIdentifier: "group",
136-
subscriptionPeriod: .init(value: 1, unit: .month)
139+
subscriptionPeriod: .init(value: 1, unit: .month),
140+
locale: Locale(identifier: "en_US")
137141
)
138142
static let annualProduct = TestStoreProduct(
139143
localizedTitle: "Annual",
140144
price: 69.49,
145+
currencyCode: "USD",
141146
localizedPriceString: "$69.49",
142147
productIdentifier: "com.revenuecat.product_3",
143148
productType: .autoRenewableSubscription,
144149
localizedDescription: "PRO annual",
145150
subscriptionGroupIdentifier: "group",
146-
subscriptionPeriod: .init(value: 1, unit: .year)
151+
subscriptionPeriod: .init(value: 1, unit: .year),
152+
locale: Locale(identifier: "en_US")
147153
)
148154
}
149155

Sources/FoundationExtensions/Locale+Extensions.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,16 @@
1313

1414
import Foundation
1515

16-
extension Locale {
16+
@_spi(Internal)
17+
public extension Locale {
1718

18-
// swiftlint:disable:next identifier_name
19+
/// Returns true if the language component of the Locale is equal to the one of self
20+
func matchesLanguage(_ rhs: Locale) -> Bool {
21+
self.removingRegion == rhs.removingRegion
22+
}
23+
24+
// swiftlint:disable identifier_name
25+
/// The code of the currency used by the locale.
1926
var rc_currencyCode: String? {
2027
#if swift(>=5.9)
2128
// `Locale.currencyCode` is deprecated
@@ -29,7 +36,7 @@ extension Locale {
2936
#endif
3037
}
3138

32-
// swiftlint:disable:next identifier_name
39+
/// The language code that identifies the locale's language.
3340
var rc_languageCode: String? {
3441
#if swift(>=5.9)
3542
// `Locale.languageCode` is deprecated
@@ -42,6 +49,7 @@ extension Locale {
4249
return self.languageCode
4350
#endif
4451
}
52+
// swiftlint:enable identifier_name
4553

4654
/// - Returns: the same locale as `self` but removing its region.
4755
var removingRegion: Self? {

Sources/Purchasing/OfferingsManager.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,14 +379,16 @@ private extension OfferingsManager {
379379
let testProduct = TestStoreProduct(
380380
localizedTitle: "PRO \(productType.type)",
381381
price: Decimal(productType.price),
382+
currencyCode: "USD",
382383
localizedPriceString: String(format: "$%.2f", productType.price),
383384
productIdentifier: identifier,
384385
productType: productType.period == nil ? .nonConsumable : .autoRenewableSubscription,
385386
localizedDescription: productType.type + (productType.period == nil ? "" : " subscription"),
386387
subscriptionGroupIdentifier: productType.period == nil ? nil : "group",
387388
subscriptionPeriod: productType.period,
388389
introductoryDiscount: introductoryDiscount,
389-
discounts: []
390+
discounts: [],
391+
locale: Locale(identifier: "en_US")
390392
)
391393

392394
return testProduct.toStoreProduct()

Sources/Purchasing/SimulatedStore/WebBillingProduct+SimulatedStoreProduct.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,14 @@ extension WebBillingProductsResponse.Product {
6060

6161
let simulatedStoreProduct = SimulatedStoreProduct(localizedTitle: self.title,
6262
price: decimalPrice,
63+
currencyCode: price.currency,
6364
localizedPriceString: localizedPriceString,
6465
productIdentifier: self.identifier,
6566
productType: self.productType.storeProductType,
6667
localizedDescription: self.description ?? "",
6768
subscriptionPeriod: period,
68-
introductoryDiscount: introDiscount)
69+
introductoryDiscount: introDiscount,
70+
locale: locale)
6971
return simulatedStoreProduct.toStoreProduct()
7072
}
7173

Sources/Purchasing/StoreKitAbstractions/Test Data/TestStoreProduct.swift

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public struct TestStoreProduct {
5050

5151
public var localizedTitle: String
5252
public var price: Decimal
53+
public var currencyCode: String?
5354
public var localizedPriceString: String
5455
public var localizedPricePerDay: String?
5556
public var localizedPricePerWeek: String?
@@ -65,6 +66,8 @@ public struct TestStoreProduct {
6566
public var discounts: [StoreProductDiscount]
6667
public var locale: Locale
6768

69+
// swiftlint:disable:next line_length
70+
@available(*, deprecated, message: "Use init(localizedTitle:price:currencyCode:localizedPriceString:productIdentifier:productType:localizedDescription:subscriptionGroupIdentifier:subscriptionPeriod:isFamilyShareable:introductoryDiscount:discounts:locale:) instead")
6871
public init(
6972
localizedTitle: String,
7073
price: Decimal,
@@ -81,6 +84,54 @@ public struct TestStoreProduct {
8184
) {
8285
self.localizedTitle = localizedTitle
8386
self.price = price
87+
self.currencyCode = locale.rc_currencyCode
88+
self.localizedPriceString = localizedPriceString
89+
self.productIdentifier = productIdentifier
90+
self.productType = productType
91+
self.localizedDescription = localizedDescription
92+
self.subscriptionGroupIdentifier = subscriptionGroupIdentifier
93+
self.subscriptionPeriod = subscriptionPeriod
94+
self.isFamilyShareable = isFamilyShareable
95+
self.introductoryDiscount = introductoryDiscount?.toStoreProductDiscount()
96+
self.discounts = discounts.map { $0.toStoreProductDiscount() }
97+
self.locale = locale
98+
}
99+
100+
/// Creates a new ``TestStoreProduct``.
101+
///
102+
/// - Parameters:
103+
/// - localizedTitle: The localized title of the product
104+
/// - price: The price of the product
105+
/// - currencyCode: The currency code (e.g., "USD", "EUR").
106+
/// - localizedPriceString: The localized price string (e.g., "$3.99")
107+
/// - productIdentifier: The product identifier
108+
/// - productType: The type of product
109+
/// - localizedDescription: The localized description
110+
/// - subscriptionGroupIdentifier: Optional subscription group identifier
111+
/// - subscriptionPeriod: Optional subscription period
112+
/// - isFamilyShareable: Whether the product is family shareable
113+
/// - introductoryDiscount: Optional introductory discount
114+
/// - discounts: Array of discounts
115+
/// - locale: The locale that should be used when formatting prices.
116+
/// It is important that this matches with the price strings passed (e.g. localizedPriceString)
117+
public init(
118+
localizedTitle: String,
119+
price: Decimal,
120+
currencyCode: String,
121+
localizedPriceString: String,
122+
productIdentifier: String,
123+
productType: StoreProduct.ProductType,
124+
localizedDescription: String,
125+
subscriptionGroupIdentifier: String? = nil,
126+
subscriptionPeriod: SubscriptionPeriod? = nil,
127+
isFamilyShareable: Bool = false,
128+
introductoryDiscount: TestStoreProductDiscount? = nil,
129+
discounts: [TestStoreProductDiscount] = [],
130+
locale: Locale
131+
) {
132+
self.localizedTitle = localizedTitle
133+
self.price = price
134+
self.currencyCode = currencyCode
84135
self.localizedPriceString = localizedPriceString
85136
self.productIdentifier = productIdentifier
86137
self.productType = productType
@@ -104,10 +155,6 @@ extension TestStoreProduct: StoreProductType {
104155

105156
internal var productCategory: StoreProduct.ProductCategory { return self.productType.productCategory }
106157

107-
internal var currencyCode: String? {
108-
return self.locale.rc_currencyCode
109-
}
110-
111158
internal var priceFormatter: NumberFormatter? {
112159
return self.currencyCode.map {
113160
self.priceFormatterProvider.priceFormatterForSK2(withCurrencyCode: $0, locale: self.locale)

0 commit comments

Comments
 (0)