Skip to content

Commit

Permalink
Paywalls: replaced defaultLocale with preferredLocales (#3003)
Browse files Browse the repository at this point in the history
Depends on #3002.

This is the new logic for finding the locale to use

```swift
var localizedConfiguration: LocalizedConfiguration {
    let locales: [Locale] = [.current] + Locale.preferredLocales

    return locales
        .lazy
        .compactMap(self.config(for:))
        .first ?? self.fallbackLocalizedConfiguration
}
```

And note that `config(for:)` is a fuzzy-lookup (#2847).
  • Loading branch information
NachoSoto committed Aug 14, 2023
1 parent 04972db commit 448ea21
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 69 deletions.
4 changes: 4 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
42F1DF385E3C1F9903A07FBF /* ProductsFetcherSK1.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFB3CBAA73855779FE828CE2 /* ProductsFetcherSK1.swift */; };
4F0201C42A13C85500091612 /* Assertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0201C32A13C85500091612 /* Assertions.swift */; };
4F05876F2A5DE03F00E9A834 /* PaywallDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F05876E2A5DE03F00E9A834 /* PaywallDataTests.swift */; };
4F062D322A85A11600A8A613 /* PaywallData+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F062D312A85A11600A8A613 /* PaywallData+Localization.swift */; };
4F0BBA812A1D0524000E75AB /* DefaultDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0BBA802A1D0524000E75AB /* DefaultDecodable.swift */; };
4F0BBAAC2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0BBAAB2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift */; };
4F0CE2BD2A215CE600561895 /* TransactionPosterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0CE2BC2A215CE600561895 /* TransactionPosterTests.swift */; };
Expand Down Expand Up @@ -966,6 +967,7 @@
37E35FDA0A44EA03EA12DAA2 /* DateFormatter+ExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormatter+ExtensionsTests.swift"; sourceTree = "<group>"; };
4F0201C32A13C85500091612 /* Assertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assertions.swift; sourceTree = "<group>"; };
4F05876E2A5DE03F00E9A834 /* PaywallDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallDataTests.swift; sourceTree = "<group>"; };
4F062D312A85A11600A8A613 /* PaywallData+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaywallData+Localization.swift"; sourceTree = "<group>"; };
4F0BBA802A1D0524000E75AB /* DefaultDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultDecodable.swift; sourceTree = "<group>"; };
4F0BBAAB2A1D253D000E75AB /* OfflineCustomerInfoCreatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineCustomerInfoCreatorTests.swift; sourceTree = "<group>"; };
4F0CE2BC2A215CE600561895 /* TransactionPosterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionPosterTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2259,6 +2261,7 @@
children = (
4FBBD4E52A620573001CBA21 /* PaywallColor.swift */,
4F87610E2A5C9E490006FA14 /* PaywallData.swift */,
4F062D312A85A11600A8A613 /* PaywallData+Localization.swift */,
4F87612B2A5CAB980006FA14 /* PaywallTemplate.swift */,
4F6ABC772A81595900250E63 /* PaywallCacheWarming.swift */,
4F89A55C2A6ABADF008A411E /* PaywallViewMode.swift */,
Expand Down Expand Up @@ -3394,6 +3397,7 @@
579415D2293689DD00218FBC /* Codable+Extensions.swift in Sources */,
2DDF41B424F6F387005BC22D /* ASN1ContainerBuilder.swift in Sources */,
57F3C10529B7B22E0004FD7E /* CustomerInfo+ActiveDates.swift in Sources */,
4F062D322A85A11600A8A613 /* PaywallData+Localization.swift in Sources */,
B35F9E0926B4BEED00095C3F /* String+Extensions.swift in Sources */,
574A2EE7282C3F0800150D40 /* AnyDecodable.swift in Sources */,
57488B7F29CB70E50000EE7E /* ProductEntitlementMapping.swift in Sources */,
Expand Down
4 changes: 3 additions & 1 deletion RevenueCatUI/Data/Localization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ private extension Localization {
static let unitAbbreviationLengthPriorities = [ 2, 3 ]

/// For falling back in case language isn't localized.
static let defaultLocale: Locale = .init(identifier: "en_US")
static let defaultLocale: Locale = .init(identifier: Self.defaultLocaleIdentifier)

private static let defaultLocaleIdentifier: String = Locale.preferredLanguages.first ?? "en_US"

}

Expand Down
48 changes: 48 additions & 0 deletions Sources/Paywalls/PaywallData+Localization.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// PaywallData+Localization.swift
//
// Created by Nacho Soto on 8/10/23.

import Foundation

public extension PaywallData {

/// - Returns: the ``PaywallData/LocalizedConfiguration-swift.struct`` to be used
/// based on `Locale.current` or `Locale.preferredLocales`.
var localizedConfiguration: LocalizedConfiguration {
let locales: [Locale] = [.current] + Locale.preferredLocales

return locales
.lazy
.compactMap(self.config(for:))
.first ?? self.fallbackLocalizedConfiguration
}

private var fallbackLocalizedConfiguration: LocalizedConfiguration {
// This can't happen because `localization` has `@EnsureNonEmptyCollectionDecodable`.
guard let result = self.localization.first?.value else {
fatalError("Corrupted data: localization is empty.")
}

return result
}

}

// MARK: -

private extension Locale {

static var preferredLocales: [Self] {
return Self.preferredLanguages.map(Locale.init(identifier:))
}

}
32 changes: 2 additions & 30 deletions Sources/Paywalls/PaywallData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public struct PaywallData {
/// The base remote URL where assets for this paywall are stored.
public var assetBaseURL: URL

fileprivate var defaultLocaleIdentifier: String
fileprivate var localization: [String: LocalizedConfiguration]
@EnsureNonEmptyCollectionDecodable
internal var localization: [String: LocalizedConfiguration]

}

Expand Down Expand Up @@ -130,30 +130,6 @@ extension PaywallData {
}?.value
}

/// The default `Locale` used if `Locale.current` is not configured for this paywall.
public var defaultLocale: Locale {
return .init(identifier: self.defaultLocaleIdentifier)
}

/// - Returns: the ``PaywallData/LocalizedConfiguration-swift.struct`` associated to the current `Locale`
/// or the configuration associated to ``defaultLocale``.
public var localizedConfiguration: LocalizedConfiguration {
return self.config(for: Locale.current) ?? self.defaultLocalizedConfiguration
}

private var defaultLocalizedConfiguration: LocalizedConfiguration {
let defaultLocale = self.defaultLocale

guard let result = self.config(for: defaultLocale) else {
fatalError(
"Corrupted data. Expected to find locale \(defaultLocale.identifier) " +
"in locales: \(Set(self.localization.keys))"
)
}

return result
}

}

extension PaywallData.LocalizedConfiguration {
Expand Down Expand Up @@ -353,13 +329,11 @@ extension PaywallData {
init(
template: PaywallTemplate,
config: Configuration,
defaultLocale: String,
localization: [String: LocalizedConfiguration],
assetBaseURL: URL
) {
self.template = template
self.config = config
self.defaultLocaleIdentifier = defaultLocale
self.localization = localization
self.assetBaseURL = assetBaseURL
}
Expand All @@ -376,7 +350,6 @@ extension PaywallData {
self.init(
template: template,
config: config,
defaultLocale: locale,
localization: [locale: localization],
assetBaseURL: assetBaseURL
)
Expand Down Expand Up @@ -435,7 +408,6 @@ extension PaywallData: Codable {
// Note: these are camel case but converted by the decoder
private enum CodingKeys: String, CodingKey {
case template = "templateName"
case defaultLocaleIdentifier = "defaultLocale"
case config
case localization = "localizedStrings"
case assetBaseURL = "assetBaseUrl"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import SwiftUI
func checkPaywallData(_ data: PaywallData) {
let template: PaywallTemplate = data.template
let config: PaywallData.Configuration = data.config
let locale: Locale = data.defaultLocale
let _: PaywallData.LocalizedConfiguration? = data.config(for: locale)
let _: PaywallData.LocalizedConfiguration? = data.config(for: Locale.current)
let localization: PaywallData.LocalizedConfiguration = data.localizedConfiguration
let assetBaseURL: URL = data.assetBaseURL

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
"privacy_url" : "https://revenuecat.com/privacy",
"tos_url" : "https://revenuecat.com/tos"
},
"default_locale" : "en_US",
"localized_strings" : {
"en_US" : {
"call_to_action" : "Purchase for {{ sub_price_per_month }} per month",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
"offer_details_with_intro_offer": "Comienza tu prueba de {{ sub_offer_duration }}, y después {{ sub_price_per_month }} cada mes"
}
},
"default_locale": "en_US",
"config": {
"packages": ["$rc_monthly", "$rc_annual", "custom_package"],
"images": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
]
}
},
"default_locale": "en_US",
"config": {
"packages": ["$rc_monthly", "$rc_annual", "custom_package"],
"default_package": "$rc_annual",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"offer_details_with_intro_offer": "Comienza tu prueba de {{ sub_offer_duration }}, y después {{ sub_price_per_month }} cada mes"
}
},
"default_locale": "es_ES",
"config": {
"packages": ["$rc_monthly", "$rc_annual"],
"images": {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
{
"template_name": "1",
"localized_strings": {
"it_IT": {
"title": "Paywall",
"subtitle": "Description",
"call_to_action": "Purchase now",
"call_to_action_with_intro_offer": "Purchase now",
"offer_details": "{{ sub_price_per_month }} per month",
"offer_details_with_intro_offer": "Start your {{ sub_offer_duration }} trial, then {{ sub_price_per_month }} per month"
}
},
"default_locale": "es_ES",
"localized_strings": {},
"config": {
"packages": ["$rc_monthly", "$rc_annual"],
"images": {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ class OfferingsDecodingTests: BaseHTTPResponseTest {

let paywall = try XCTUnwrap(offering.paywall)
expect(paywall.template) == .template1
expect(paywall.defaultLocale) == Locale(identifier: "en_US")
try expect(paywall.assetBaseURL) == XCTUnwrap(URL(string: "https://rc-paywalls.s3.amazonaws.com"))

expect(paywall.config.packages) == ["$rc_monthly", "$rc_annual", "custom_package"]
Expand Down
24 changes: 4 additions & 20 deletions Tests/UnitTests/Paywalls/PaywallDataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,10 @@ import XCTest

class PaywallDataTests: BaseHTTPResponseTest {

override func setUp() {
super.setUp()

expect(Locale.current.identifier).to(
equal(Self.defaultLocale),
description: "Tests require this"
)
}

func testSample1() throws {
let paywall: PaywallData = try self.decodeFixture("PaywallData-Sample1")

expect(paywall.template) == .template1
expect(paywall.defaultLocale) == Locale(identifier: Self.defaultLocale)
expect(paywall.assetBaseURL) == URL(string: "https://rc-paywalls.s3.amazonaws.com")!
expect(paywall.config.packages) == ["$rc_monthly", "$rc_annual", "custom_package"]
expect(paywall.config.defaultPackage) == "$rc_annual"
Expand Down Expand Up @@ -111,11 +101,9 @@ class PaywallDataTests: BaseHTTPResponseTest {
expect(paywall.config(for: Locale(identifier: "fr"))).to(beNil())
}

func testMissingCurrentLocaleLoadsDefault() throws {
func testMissingCurrentLocaleLoadsAvailableLocale() throws {
let paywall: PaywallData = try self.decodeFixture("PaywallData-missing_current_locale")

expect(paywall.defaultLocale.identifier) == "es_ES"

let localization = paywall.localizedConfiguration
expect(localization.callToAction) == "Comprar"
expect(localization.title) == "Tienda"
Expand All @@ -128,14 +116,10 @@ class PaywallDataTests: BaseHTTPResponseTest {
}

#if !os(watchOS)
func testMissingCurrentAndDefaultFails() throws {
let paywall: PaywallData = try self.decodeFixture("PaywallData-missing_current_and_default_locale")

expect(paywall.defaultLocale.identifier) == "es_ES"

func testMissingLocalizationFails() throws {
expect {
let _: PaywallData.LocalizedConfiguration = paywall.localizedConfiguration
}.to(throwAssertion())
let _: PaywallData = try self.decodeFixture("PaywallData-missing_localization")
}.to(throwError())
}
#endif

Expand Down

0 comments on commit 448ea21

Please sign in to comment.